Skip to content

self-signed certificates: Add CN as subjectAltName #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

New functionality:

For self-signed certificates generated on the ESP32, the CN is now added as subjectAltName

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)

Expand Down
71 changes: 71 additions & 0 deletions src/SSLCert.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#include "SSLCert.hpp"

#include <Arduino.h>

#ifndef HTTPS_DISABLE_SELFSIGNING
#include <mbedtls/asn1write.h>
#include <mbedtls/oid.h>
#endif

namespace httpsserver {

SSLCert::SSLCert(unsigned char * certData, uint16_t certLength, unsigned char * pkData, uint16_t pkLength):
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions src/SSLCert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
*
Expand Down