]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
x509: Support parsing of OCSP requests
authorAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 15 Jun 2023 14:37:45 +0000 (16:37 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 13 Nov 2023 11:39:10 +0000 (12:39 +0100)
src/libstrongswan/credentials/certificates/ocsp_request.h
src/libstrongswan/plugins/x509/x509_ocsp_request.c
src/libstrongswan/plugins/x509/x509_ocsp_request.h
src/libstrongswan/plugins/x509/x509_plugin.c

index 2a63e0cd320485d40cba32fd6e95c85c5552141b..f63d78b5390b8303b2cade09c06830d53c38aecc 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2019 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
  *
  * Copyright (C) secunet Security Networks AG
  *
@@ -43,6 +44,20 @@ struct ocsp_request_t {
         * @return                                      nonce in the request (internal data)
         */
        chunk_t (*get_nonce)(ocsp_request_t *this);
+
+    /**
+     * Get the optional signer certificate.
+     *
+     * @return                  X.509 signer certificate
+     */
+    certificate_t* (*get_signer_cert)(ocsp_request_t *this);
+
+    /**
+     * Create an enumerator over the request list.
+     *
+     * @return                  enumerator over the request fields
+     */
+    enumerator_t* (*create_request_enumerator)(ocsp_request_t *this);
 };
 
 #endif /** OCSP_REQUEST_H_ @}*/
index 152fd2044c5fc49ad1d2044851d7ce17cd9e2a4e..4f0362967a62444bfc988463fb669067c583fda3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2017-2019 Tobias Brunner
  * Copyright (C) 2008-2009 Martin Willi
- * Copyright (C) 2007-2022 Andreas Steffen
+ * Copyright (C) 2007-2023 Andreas Steffen, strongSec GmbH
  * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
  *
  * Copyright (C) secunet Security Networks AG
 #include <library.h>
 #include <asn1/oid.h>
 #include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
 #include <utils/identification.h>
 #include <collections/linked_list.h>
 #include <utils/debug.h>
 #include <credentials/certificates/x509.h>
 #include <credentials/keys/private_key.h>
 
-#define NONCE_LEN              16
+/* RFC 8954 OCSP Nonce Extension */
+#define NONCE_LEN              32
 
 typedef struct private_x509_ocsp_request_t private_x509_ocsp_request_t;
 
@@ -43,9 +45,9 @@ struct private_x509_ocsp_request_t {
        x509_ocsp_request_t public;
 
        /**
-        * CA the candidates belong to
+        * CA the certificates where issued by
         */
-       x509_t *ca;
+       certificate_t *cacert;
 
        /**
         * Requestor name, subject of cert used if not set
@@ -63,9 +65,9 @@ struct private_x509_ocsp_request_t {
        private_key_t *key;
 
        /**
-        * list of certificates to check, x509_t
+        * list of X.509 certificates to check
         */
-       linked_list_t *candidates;
+       linked_list_t *reqCerts;
 
        /**
         * nonce used in request
@@ -77,12 +79,53 @@ struct private_x509_ocsp_request_t {
         */
        chunk_t encoding;
 
+       /**
+        * data for signature verification
+        */
+       chunk_t tbsRequest;
+
+       /**
+        * signature scheme
+        */
+       signature_params_t *scheme;
+
+       /**
+        * signature
+        */
+       chunk_t signature;
+
        /**
         * reference count
         */
        refcount_t ref;
 };
 
+/**
+ * Single reqCert object sent in OCSP request
+ */
+typedef struct {
+       /** hash algorithm for the two hashes */
+       hash_algorithm_t hashAlgorithm;
+       /** hash of issuer DN */
+       chunk_t issuerNameHash;
+       /** issuerKeyID */
+       chunk_t issuerKeyHash;
+       /** serial number of certificate */
+       chunk_t serialNumber;
+} req_cert_t;
+
+/**
+ * Clean up a reqCert object
+ */
+CALLBACK(req_cert_destroy, void,
+       req_cert_t *reqCert)
+{
+       chunk_free(&reqCert->issuerNameHash);
+       chunk_free(&reqCert->issuerKeyHash);
+       chunk_free(&reqCert->serialNumber);
+       free(reqCert);
+}
+
 static const chunk_t ASN1_nonce_oid = chunk_from_chars(
        0x06, 0x09,
                  0x2B, 0x06,
@@ -125,15 +168,15 @@ static chunk_t build_requestorName(private_x509_ocsp_request_t *this)
  * build Request, not using singleRequestExtensions
  */
 static chunk_t build_Request(private_x509_ocsp_request_t *this,
-                                                        chunk_t issuerNameHash, chunk_t issuerKeyHash,
-                                                        chunk_t serialNumber)
+                                                        req_cert_t *reqCert)
 {
        return asn1_wrap(ASN1_SEQUENCE, "m",
-                               asn1_wrap(ASN1_SEQUENCE, "mmmm",
-                                       asn1_algorithmIdentifier(OID_SHA1),
-                                       asn1_simple_object(ASN1_OCTET_STRING, issuerNameHash),
-                                       asn1_simple_object(ASN1_OCTET_STRING, issuerKeyHash),
-                                       asn1_integer("c", serialNumber)));
+                       asn1_wrap(ASN1_SEQUENCE, "mmmm",
+                               asn1_algorithmIdentifier(
+                                       hasher_algorithm_to_oid(reqCert->hashAlgorithm)),
+                               asn1_simple_object(ASN1_OCTET_STRING, reqCert->issuerNameHash),
+                               asn1_simple_object(ASN1_OCTET_STRING, reqCert->issuerKeyHash),
+                               asn1_integer("c", reqCert->serialNumber)));
 }
 
 /**
@@ -141,57 +184,18 @@ static chunk_t build_Request(private_x509_ocsp_request_t *this,
  */
 static chunk_t build_requestList(private_x509_ocsp_request_t *this)
 {
-       chunk_t issuerNameHash, issuerKeyHash;
-       identification_t *issuer;
-       x509_t *x509;
-       certificate_t *cert;
-       chunk_t list = chunk_empty;
-       public_key_t *public;
-
-       cert = (certificate_t*)this->ca;
-       public = cert->get_public_key(cert);
-       if (public)
-       {
-               hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
-               if (hasher)
-               {
-                       if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1,
-                                                                               &issuerKeyHash))
-                       {
-                               enumerator_t *enumerator;
+       chunk_t list = chunk_empty, request;
+       enumerator_t *enumerator;
+       req_cert_t *reqCert;
 
-                               issuer = cert->get_subject(cert);
-                               if (hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
-                                                                                 &issuerNameHash))
-                               {
-                                       enumerator = this->candidates->create_enumerator(
-                                                                                                                       this->candidates);
-                                       while (enumerator->enumerate(enumerator, &x509))
-                                       {
-                                               chunk_t request, serialNumber;
-
-                                               serialNumber = x509->get_serial(x509);
-                                               request = build_Request(this, issuerNameHash,
-                                                                                               issuerKeyHash, serialNumber);
-                                               list = chunk_cat("mm", list, request);
-                                       }
-                                       enumerator->destroy(enumerator);
-                                       chunk_free(&issuerNameHash);
-                               }
-                               hasher->destroy(hasher);
-                       }
-               }
-               else
-               {
-                       DBG1(DBG_LIB, "creating OCSP request failed, SHA1 not supported");
-               }
-               public->destroy(public);
-       }
-       else
+       enumerator = this->reqCerts->create_enumerator(this->reqCerts);
+       while (enumerator->enumerate(enumerator, &reqCert))
        {
-               DBG1(DBG_LIB, "creating OCSP request failed, CA certificate has "
-                        "no public key");
+               request = build_Request(this, reqCert);
+               list = chunk_cat("mm", list, request);
        }
+       enumerator->destroy(enumerator);
+
        return asn1_wrap(ASN1_SEQUENCE, "m", list);
 }
 
@@ -205,7 +209,7 @@ static chunk_t build_nonce(private_x509_ocsp_request_t *this)
        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        if (!rng || !rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
        {
-               DBG1(DBG_LIB, "creating OCSP request nonce failed, no RNG found");
+               DBG1(DBG_LIB, "failed to create RNG");
                DESTROY_IF(rng);
                return chunk_empty;
        }
@@ -237,7 +241,7 @@ static chunk_t build_requestExtensions(private_x509_ocsp_request_t *this)
 }
 
 /**
- * build tbsRequest
+ * Build tbsRequest
  */
 static chunk_t build_tbsRequest(private_x509_ocsp_request_t *this)
 {
@@ -298,7 +302,6 @@ static chunk_t build_optionalSignature(private_x509_ocsp_request_t *this,
 
 /**
  * Build the OCSPRequest data
- *
  */
 static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this)
 {
@@ -312,6 +315,187 @@ static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this)
        return asn1_wrap(ASN1_SEQUENCE, "mm", tbsRequest, optionalSignature);
 }
 
+/**
+ * ASN.1 definition of ocspRequest
+ */
+static const asn1Object_t ocspRequestObjects[] = {
+       { 0, "OCSPRequest",                     ASN1_SEQUENCE,     ASN1_NONE }, /*  0 */
+       { 1,   "tbsRequest",                    ASN1_SEQUENCE,     ASN1_OBJ  }, /*  1 */
+       { 2,     "versionContext",              ASN1_CONTEXT_C_0,  ASN1_NONE |
+                                                                  ASN1_DEF  }, /*  2 */
+       { 3,       "version",                   ASN1_INTEGER,      ASN1_BODY }, /*  3 */
+       { 2,     "requestorNameContext",        ASN1_CONTEXT_C_1,  ASN1_OPT  }, /*  4 */
+       { 3,       "requestorName",             ASN1_CONTEXT_C_4,  ASN1_BODY }, /*  5 */
+       { 2,     "end opt",                     ASN1_EOC,          ASN1_END  }, /*  6 */
+       { 2,     "requestList",                 ASN1_SEQUENCE,     ASN1_LOOP }, /*  7 */
+       { 3,       "request",                   ASN1_SEQUENCE,     ASN1_BODY }, /*  8 */
+       { 4,         "reqCert",                 ASN1_SEQUENCE,     ASN1_NONE }, /*  9 */
+       { 5,           "hashAlgorithm",         ASN1_EOC,          ASN1_RAW  }, /* 10 */
+       { 5,           "issuerNameHash",        ASN1_OCTET_STRING, ASN1_BODY }, /* 11 */
+       { 5,           "issuerKeyHash",         ASN1_OCTET_STRING, ASN1_BODY }, /* 12 */
+       { 5,           "serialNumber",          ASN1_INTEGER,      ASN1_BODY }, /* 13 */
+       { 4,         "singleRequestExtensions", ASN1_CONTEXT_C_0,  ASN1_OPT  }, /* 14 */
+       { 4,         "end opt",                 ASN1_EOC,          ASN1_END  }, /* 15 */
+       { 2,     "end loop",                    ASN1_EOC,          ASN1_END  }, /* 16 */
+       { 2,     "requestExtensions",           ASN1_CONTEXT_C_2,  ASN1_OPT  }, /* 17 */
+       { 3,       "Extensions",                ASN1_SEQUENCE,     ASN1_LOOP }, /* 18 */
+       { 4,         "Extension",               ASN1_SEQUENCE,     ASN1_NONE }, /* 19 */
+       { 5,           "extnID",                ASN1_OID,          ASN1_BODY }, /* 20 */
+       { 5,           "critical",              ASN1_BOOLEAN,      ASN1_BODY |
+                                                                  ASN1_DEF  }, /* 21 */
+       { 5,           "extnValue",             ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */
+       { 3,       "end loop",                  ASN1_EOC,          ASN1_END  }, /* 23 */
+       { 2,     "end opt",                     ASN1_EOC,          ASN1_END  }, /* 24 */
+       { 1,   "optionalSignature",             ASN1_CONTEXT_C_0,  ASN1_OPT  }, /* 25 */
+       { 2,     "signature",                   ASN1_SEQUENCE,     ASN1_NONE }, /* 26 */
+       { 3,       "signatureAlgorithm",        ASN1_EOC,          ASN1_RAW  }, /* 27 */
+       { 3,       "signature",                 ASN1_BIT_STRING,   ASN1_BODY }, /* 28 */
+       { 3,       "certsContext",              ASN1_CONTEXT_C_0,  ASN1_OPT  }, /* 29 */
+       { 4,         "certs",                   ASN1_SEQUENCE,     ASN1_LOOP }, /* 30 */
+       { 5,           "certificate",           ASN1_EOC,          ASN1_RAW  }, /* 31 */
+       { 4,         "end loop",                ASN1_EOC,          ASN1_END  }, /* 32 */
+       { 3,       "end opt",                   ASN1_EOC,          ASN1_END  }, /* 33 */
+       { 1,   "end opt",                       ASN1_EOC,          ASN1_END  }, /* 34 */
+       { 0, "exit",                            ASN1_EOC,          ASN1_EXIT }
+};
+
+#define OCSP_REQ_TBS_REQUEST         1
+#define OCSP_REQ_REQUESTOR           5
+#define OCSP_REQ_REQ_CERT            9
+#define OCSP_REQ_HASH_ALG           10
+#define OCSP_REQ_ISSUER_NAME_HASH   11
+#define OCSP_REQ_ISSUER_KEY_HASH    12
+#define OCSP_REQ_SERIAL_NUMBER      13
+#define OCSP_REQ_EXTN_ID            20
+#define OCSP_REQ_CRITICAL           21
+#define OCSP_REQ_EXTN_VALUE         22
+#define OCSP_REQ_SIG_ALG            27
+#define OCSP_REQ_SIGNATURE          28
+#define OCSP_REQ_CERTIFICATE       31
+
+/**
+ * Parse the OCSPRequest data
+ *
+ */
+static bool parse_OCSPRequest(private_x509_ocsp_request_t *this)
+{
+       asn1_parser_t *parser;
+       req_cert_t *reqCert = NULL;
+       chunk_t object;
+       int extn_oid = OID_UNKNOWN;
+       int objectID;
+       bool critical = FALSE, success = FALSE;
+
+       parser = asn1_parser_create(ocspRequestObjects, this->encoding);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               u_int level = parser->get_level(parser)+1;
+
+               switch (objectID)
+               {
+
+                       case OCSP_REQ_TBS_REQUEST:
+                               this->tbsRequest = object;
+                               break;
+                       case OCSP_REQ_REQUESTOR:
+                               this->requestor = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+                               break;
+                       case OCSP_REQ_REQ_CERT:
+                               INIT(reqCert);
+                               this->reqCerts->insert_last(this->reqCerts, reqCert);
+                               break;
+                       case OCSP_REQ_HASH_ALG:
+                               reqCert->hashAlgorithm = hasher_algorithm_from_oid(
+                                                       asn1_parse_algorithmIdentifier(object, level, NULL));
+                               if (reqCert->hashAlgorithm == HASH_UNKNOWN)
+                               {
+                                       DBG1(DBG_ASN, "unknowm hash algorithm");
+                                       goto end;
+                               }
+                               break;
+                       case OCSP_REQ_ISSUER_NAME_HASH:
+                               reqCert->issuerNameHash = chunk_clone(object);
+                               break;
+                       case OCSP_REQ_ISSUER_KEY_HASH:
+                               reqCert->issuerKeyHash = chunk_clone(object);
+                               break;
+                       case OCSP_REQ_SERIAL_NUMBER:
+                               reqCert->serialNumber = chunk_clone(chunk_skip_zero(object));
+                               break;
+                       case OCSP_REQ_EXTN_ID:
+                               extn_oid = asn1_known_oid(object);
+                               break;
+                       case OCSP_REQ_CRITICAL:
+                               critical = object.len && *object.ptr;
+                               DBG2(DBG_ASN, "  %s", critical ? "TRUE" : "FALSE");
+                               break;
+                       case OCSP_REQ_EXTN_VALUE:
+                       {
+                               switch (extn_oid)
+                               {
+                                       case OID_NONCE:
+                                               if (!asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
+                                                                                               level, "nonce"))
+                                               {
+                                                       goto end;
+                                               }
+                                               this->nonce = chunk_clone(object);
+                                               break;
+                                       case OID_RESPONSE:
+                                               if (!asn1_parse_simple_object(&object, ASN1_SEQUENCE,
+                                                                                               level, "acceptableResponses"))
+                                               {
+                                                       goto end;
+                                               }
+                                               break;
+                                       default:
+                                               if (critical && lib->settings->get_bool(lib->settings,
+                                                       "%s.x509.enforce_critical", TRUE, lib->ns))
+                                               {
+                                                       DBG1(DBG_ASN, "critical '%s' extension not supported",
+                                                                (extn_oid == OID_UNKNOWN) ? "unknown" :
+                                                                (char*)oid_names[extn_oid].name);
+                                                       goto end;
+                                               }
+                                               break;
+                               }
+                               break;
+                       }
+                       case OCSP_REQ_SIG_ALG:
+                               INIT(this->scheme);
+                               if (!signature_params_parse(object, level, this->scheme))
+                               {
+                                       DBG1(DBG_ASN, "  unable to parse signature algorithm");
+                                       goto end;
+                               }
+
+                               break;
+                       case OCSP_REQ_SIGNATURE:
+                               this->signature = chunk_skip(object, 1);
+                               break;
+                       case OCSP_REQ_CERTIFICATE:
+                               if (this->cert)
+                               {
+                                       DBG1(DBG_LIB, "  skipping additional signing certificate");
+                                       break;
+                               }
+                               this->cert = lib->creds->create(lib->creds,
+                                                                       CRED_CERTIFICATE,CERT_X509,
+                                                                       BUILD_BLOB_ASN1_DER, object, BUILD_END);
+                               if (!this->cert)
+                               {
+                                       goto end;
+                               }
+                               break;
+               }
+       }
+       success = parser->success(parser);
+
+end:
+       parser->destroy(parser);
+       return success;
+}
 
 METHOD(certificate_t, get_type, certificate_type_t,
        private_x509_ocsp_request_t *this)
@@ -322,62 +506,60 @@ METHOD(certificate_t, get_type, certificate_type_t,
 METHOD(certificate_t, get_subject, identification_t*,
        private_x509_ocsp_request_t *this)
 {
-       certificate_t *ca = (certificate_t*)this->ca;
-
        if (this->requestor)
        {
                return this->requestor;
        }
-       if (this->cert)
-       {
-               return this->cert->get_subject(this->cert);
-       }
-       return ca->get_subject(ca);
+       return (this->cert) ? this->cert->get_subject(this->cert) : NULL;
 }
 
 METHOD(certificate_t, get_issuer, identification_t*,
        private_x509_ocsp_request_t *this)
 {
-       certificate_t *ca = (certificate_t*)this->ca;
-
-       return ca->get_subject(ca);
+       return this->cacert ? this->cacert->get_subject(this->cacert) : NULL;
 }
 
 METHOD(certificate_t, has_subject, id_match_t,
        private_x509_ocsp_request_t *this, identification_t *subject)
 {
-       certificate_t *current;
-       enumerator_t *enumerator;
-       id_match_t match, best = ID_MATCH_NONE;
-
-       enumerator = this->candidates->create_enumerator(this->candidates);
-       while (enumerator->enumerate(enumerator, &current))
-       {
-               match = current->has_subject(current, subject);
-               if (match > best)
-               {
-                       best = match;
-               }
-       }
-       enumerator->destroy(enumerator);
-       return best;
+       return ID_MATCH_NONE;
 }
 
 METHOD(certificate_t, has_issuer, id_match_t,
        private_x509_ocsp_request_t *this,
                                                         identification_t *issuer)
 {
-       certificate_t *ca = (certificate_t*)this->ca;
-
-       return ca->has_subject(ca, issuer);
+       return this->cacert ? this->cacert->has_subject(this->cacert, issuer) :
+                                                 ID_MATCH_NONE;
 }
 
 METHOD(certificate_t, issued_by, bool,
        private_x509_ocsp_request_t *this, certificate_t *issuer,
        signature_params_t **scheme)
 {
-       DBG1(DBG_LIB, "OCSP request validation not implemented!");
-       return FALSE;
+       public_key_t *key;
+       bool valid;
+
+       if (issuer->get_type(issuer) != CERT_X509 || this->cert == NULL ||
+          !issuer->equals(issuer, this->cert))
+       {
+               return FALSE;
+       }
+
+       key = issuer->get_public_key(issuer);
+       if (!key)
+       {
+               return FALSE;
+       }
+       valid = key->verify(key, this->scheme->scheme, this->scheme->params,
+                                               this->tbsRequest, this->signature);
+       key->destroy(key);
+
+       if (valid && scheme)
+       {
+               *scheme = signature_params_clone(this->scheme);
+       }
+       return valid;
 }
 
 METHOD(certificate_t, get_public_key, public_key_t*,
@@ -390,17 +572,7 @@ METHOD(certificate_t, get_validity, bool,
        private_x509_ocsp_request_t *this, time_t *when, time_t *not_before,
        time_t *not_after)
 {
-       certificate_t *cert;
-
-       if (this->cert)
-       {
-               cert = this->cert;
-       }
-       else
-       {
-               cert = (certificate_t*)this->ca;
-       }
-       return cert->get_validity(cert, when, not_before, not_after);
+       return FALSE;
 }
 
 METHOD(certificate_t, get_encoding, bool,
@@ -455,11 +627,12 @@ METHOD(certificate_t, destroy, void,
 {
        if (ref_put(&this->ref))
        {
-               DESTROY_IF((certificate_t*)this->ca);
+               DESTROY_IF(this->cacert);
                DESTROY_IF(this->requestor);
                DESTROY_IF(this->cert);
                DESTROY_IF(this->key);
-               this->candidates->destroy_offset(this->candidates, offsetof(certificate_t, destroy));
+               signature_params_destroy(this->scheme);
+               this->reqCerts->destroy_function(this->reqCerts, req_cert_destroy);
                chunk_free(&this->nonce);
                chunk_free(&this->encoding);
                free(this);
@@ -472,6 +645,52 @@ METHOD(ocsp_request_t, get_nonce, chunk_t,
        return this->nonce;
 }
 
+METHOD(ocsp_request_t, get_signer_cert, certificate_t*,
+       private_x509_ocsp_request_t *this)
+{
+       return this->cert;
+}
+
+CALLBACK(filter, bool,
+       void *data, enumerator_t *orig, va_list args)
+{
+       req_cert_t *reqCert;
+       hash_algorithm_t *hashAlgorithm;
+       chunk_t *issuerNameHash, *issuerKeyHash, *serialNumber;
+
+       VA_ARGS_VGET(args, hashAlgorithm, issuerNameHash, issuerKeyHash, serialNumber);
+
+       if (orig->enumerate(orig, &reqCert))
+       {
+               if (hashAlgorithm)
+               {
+                       *hashAlgorithm = reqCert->hashAlgorithm;
+               }
+               if (issuerNameHash)
+               {
+                       *issuerNameHash = reqCert->issuerNameHash;
+               }
+               if (issuerKeyHash)
+               {
+                       *issuerKeyHash = reqCert->issuerKeyHash;
+               }
+               if (serialNumber)
+               {
+                       *serialNumber = reqCert->serialNumber;
+               }
+               return TRUE;
+       }
+       return FALSE;
+}
+
+METHOD(ocsp_request_t, create_request_enumerator, enumerator_t*,
+       private_x509_ocsp_request_t *this)
+{
+       return enumerator_create_filter(
+                               this->reqCerts->create_enumerator(this->reqCerts),
+                               filter, NULL, NULL);
+}
+
 /**
  * create an empty but initialized OCSP request
  */
@@ -497,9 +716,11 @@ static private_x509_ocsp_request_t *create_empty()
                                        .destroy = _destroy,
                                },
                                .get_nonce = _get_nonce,
+                               .get_signer_cert = _get_signer_cert,
+                               .create_request_enumerator = _create_request_enumerator,
                        },
                },
-               .candidates = linked_list_create(),
+               .reqCerts = linked_list_create(),
                .ref = 1,
        );
 
@@ -511,12 +732,15 @@ static private_x509_ocsp_request_t *create_empty()
  */
 x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args)
 {
-       private_x509_ocsp_request_t *req;
-       certificate_t *cert;
+       private_x509_ocsp_request_t *this;
        private_key_t *private;
        identification_t *subject;
+       certificate_t *cert;
+       x509_t *x509;
+       req_cert_t *reqCert;
+
+       this = create_empty();
 
-       req = create_empty();
        while (TRUE)
        {
                switch (va_arg(args, builder_part_t))
@@ -525,43 +749,137 @@ x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args
                                cert = va_arg(args, certificate_t*);
                                if (cert->get_type(cert) == CERT_X509)
                                {
-                                       req->ca = (x509_t*)cert->get_ref(cert);
+                                       this->cacert = cert->get_ref(cert);
                                }
                                continue;
                        case BUILD_CERT:
                                cert = va_arg(args, certificate_t*);
                                if (cert->get_type(cert) == CERT_X509)
                                {
-                                       req->candidates->insert_last(req->candidates,
-                                                                                                cert->get_ref(cert));
+                                       x509 = (x509_t*)cert;
+                                       INIT(reqCert,
+                                               .serialNumber = chunk_clone(x509->get_serial(x509)),
+                                       );
+                                       this->reqCerts->insert_last(this->reqCerts, reqCert);
                                }
                                continue;
                        case BUILD_SIGNING_CERT:
                                cert = va_arg(args, certificate_t*);
-                               req->cert = cert->get_ref(cert);
+                               this->cert = cert->get_ref(cert);
                                continue;
                        case BUILD_SIGNING_KEY:
                                private = va_arg(args, private_key_t*);
-                               req->key = private->get_ref(private);
+                               this->key = private->get_ref(private);
                                continue;
                        case BUILD_SUBJECT:
                                subject = va_arg(args, identification_t*);
-                               req->requestor = subject->clone(subject);
+                               this->requestor = subject->clone(subject);
                                continue;
                        case BUILD_END:
                                break;
                        default:
-                               destroy(req);
-                               return NULL;
+                               goto error;
                }
                break;
        }
-       if (req->ca)
+
+       if (this->cacert)
        {
-               req->encoding = build_OCSPRequest(req);
-               return &req->public;
+               chunk_t  issuerNameHash, issuerKeyHash;
+               enumerator_t *enumerator;
+               identification_t *issuer;
+               public_key_t *public;
+               req_cert_t *reqCert;
+               hasher_t *hasher;
+
+               public = this->cacert->get_public_key(this->cacert);
+               if (!public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &issuerKeyHash))
+               {
+                       DBG1(DBG_LIB, "failed to compute SHA1 issuerKeyHash");
+                       public->destroy(public);
+                       goto error;
+               }
+               public->destroy(public);
+
+               hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+               if (!hasher)
+               {
+                       DBG1(DBG_LIB, "failed to create SHA1 hasher");
+                       goto error;
+               }
+
+               issuer = this->cacert->get_subject(this->cacert);
+               if (!hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
+                                                                                 &issuerNameHash))
+               {
+                       DBG1(DBG_LIB, "failed to compute SHA1 issuerNameHash");
+                       hasher->destroy(hasher);
+                       goto error;
+               }
+               hasher->destroy(hasher);
+
+               enumerator = this->reqCerts->create_enumerator(this->reqCerts);
+               while (enumerator->enumerate(enumerator, &reqCert))
+               {
+                       reqCert->hashAlgorithm  = HASH_SHA1;
+                       reqCert->issuerNameHash = chunk_clone(issuerNameHash);
+                       reqCert->issuerKeyHash  = chunk_clone(issuerKeyHash);
+               }
+               enumerator->destroy(enumerator);
+               chunk_free(&issuerNameHash);
+
+               this->encoding = build_OCSPRequest(this);
+
+               return &this->public;
        }
-       destroy(req);
+
+error:
+       destroy(this);
        return NULL;
 }
 
+/**
+ * load an OCSP request
+ */
+static x509_ocsp_request_t *load(chunk_t blob)
+{
+       private_x509_ocsp_request_t *this;
+
+       this = create_empty();
+       this->encoding = chunk_clone(blob);
+
+       if (!parse_OCSPRequest(this))
+       {
+               destroy(this);
+               return NULL;
+       }
+       return &this->public;
+}
+
+/**
+ * See header.
+ */
+x509_ocsp_request_t *x509_ocsp_request_load(certificate_type_t type, va_list args)
+{
+       chunk_t blob = chunk_empty;
+
+       while (TRUE)
+       {
+               switch (va_arg(args, builder_part_t))
+               {
+                       case BUILD_BLOB_ASN1_DER:
+                               blob = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_END:
+                               break;
+                       default:
+                               return NULL;
+               }
+               break;
+       }
+       if (blob.ptr)
+       {
+               return load(blob);
+       }
+       return NULL;
+}
index d9ff4f5cbec2332a88ae86033310c21cb46af8c4..74ba0532b15ff099ac31e617d56a3043c6fc65da 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008-2009 Martin Willi
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
  *
  * Copyright (C) secunet Security Networks AG
  *
@@ -54,4 +55,13 @@ struct x509_ocsp_request_t {
  */
 x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args);
 
+/**
+ * Load a X.509 OCSP request.
+ *
+ * @param type      certificate type, CERT_X509_OCSP_REQUEST only
+ * @param args      builder_part_t argument list
+ * @return          OCSP request, NULL on failure
+ */
+x509_ocsp_request_t *x509_ocsp_request_load(certificate_type_t type, va_list args);
+
 #endif /** X509_OCSP_REQUEST_H_ @}*/
index 0570e7a317f3dba021d50404321f60d719bdc5ec..6742bc0cd613b5427459697ac49bd8d5cff611d0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008-2009 Martin Willi
+ * Copyright (C) 2023 Andreas Steffen, strongSec GmbH
  *
  * Copyright (C) secunet Security Networks AG
  *
@@ -69,6 +70,8 @@ METHOD(plugin_t, get_features, int,
                        PLUGIN_PROVIDE(CERT_ENCODE, CERT_X509_OCSP_REQUEST),
                                PLUGIN_DEPENDS(HASHER, HASH_SHA1),
                                PLUGIN_DEPENDS(RNG, RNG_WEAK),
+               PLUGIN_REGISTER(CERT_DECODE, x509_ocsp_request_load, TRUE),
+                       PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_OCSP_REQUEST),
                PLUGIN_REGISTER(CERT_DECODE, x509_ocsp_response_load, TRUE),
                        PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_OCSP_RESPONSE),