From 5f1318b10e7e947d4ebf2077fdfec5856b7429a7 Mon Sep 17 00:00:00 2001 From: Christos Tsantilas Date: Tue, 14 Jun 2016 18:56:12 +0300 Subject: [PATCH] TLS Authority Key Identifier certificate extension This patch add support for mimicking TLS Authority Key Identifier certificate extension in Squid generated TLS certificates: If the origin server certificate has that extension, the generated certificate (via the ssl_crtd daemon or internally) should have the same extension, with the same set of fields if possible. This is a Measurement Factory project --- src/ssl/gadgets.cc | 89 ++++++++++++++++++++++++++++++++++++++++++++-- src/ssl/gadgets.h | 18 ++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc index 218167b3f5..d7dd0813a8 100644 --- a/src/ssl/gadgets.cc +++ b/src/ssl/gadgets.cc @@ -264,13 +264,95 @@ std::string & Ssl::CertificateProperties::dbKey() const return certKey; } +/// Check if mimicCert certificate has the Authority Key Identifier extension +/// and if yes add the extension to cert certificate with the same fields if +/// possible. If the issuerCert certificate does not have the Subject Key +/// Identifier extension (required to build the keyIdentifier field of +/// AuthorityKeyIdentifier) then the authorityCertIssuer and +/// authorityCertSerialNumber fields added. +static bool +mimicAuthorityKeyId(Security::CertPointer &cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert) +{ + if (!mimicCert.get() || !issuerCert.get()) + return false; + + Ssl::AUTHORITY_KEYID_Pointer akid((AUTHORITY_KEYID *)X509_get_ext_d2i(mimicCert.get(), NID_authority_key_identifier, nullptr, nullptr)); + + bool addKeyId = false, addIssuer = false; + if (akid.get()) { + addKeyId = (akid.get()->keyid != nullptr); + addIssuer = (akid.get()->issuer && akid.get()->serial); + } + + if (!addKeyId && !addIssuer) + return false; // No need to add AuthorityKeyIdentifier + + Ssl::ASN1_OCTET_STRING_Pointer issuerKeyId; + if (addKeyId) { + X509_EXTENSION *ext; + // Check if the issuer has the Subject Key Identifier extension + const int indx = X509_get_ext_by_NID(issuerCert.get(), NID_subject_key_identifier, -1); + if (indx >= 0 && (ext = X509_get_ext(issuerCert.get(), indx))) { + issuerKeyId.reset((ASN1_OCTET_STRING *)X509V3_EXT_d2i(ext)); + } + } + + Ssl::X509_NAME_Pointer issuerName; + Ssl::ASN1_INT_Pointer issuerSerial; + if (issuerKeyId.get() == nullptr || addIssuer) { + issuerName.reset(X509_NAME_dup(X509_get_issuer_name(issuerCert.get()))); + issuerSerial.reset(M_ASN1_INTEGER_dup(X509_get_serialNumber(issuerCert.get()))); + } + + Ssl::AUTHORITY_KEYID_Pointer theAuthKeyId(AUTHORITY_KEYID_new()); + if (!theAuthKeyId.get()) + return false; + theAuthKeyId.get()->keyid = issuerKeyId.release(); + if (issuerName && issuerSerial) { + Ssl::GENERAL_NAME_STACK_Pointer genNames(sk_GENERAL_NAME_new_null()); + if (genNames.get()) { + if (GENERAL_NAME *aname = GENERAL_NAME_new()) { + sk_GENERAL_NAME_push(genNames.get(), aname); + aname->type = GEN_DIRNAME; + aname->d.dirn = issuerName.release(); + theAuthKeyId.get()->issuer = genNames.release(); + theAuthKeyId.get()->serial = issuerSerial.release(); + } + } + } + + // The Authority Key Identifier extension should include KeyId or/and both + /// issuer name and issuer serial + if (!theAuthKeyId.get()->keyid && (!theAuthKeyId.get()->issuer || !theAuthKeyId.get()->serial)) + return false; + + const X509V3_EXT_METHOD *method = X509V3_EXT_get_nid(NID_authority_key_identifier); + if (!method) + return false; + + unsigned char *ext_der = NULL; + int ext_len = ASN1_item_i2d((ASN1_VALUE *)theAuthKeyId.get(), &ext_der, ASN1_ITEM_ptr(method->it)); + Ssl::ASN1_OCTET_STRING_Pointer extOct(M_ASN1_OCTET_STRING_new()); + extOct.get()->data = ext_der; + extOct.get()->length = ext_len; + Ssl::X509_EXTENSION_Pointer extAuthKeyId(X509_EXTENSION_create_by_NID(NULL, NID_authority_key_identifier, 0, extOct.get())); + if (!extAuthKeyId.get()) + return false; + + extOct.release(); + if (!X509_add_ext(cert.get(), extAuthKeyId.get(), -1)) + return false; + + return true; +} + /// Copy certificate extensions from cert to mimicCert. /// Returns the number of extensions copied. // Currently only extensions which are reported by the users that required are // mimicked. More safe to mimic extensions would be added here if users request // them. static int -mimicExtensions(Security::CertPointer & cert, Security::CertPointer const & mimicCert) +mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert) { static int extensions[]= { NID_key_usage, @@ -333,6 +415,9 @@ mimicExtensions(Security::CertPointer & cert, Security::CertPointer const & mimi } } + if (mimicAuthorityKeyId(cert, mimicCert, issuerCert)) + ++added; + // We could also restrict mimicking of the CA extension to CA:FALSE // because Squid does not generate valid fake CA certificates. @@ -409,7 +494,7 @@ static bool buildCertificate(Security::CertPointer & cert, Ssl::CertificatePrope } } - addedExtensions += mimicExtensions(cert, properties.mimicCert); + addedExtensions += mimicExtensions(cert, properties.mimicCert, properties.signWithX509); // According to RFC 5280, using extensions requires v3 certificate. if (addedExtensions) diff --git a/src/ssl/gadgets.h b/src/ssl/gadgets.h index e6c7324542..cbd40b3d83 100644 --- a/src/ssl/gadgets.h +++ b/src/ssl/gadgets.h @@ -15,6 +15,9 @@ #if HAVE_OPENSSL_TXT_DB_H #include #endif +#if HAVE_OPENSSL_X509V3_H +#include +#endif #include namespace Ssl @@ -54,6 +57,9 @@ typedef TidyPointer BIO_Pointer; CtoCpp1(ASN1_INTEGER_free, ASN1_INTEGER *) typedef TidyPointer ASN1_INT_Pointer; +CtoCpp1(ASN1_OCTET_STRING_free, ASN1_OCTET_STRING *) +typedef TidyPointer ASN1_OCTET_STRING_Pointer; + CtoCpp1(TXT_DB_free, TXT_DB *) typedef TidyPointer TXT_DB_Pointer; @@ -69,6 +75,18 @@ typedef TidyPointer X509_REQ_Pointer; sk_free_wrapper(sk_X509_NAME, STACK_OF(X509_NAME) *, X509_NAME_free) typedef TidyPointer X509_NAME_STACK_Pointer; +CtoCpp1(AUTHORITY_KEYID_free, AUTHORITY_KEYID *) +typedef TidyPointer AUTHORITY_KEYID_Pointer; + +sk_free_wrapper(sk_GENERAL_NAME, STACK_OF(GENERAL_NAME) *, GENERAL_NAME_free) +typedef TidyPointer GENERAL_NAME_STACK_Pointer; + +CtoCpp1(GENERAL_NAME_free, GENERAL_NAME *) +typedef TidyPointer GENERAL_NAME_Pointer; + +CtoCpp1(X509_EXTENSION_free, X509_EXTENSION *) +typedef TidyPointer X509_EXTENSION_Pointer; + /** \ingroup SslCrtdSslAPI * Create 1024 bits rsa key. -- 2.39.5