From 253013d9bfde6f0aaaef563065aee1c29f113f5d Mon Sep 17 00:00:00 2001 From: Frank Hessel Date: Sat, 23 Jan 2021 04:33:18 +0100 Subject: [PATCH] self-signed certificates: Add CN as subjectAltName --- CHANGELOG.md | 4 +-- src/SSLCert.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ src/SSLCert.hpp | 3 +++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ba97b..a913465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ New functionality: -– +– For self-signed certificates generated on the ESP32, the CN is now added as subjectAltName Bug fixes: @@ -12,7 +12,7 @@ Bug fixes: Breaking changes: -– +– Generating self-signed certificates requires now a `CN=` as part of the distinguished name of the subject ## [v1.0.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0) diff --git a/src/SSLCert.cpp b/src/SSLCert.cpp index 3df7073..7f638ca 100644 --- a/src/SSLCert.cpp +++ b/src/SSLCert.cpp @@ -1,5 +1,12 @@ #include "SSLCert.hpp" +#include + +#ifndef HTTPS_DISABLE_SELFSIGNING +#include +#include +#endif + namespace httpsserver { SSLCert::SSLCert(unsigned char * certData, uint16_t certLength, unsigned char * pkData, uint16_t pkLength): @@ -53,6 +60,56 @@ void SSLCert::clear() { #ifndef HTTPS_DISABLE_SELFSIGNING +/** + * Returns the CN value from a DN, or "" if it cannot be found + */ +static std::string get_cn(std::string dn) { + size_t cnStart = dn.find("CN="); + if (cnStart == std::string::npos) { + return ""; + } + cnStart += 3; + size_t cnStop = dn.find(",", cnStart); + if (cnStop == std::string::npos) { + cnStop = dn.length(); + } + return dn.substr(cnStart, cnStop - cnStart); +} + +/** + * Sets the DN as subjectAltName extension in the certificate + */ +static int add_subject_alt_name(mbedtls_x509write_cert *crt, std::string &cn) { + size_t bufsize = cn.length() + 8; // some additional space for tags and length fields + uint8_t buf[bufsize]; + uint8_t *p = &buf[bufsize - 1]; + uint8_t *start = buf; + int length = 0; + int ret; // used by MBEDTLS macro + + // The ASN structure that we will construct as parameter for write_crt_set_extension is as follows: + // | 0x30 = Sequence | length | 0x82 = dNSName, context-specific | length | cn0 | cn1 | cn2 | cn3 | .. | cnn | + // ↑ : ↑ `-------------v------------------´: + // | : `-------------------´ : + // | `----------v------------------------------------------------------------------´ + // `---------------´ + // Let's encrypt has useful infos: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/#choice-and-any-encoding + MBEDTLS_ASN1_CHK_ADD(length, + mbedtls_asn1_write_raw_buffer(&p, start, (uint8_t*)cn.c_str(), cn.length())); + MBEDTLS_ASN1_CHK_ADD(length, + mbedtls_asn1_write_len(&p, start, length)); + MBEDTLS_ASN1_CHK_ADD(length, + mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0x02)); // 0x02 = dNSName + MBEDTLS_ASN1_CHK_ADD(length, + mbedtls_asn1_write_len(&p, start, length)); + MBEDTLS_ASN1_CHK_ADD(length, + mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE )); + return mbedtls_x509write_crt_set_extension( crt, + MBEDTLS_OID_SUBJECT_ALT_NAME, MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), + 0, // not critical + p, length); +} + /** * Function to create the key for a self-signed certificate. * @@ -169,6 +226,12 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom char dn_cstr[dn.length()+1]; strcpy(dn_cstr, dn.c_str()); + // Get the common name for the subject alternative name + std::string cn = get_cn(dn); + if (cn == "") { + return HTTPS_SERVER_ERROR_CERTGEN_CN; + } + // Initialize the entropy source mbedtls_entropy_init( &entropy ); @@ -209,6 +272,13 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom goto error_after_cert; } + // Set subject alternative name + stepRes = add_subject_alt_name( &crt, cn ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME; + goto error_after_cert; + } + // Set the validity of the certificate. At the moment, it's fixed from 2019 to end of 2029. stepRes = mbedtls_x509write_crt_set_validity( &crt, validityFrom.c_str(), validityTo.c_str()); if (stepRes != 0) { @@ -281,6 +351,7 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom error_after_entropy: mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_entropy_free( &entropy ); + return funcRes; } diff --git a/src/SSLCert.hpp b/src/SSLCert.hpp index 6296c3d..2fc9909 100644 --- a/src/SSLCert.hpp +++ b/src/SSLCert.hpp @@ -27,6 +27,7 @@ #define HTTPS_SERVER_ERROR_CERTGEN_NAME 0x17 #define HTTPS_SERVER_ERROR_CERTGEN_SERIAL 0x18 #define HTTPS_SERVER_ERROR_CERTGEN_VALIDITY 0x19 +#define HTTPS_SERVER_ERROR_CERTGEN_CN 0x1a #endif // !HTTPS_DISABLE_SELFSIGNING @@ -165,6 +166,8 @@ enum SSLKeySize { * would be: * CN=myesp.local,O=acme,C=US * + * The subjectAltName is extracted from the CN component of the distinguished name. + * * The strings validFrom and validUntil have to be formatted like this: * "20190101000000", "20300101000000" *