1
0
mirror of https://github.com/Mbed-TLS/mbedtls.git synced 2025-08-08 17:42:09 +03:00

Merge pull request #7400 from AndrzejKurek/cert-write-sans

Add a possibility to generate certificates with a Subject Alternative Name
This commit is contained in:
Gilles Peskine
2023-06-05 15:38:38 +02:00
committed by GitHub
11 changed files with 397 additions and 36 deletions

View File

@@ -66,7 +66,7 @@ int main(void)
" output_file=%%s default: cert.req\n" \
" subject_name=%%s default: CN=Cert,O=mbed TLS,C=UK\n" \
" san=%%s default: (none)\n" \
" Comma-separated-list of values:\n" \
" Semicolon-separated-list of values:\n" \
" DNS:value\n" \
" URI:value\n" \
" IP:value (Only IPv4 is supported)\n" \
@@ -119,7 +119,7 @@ struct options {
static void ip_string_to_bytes(const char *str, uint8_t *bytes, int maxBytes)
{
for (int i = 0; i < maxBytes; i++) {
bytes[i] = (uint8_t) strtoul(str, NULL, 16);
bytes[i] = (uint8_t) strtoul(str, NULL, 10);
str = strchr(str, '.');
if (str == NULL || *str == '\0') {
break;

View File

@@ -79,6 +79,7 @@ int main(void)
#define DFL_NOT_AFTER "20301231235959"
#define DFL_SERIAL "1"
#define DFL_SERIAL_HEX "1"
#define DFL_EXT_SUBJECTALTNAME ""
#define DFL_SELFSIGN 0
#define DFL_IS_CA 0
#define DFL_MAX_PATHLEN -1
@@ -134,6 +135,13 @@ int main(void)
" subject_identifier=%%s default: 1\n" \
" Possible values: 0, 1\n" \
" (Considered for v3 only)\n" \
" san=%%s default: (none)\n" \
" Semicolon-separated-list of values:\n" \
" DNS:value\n" \
" URI:value\n" \
" RFC822:value\n" \
" IP:value (Only IPv4 is supported)\n" \
" DN:list of comma separated key=value pairs\n" \
" authority_identifier=%%s default: 1\n" \
" Possible values: 0, 1\n" \
" (Considered for v3 only)\n" \
@@ -188,6 +196,7 @@ struct options {
const char *issuer_pwd; /* password for the issuer key file */
const char *output_file; /* where to store the constructed CRT */
const char *subject_name; /* subject name for certificate */
mbedtls_x509_san_list *san_list; /* subjectAltName for certificate */
const char *issuer_name; /* issuer name for certificate */
const char *not_before; /* validity period not before */
const char *not_after; /* validity period not after */
@@ -207,6 +216,18 @@ struct options {
int format; /* format */
} opt;
static void ip_string_to_bytes(const char *str, uint8_t *bytes, int maxBytes)
{
for (int i = 0; i < maxBytes; i++) {
bytes[i] = (uint8_t) strtoul(str, NULL, 10);
str = strchr(str, '.');
if (str == NULL || *str == '\0') {
break;
}
str++;
}
}
int write_certificate(mbedtls_x509write_cert *crt, const char *output_file,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng)
@@ -314,7 +335,9 @@ int main(int argc, char *argv[])
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
const char *pers = "crt example app";
mbedtls_x509_san_list *cur, *prev;
mbedtls_asn1_named_data *ext_san_dirname = NULL;
uint8_t ip[4] = { 0 };
/*
* Set to sane values
*/
@@ -370,6 +393,7 @@ usage:
opt.authority_identifier = DFL_AUTH_IDENT;
opt.basic_constraints = DFL_CONSTRAINTS;
opt.format = DFL_FORMAT;
opt.san_list = NULL;
for (i = 1; i < argc; i++) {
@@ -526,6 +550,92 @@ usage:
*tail = ext_key_usage;
tail = &ext_key_usage->next;
q = r;
}
} else if (strcmp(p, "san") == 0) {
char *subtype_value;
prev = NULL;
while (q != NULL) {
char *semicolon;
r = q;
/* Find the first non-escaped ; occurrence and remove escaped ones */
do {
if ((semicolon = strchr(r, ';')) != NULL) {
if (*(semicolon-1) != '\\') {
r = semicolon;
break;
}
/* Remove the escape character */
size_t size_left = strlen(semicolon);
memmove(semicolon-1, semicolon, size_left);
*(semicolon + size_left - 1) = '\0';
/* r will now point at the character after the semicolon */
r = semicolon;
}
} while (semicolon != NULL);
if (semicolon != NULL) {
*r++ = '\0';
} else {
r = NULL;
}
cur = mbedtls_calloc(1, sizeof(mbedtls_x509_san_list));
if (cur == NULL) {
mbedtls_printf("Not enough memory for subjectAltName list\n");
goto usage;
}
cur->next = NULL;
if ((subtype_value = strchr(q, ':')) != NULL) {
*subtype_value++ = '\0';
}
if (strcmp(q, "RFC822") == 0) {
cur->node.type = MBEDTLS_X509_SAN_RFC822_NAME;
} else if (strcmp(q, "URI") == 0) {
cur->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER;
} else if (strcmp(q, "DNS") == 0) {
cur->node.type = MBEDTLS_X509_SAN_DNS_NAME;
} else if (strcmp(q, "IP") == 0) {
cur->node.type = MBEDTLS_X509_SAN_IP_ADDRESS;
ip_string_to_bytes(subtype_value, ip, 4);
cur->node.san.unstructured_name.p = (unsigned char *) ip;
cur->node.san.unstructured_name.len = sizeof(ip);
} else if (strcmp(q, "DN") == 0) {
cur->node.type = MBEDTLS_X509_SAN_DIRECTORY_NAME;
if ((ret = mbedtls_x509_string_to_names(&ext_san_dirname,
subtype_value)) != 0) {
mbedtls_strerror(ret, buf, sizeof(buf));
mbedtls_printf(
" failed\n ! mbedtls_x509_string_to_names "
"returned -0x%04x - %s\n\n",
(unsigned int) -ret, buf);
goto exit;
}
cur->node.san.directory_name = *ext_san_dirname;
} else {
mbedtls_free(cur);
goto usage;
}
if (cur->node.type == MBEDTLS_X509_SAN_RFC822_NAME ||
cur->node.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER ||
cur->node.type == MBEDTLS_X509_SAN_DNS_NAME) {
cur->node.san.unstructured_name.p = (unsigned char *) subtype_value;
cur->node.san.unstructured_name.len = strlen(subtype_value);
}
if (prev == NULL) {
opt.san_list = cur;
} else {
prev->next = cur;
}
prev = cur;
q = r;
}
} else if (strcmp(p, "ns_cert_type") == 0) {
@@ -833,6 +943,17 @@ usage:
mbedtls_printf(" ok\n");
}
if (opt.san_list != NULL) {
ret = mbedtls_x509write_crt_set_subject_alternative_name(&crt, opt.san_list);
if (ret != 0) {
mbedtls_printf(
" failed\n ! mbedtls_x509write_crt_set_subject_alternative_name returned %d",
ret);
goto exit;
}
}
if (opt.ext_key_usage) {
mbedtls_printf(" . Adding the Extended Key Usage extension ...");
fflush(stdout);
@@ -888,6 +1009,7 @@ exit:
#if defined(MBEDTLS_X509_CSR_PARSE_C)
mbedtls_x509_csr_free(&csr);
#endif /* MBEDTLS_X509_CSR_PARSE_C */
mbedtls_asn1_free_named_data_list(&ext_san_dirname);
mbedtls_x509_crt_free(&issuer_crt);
mbedtls_x509write_crt_free(&crt);
mbedtls_pk_free(&loaded_subject_key);