Skip to content

Commit 253013d

Browse files
committed
self-signed certificates: Add CN as subjectAltName
1 parent de1876c commit 253013d

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

CHANGELOG.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
New functionality:
66

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

99
Bug fixes:
1010

1111
1212

1313
Breaking changes:
1414

15-
15+
Generating self-signed certificates requires now a `CN=` as part of the distinguished name of the subject
1616

1717
## [v1.0.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0)
1818

src/SSLCert.cpp

+71
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
#include "SSLCert.hpp"
22

3+
#include <Arduino.h>
4+
5+
#ifndef HTTPS_DISABLE_SELFSIGNING
6+
#include <mbedtls/asn1write.h>
7+
#include <mbedtls/oid.h>
8+
#endif
9+
310
namespace httpsserver {
411

512
SSLCert::SSLCert(unsigned char * certData, uint16_t certLength, unsigned char * pkData, uint16_t pkLength):
@@ -53,6 +60,56 @@ void SSLCert::clear() {
5360

5461
#ifndef HTTPS_DISABLE_SELFSIGNING
5562

63+
/**
64+
* Returns the CN value from a DN, or "" if it cannot be found
65+
*/
66+
static std::string get_cn(std::string dn) {
67+
size_t cnStart = dn.find("CN=");
68+
if (cnStart == std::string::npos) {
69+
return "";
70+
}
71+
cnStart += 3;
72+
size_t cnStop = dn.find(",", cnStart);
73+
if (cnStop == std::string::npos) {
74+
cnStop = dn.length();
75+
}
76+
return dn.substr(cnStart, cnStop - cnStart);
77+
}
78+
79+
/**
80+
* Sets the DN as subjectAltName extension in the certificate
81+
*/
82+
static int add_subject_alt_name(mbedtls_x509write_cert *crt, std::string &cn) {
83+
size_t bufsize = cn.length() + 8; // some additional space for tags and length fields
84+
uint8_t buf[bufsize];
85+
uint8_t *p = &buf[bufsize - 1];
86+
uint8_t *start = buf;
87+
int length = 0;
88+
int ret; // used by MBEDTLS macro
89+
90+
// The ASN structure that we will construct as parameter for write_crt_set_extension is as follows:
91+
// | 0x30 = Sequence | length | 0x82 = dNSName, context-specific | length | cn0 | cn1 | cn2 | cn3 | .. | cnn |
92+
// ↑ : ↑ `-------------v------------------´:
93+
// | : `-------------------´ :
94+
// | `----------v------------------------------------------------------------------´
95+
// `---------------´
96+
// Let's encrypt has useful infos: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/#choice-and-any-encoding
97+
MBEDTLS_ASN1_CHK_ADD(length,
98+
mbedtls_asn1_write_raw_buffer(&p, start, (uint8_t*)cn.c_str(), cn.length()));
99+
MBEDTLS_ASN1_CHK_ADD(length,
100+
mbedtls_asn1_write_len(&p, start, length));
101+
MBEDTLS_ASN1_CHK_ADD(length,
102+
mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0x02)); // 0x02 = dNSName
103+
MBEDTLS_ASN1_CHK_ADD(length,
104+
mbedtls_asn1_write_len(&p, start, length));
105+
MBEDTLS_ASN1_CHK_ADD(length,
106+
mbedtls_asn1_write_tag(&p, start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ));
107+
return mbedtls_x509write_crt_set_extension( crt,
108+
MBEDTLS_OID_SUBJECT_ALT_NAME, MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
109+
0, // not critical
110+
p, length);
111+
}
112+
56113
/**
57114
* Function to create the key for a self-signed certificate.
58115
*
@@ -169,6 +226,12 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom
169226
char dn_cstr[dn.length()+1];
170227
strcpy(dn_cstr, dn.c_str());
171228

229+
// Get the common name for the subject alternative name
230+
std::string cn = get_cn(dn);
231+
if (cn == "") {
232+
return HTTPS_SERVER_ERROR_CERTGEN_CN;
233+
}
234+
172235
// Initialize the entropy source
173236
mbedtls_entropy_init( &entropy );
174237

@@ -209,6 +272,13 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom
209272
goto error_after_cert;
210273
}
211274

275+
// Set subject alternative name
276+
stepRes = add_subject_alt_name( &crt, cn );
277+
if (stepRes != 0) {
278+
funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME;
279+
goto error_after_cert;
280+
}
281+
212282
// Set the validity of the certificate. At the moment, it's fixed from 2019 to end of 2029.
213283
stepRes = mbedtls_x509write_crt_set_validity( &crt, validityFrom.c_str(), validityTo.c_str());
214284
if (stepRes != 0) {
@@ -281,6 +351,7 @@ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom
281351
error_after_entropy:
282352
mbedtls_ctr_drbg_free( &ctr_drbg );
283353
mbedtls_entropy_free( &entropy );
354+
284355
return funcRes;
285356
}
286357

src/SSLCert.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#define HTTPS_SERVER_ERROR_CERTGEN_NAME 0x17
2828
#define HTTPS_SERVER_ERROR_CERTGEN_SERIAL 0x18
2929
#define HTTPS_SERVER_ERROR_CERTGEN_VALIDITY 0x19
30+
#define HTTPS_SERVER_ERROR_CERTGEN_CN 0x1a
3031

3132
#endif // !HTTPS_DISABLE_SELFSIGNING
3233

@@ -165,6 +166,8 @@ enum SSLKeySize {
165166
* would be:
166167
* CN=myesp.local,O=acme,C=US
167168
*
169+
* The subjectAltName is extracted from the CN component of the distinguished name.
170+
*
168171
* The strings validFrom and validUntil have to be formatted like this:
169172
* "20190101000000", "20300101000000"
170173
*

0 commit comments

Comments
 (0)