The "req" and "ca" utilities accept command line arguments to specify
the hash algorithm to use when signing certificates and CRLs, and in
March 2010 support was added to these utilities for the "-sigopt"
option. However, the "ocsp" utility does not support either of these
options.
The attached patch file adds support for the "-md" and "-sigopt" options
to the OCSP utility to allow users to specify the hash algorithm and
other options to use when signing OCSP requests or responses. This code
was developed based on the code that is currently included to support
these options in the CA utility and works with the current CVS version
of the code.
As requested in the README file, I have already emailed a copy of this
patch file to BIS and the ENC Encryption Request Coordinator.
David Cooper
diff -ur openssl-orig/apps/ocsp.c openssl-work/apps/ocsp.c
--- openssl-orig/apps/ocsp.c 2009-09-30 17:40:54.000000000 -0400
+++ openssl-work/apps/ocsp.c 2010-12-03 14:55:37.000000000 -0500
@@ -104,9 +104,9 @@
STACK_OF(OCSP_CERTID) *ids, long nsec,
long maxage);
-static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db,
- X509 *ca, X509 *rcert, EVP_PKEY *rkey,
- STACK_OF(X509) *rother, unsigned long flags,
+static int make_ocsp_response(BIO *err, OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db,
+ X509 *ca, X509 *rcert, EVP_PKEY *rkey, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts, STACK_OF(X509) *rother, unsigned long flags,
int nmin, int ndays);
static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser);
@@ -116,6 +116,14 @@
static OCSP_RESPONSE *query_responder(BIO *err, BIO *cbio, char *path,
STACK_OF(CONF_VALUE) *headers,
OCSP_REQUEST *req, int req_timeout);
+static int do_OCSP_request_sign(BIO *err, OCSP_REQUEST *req, X509 *signer,
+ EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts,
+ STACK_OF(X509) *certs, unsigned long flags);
+static int do_OCSP_basic_sign(BIO *err, OCSP_BASICRESP *brsp,
+ X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts,
+ STACK_OF(X509) *certs, unsigned long flags);
#undef PROG
#define PROG ocsp_main
@@ -165,6 +173,9 @@
CA_DB *rdb = NULL;
int nmin = 0, ndays = -1;
const EVP_MD *cert_id_md = NULL;
+ char *md=NULL;
+ const EVP_MD *dgst=NULL;
+ STACK_OF(OPENSSL_STRING) *sigopts = NULL;
if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
@@ -558,6 +569,27 @@
}
else badarg = 1;
}
+ else if (strcmp(*args,"-md") == 0)
+ {
+ if (args[1])
+ {
+ args++;
+ md = *args;
+ }
+ else badarg = 1;
+ }
+ else if (strcmp(*args,"-sigopt") == 0)
+ {
+ if (args[1])
+ {
+ args++;
+ if (!sigopts)
+ sigopts = sk_OPENSSL_STRING_new_null();
+ if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, *args))
+ badarg = 1;
+ }
+ else badarg = 1;
+ }
else if ((cert_id_md = EVP_get_digestbyname((*args)+1))==NULL)
{
badarg = 1;
@@ -617,7 +649,8 @@
BIO_printf (bio_err, "-ndays n number of days before next update\n");
BIO_printf (bio_err, "-resp_key_id identify reponse by signing certificate key ID\n");
BIO_printf (bio_err, "-nrequest n number of requests to accept (default unlimited)\n");
- BIO_printf (bio_err, "-<dgst alg> use specified digest in the request");
+ BIO_printf (bio_err, "-md arg md to use, one of md2, md5, sha or sha1\n");
+ BIO_printf (bio_err, "-<dgst alg> use specified digest in the request\n");
goto end;
}
@@ -678,6 +711,22 @@
"responder private key");
if (!rkey)
goto end;
+
+ if ((md == NULL) || !strcmp(md, "default"))
+ {
+ int def_nid;
+ if (EVP_PKEY_get_default_digest_nid(rkey, &def_nid) <= 0)
+ {
+ BIO_puts(bio_err,"no default digest\n");
+ goto end;
+ }
+ md = (char *)OBJ_nid2sn(def_nid);
+ }
+ else if ((dgst=EVP_get_digestbyname(md)) == NULL)
+ {
+ BIO_printf(bio_err,"%s is an unsupported message digest type\n",md);
+ goto end;
+ }
}
if(acbio)
BIO_printf(bio_err, "Waiting for OCSP client connections...\n");
@@ -725,7 +774,23 @@
if (!key)
goto end;
- if (!OCSP_request_sign(req, signer, key, NULL, sign_other, sign_flags))
+ if ((md == NULL) || !strcmp(md, "default"))
+ {
+ int def_nid;
+ if (EVP_PKEY_get_default_digest_nid(key, &def_nid) <= 0)
+ {
+ BIO_puts(bio_err,"no default digest\n");
+ goto end;
+ }
+ md = (char *)OBJ_nid2sn(def_nid);
+ }
+ else if ((dgst=EVP_get_digestbyname(md)) == NULL)
+ {
+ BIO_printf(bio_err,"%s is an unsupported message digest type\n",md);
+ goto end;
+ }
+
+ if (!do_OCSP_request_sign(bio_err, req, signer, key, dgst, sigopts, sign_other, sign_flags))
{
BIO_printf(bio_err, "Error signing OCSP request\n");
goto end;
@@ -761,7 +826,8 @@
if (rdb)
{
- i = make_ocsp_response(&resp, req, rdb, rca_cert, rsigner, rkey, rother, rflags, nmin, ndays);
+ i = make_ocsp_response(bio_err, &resp, req, rdb, rca_cert, rsigner,
+ rkey, dgst, sigopts, rother, rflags, nmin, ndays);
if (cbio)
send_ocsp_response(cbio, resp);
}
@@ -901,6 +967,8 @@
ERR_print_errors(bio_err);
X509_free(signer);
X509_STORE_free(store);
+ if (sigopts)
+ sk_OPENSSL_STRING_free(sigopts);
EVP_PKEY_free(key);
EVP_PKEY_free(rkey);
X509_free(issuer);
@@ -1050,9 +1118,9 @@
}
-static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db,
- X509 *ca, X509 *rcert, EVP_PKEY *rkey,
- STACK_OF(X509) *rother, unsigned long flags,
+static int make_ocsp_response(BIO *err, OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db,
+ X509 *ca, X509 *rcert, EVP_PKEY *rkey, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts, STACK_OF(X509) *rother, unsigned long flags,
int nmin, int ndays)
{
ASN1_TIME *thisupd = NULL, *nextupd = NULL;
@@ -1142,7 +1210,7 @@
OCSP_copy_nonce(bs, req);
- OCSP_basic_sign(bs, rcert, rkey, NULL, rother, flags);
+ do_OCSP_basic_sign(err, bs, rcert, rkey, dgst, sigopts, rother, flags);
*resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs);
@@ -1418,4 +1486,54 @@
return resp;
}
+static int do_sign_init(BIO *err, EVP_MD_CTX *ctx, EVP_PKEY *pkey,
+ const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts)
+ {
+ EVP_PKEY_CTX *pkctx = NULL;
+ int i;
+ EVP_MD_CTX_init(ctx);
+ if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey))
+ return 0;
+ for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++)
+ {
+ char *sigopt = sk_OPENSSL_STRING_value(sigopts, i);
+ if (pkey_ctrl_string(pkctx, sigopt) <= 0)
+ {
+ BIO_printf(err, "parameter error \"%s\"\n", sigopt);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+static int do_OCSP_request_sign(BIO *err, OCSP_REQUEST *req, X509 *signer,
+ EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts,
+ STACK_OF(X509) *certs, unsigned long flags)
+ {
+ int rv;
+ EVP_MD_CTX mctx;
+ EVP_MD_CTX_init(&mctx);
+ rv = do_sign_init(err, &mctx, key, dgst, sigopts);
+ if (rv > 0)
+ rv = OCSP_request_sign_ctx(req, signer, &mctx, certs, flags);
+ EVP_MD_CTX_cleanup(&mctx);
+ return rv > 0 ? 1 : 0;
+ }
+
+static int do_OCSP_basic_sign(BIO *err, OCSP_BASICRESP *brsp,
+ X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(OPENSSL_STRING) *sigopts,
+ STACK_OF(X509) *certs, unsigned long flags)
+ {
+ int rv;
+ EVP_MD_CTX mctx;
+ EVP_MD_CTX_init(&mctx);
+ rv = do_sign_init(err, &mctx, key, dgst, sigopts);
+ if (rv > 0)
+ rv = OCSP_basic_sign_ctx(brsp, signer, &mctx, certs, flags);
+ EVP_MD_CTX_cleanup(&mctx);
+ return rv > 0 ? 1 : 0;
+ }
#endif
diff -ur openssl-orig/crypto/ocsp/ocsp_cl.c openssl-work/crypto/ocsp/ocsp_cl.c
--- openssl-orig/crypto/ocsp/ocsp_cl.c 2007-12-04 07:41:28.000000000 -0500
+++ openssl-work/crypto/ocsp/ocsp_cl.c 2010-12-03 14:56:18.000000000 -0500
@@ -182,6 +182,53 @@
return 0;
}
+/* Sign an OCSP request set the requestorName to the subject
+ * name of an optional signers certificate and include one
+ * or more optional certificates in the request. Behaves
+ * like PKCS7_sign().
+ */
+
+int OCSP_request_sign_ctx(OCSP_REQUEST *req,
+ X509 *signer,
+ EVP_MD_CTX *ctx,
+ STACK_OF(X509) *certs,
+ unsigned long flags)
+ {
+ int i;
+ OCSP_SIGNATURE *sig;
+ X509 *x;
+
+ if (!OCSP_request_set1_name(req, X509_get_subject_name(signer)))
+ goto err;
+
+ if (!(req->optionalSignature = sig = OCSP_SIGNATURE_new())) goto err;
+ if (ctx && ctx->pctx && EVP_PKEY_CTX_get0_pkey(ctx->pctx))
+ {
+ if (!X509_check_private_key(signer, EVP_PKEY_CTX_get0_pkey(ctx->pctx)))
+ {
+ OCSPerr(OCSP_F_OCSP_REQUEST_SIGN, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+ goto err;
+ }
+ if (!OCSP_REQUEST_sign_ctx(req, ctx)) goto err;
+ }
+
+ if (!(flags & OCSP_NOCERTS))
+ {
+ if(!OCSP_request_add1_cert(req, signer)) goto err;
+ for (i = 0; i < sk_X509_num(certs); i++)
+ {
+ x = sk_X509_value(certs, i);
+ if (!OCSP_request_add1_cert(req, x)) goto err;
+ }
+ }
+
+ return 1;
+err:
+ OCSP_SIGNATURE_free(req->optionalSignature);
+ req->optionalSignature = NULL;
+ return 0;
+ }
+
/* Get response status */
int OCSP_response_status(OCSP_RESPONSE *resp)
diff -ur openssl-orig/crypto/ocsp/ocsp.h openssl-work/crypto/ocsp/ocsp.h
--- openssl-orig/crypto/ocsp/ocsp.h 2009-09-30 17:40:55.000000000 -0400
+++ openssl-work/crypto/ocsp/ocsp.h 2010-12-03 14:56:13.000000000 -0500
@@ -381,10 +381,19 @@
o->optionalSignature->signatureAlgorithm,NULL,\
o->optionalSignature->signature,o->tbsRequest,pkey,md)
+#define OCSP_REQUEST_sign_ctx(o,ctx) \
+ ASN1_item_sign_ctx(ASN1_ITEM_rptr(OCSP_REQINFO),\
+ o->optionalSignature->signatureAlgorithm,NULL,\
+ o->optionalSignature->signature,o->tbsRequest,ctx)
+
#define OCSP_BASICRESP_sign(o,pkey,md,d) \
ASN1_item_sign(ASN1_ITEM_rptr(OCSP_RESPDATA),o->signatureAlgorithm,NULL,\
o->signature,o->tbsResponseData,pkey,md)
+#define OCSP_BASICRESP_sign_ctx(o,ctx,d) \
+ ASN1_item_sign_ctx(ASN1_ITEM_rptr(OCSP_RESPDATA),o->signatureAlgorithm,NULL,\
+ o->signature,o->tbsResponseData,ctx)
+
#define OCSP_REQUEST_verify(a,r) ASN1_item_verify(ASN1_ITEM_rptr(OCSP_REQINFO),\
a->optionalSignature->signatureAlgorithm,\
a->optionalSignature->signature,a->tbsRequest,r)
@@ -434,6 +443,12 @@
STACK_OF(X509) *certs,
unsigned long flags);
+int OCSP_request_sign_ctx(OCSP_REQUEST *req,
+ X509 *signer,
+ EVP_MD_CTX *ctx,
+ STACK_OF(X509) *certs,
+ unsigned long flags);
+
int OCSP_response_status(OCSP_RESPONSE *resp);
OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp);
@@ -477,6 +492,9 @@
int OCSP_basic_sign(OCSP_BASICRESP *brsp,
X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
STACK_OF(X509) *certs, unsigned long flags);
+int OCSP_basic_sign_ctx(OCSP_BASICRESP *brsp,
+ X509 *signer, EVP_MD_CTX *ctx,
+ STACK_OF(X509) *certs, unsigned long flags);
X509_EXTENSION *OCSP_crlID_new(char *url, long *n, char *tim);
diff -ur openssl-orig/crypto/ocsp/ocsp_srv.c openssl-work/crypto/ocsp/ocsp_srv.c
--- openssl-orig/crypto/ocsp/ocsp_srv.c 2008-11-05 13:38:59.000000000 -0500
+++ openssl-work/crypto/ocsp/ocsp_srv.c 2010-12-03 14:56:23.000000000 -0500
@@ -262,3 +262,62 @@
err:
return 0;
}
+
+int OCSP_basic_sign_ctx(OCSP_BASICRESP *brsp,
+ X509 *signer, EVP_MD_CTX *ctx,
+ STACK_OF(X509) *certs, unsigned long flags)
+ {
+ int i;
+ OCSP_RESPID *rid;
+
+ if (!ctx || !ctx->pctx || !X509_check_private_key(signer, EVP_PKEY_CTX_get0_pkey(ctx->pctx)))
+ {
+ OCSPerr(OCSP_F_OCSP_BASIC_SIGN, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+ goto err;
+ }
+
+ if(!(flags & OCSP_NOCERTS))
+ {
+ if(!OCSP_basic_add1_cert(brsp, signer))
+ goto err;
+ for (i = 0; i < sk_X509_num(certs); i++)
+ {
+ X509 *tmpcert = sk_X509_value(certs, i);
+ if(!OCSP_basic_add1_cert(brsp, tmpcert))
+ goto err;
+ }
+ }
+
+ rid = brsp->tbsResponseData->responderId;
+ if (flags & OCSP_RESPID_KEY)
+ {
+ unsigned char md[SHA_DIGEST_LENGTH];
+ X509_pubkey_digest(signer, EVP_sha1(), md, NULL);
+ if (!(rid->value.byKey = ASN1_OCTET_STRING_new()))
+ goto err;
+ if (!(ASN1_OCTET_STRING_set(rid->value.byKey, md, SHA_DIGEST_LENGTH)))
+ goto err;
+ rid->type = V_OCSP_RESPID_KEY;
+ }
+ else
+ {
+ if (!X509_NAME_set(&rid->value.byName,
+ X509_get_subject_name(signer)))
+ goto err;
+ rid->type = V_OCSP_RESPID_NAME;
+ }
+
+ if (!(flags & OCSP_NOTIME) &&
+ !X509_gmtime_adj(brsp->tbsResponseData->producedAt, 0))
+ goto err;
+
+ /* Right now, I think that not doing double hashing is the right
+ thing. -- Richard Levitte */
+
+ if (!OCSP_BASICRESP_sign_ctx(brsp, ctx, 0)) goto err;
+
+ return 1;
+err:
+ return 0;
+ }
+