Added ability to include the SubjectAltName extension to a CSR
Signed-off-by: Hannes Tschofenig <hannes.tschofenig@arm.com>
diff --git a/include/mbedtls/x509_csr.h b/include/mbedtls/x509_csr.h
index 0c204be..2ac5afa 100644
--- a/include/mbedtls/x509_csr.h
+++ b/include/mbedtls/x509_csr.h
@@ -83,6 +83,19 @@
}
mbedtls_x509write_csr;
+typedef struct mbedtls_x509_san_node {
+ int type; /**< Subject Alternative Name types */
+ char *name; /**< Value, following the syntax allowed bythe type */
+ size_t len; /**< Length of the provided value */
+}
+mbedtls_x509_san_node;
+
+typedef struct mbedtls_x509_san_list {
+ mbedtls_x509_san_node node;
+ struct mbedtls_x509_san_list *next;
+}
+mbedtls_x509_san_list;
+
#if defined(MBEDTLS_X509_CSR_PARSE_C)
/**
* \brief Load a Certificate Signing Request (CSR) in DER format
@@ -221,6 +234,20 @@
int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage);
/**
+ * \brief Set Subject Alternative Name
+ *
+ * \param ctx CSR context to use
+ * \param san_list List of SAN values
+ *
+ * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED
+ *
+ * \note Only "dnsName", "uniformResourceIdentifier" and "otherName",
+ * as defined in RFC 5280, are supported.
+ */
+int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx,
+ const mbedtls_x509_san_list *san_list);
+
+/**
* \brief Set the Netscape Cert Type flags
* (e.g. MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT | MBEDTLS_X509_NS_CERT_TYPE_EMAIL)
*
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index d8d8e99..1d46fdb 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -85,6 +85,79 @@
critical, val, val_len);
}
+int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx,
+ const mbedtls_x509_san_list *san_list)
+{
+ int ret = 0;
+ size_t sandeep = 0;
+ const mbedtls_x509_san_list *cur = san_list;
+ unsigned char *buf;
+ unsigned char *p;
+ size_t len;
+ size_t buflen = 0;
+
+ /* Determine the maximum size of the SubjectAltName list */
+ while (cur != NULL) {
+ if (cur->node.len <= 0) {
+ return 0;
+ }
+
+ /* Calculate size of the required buffer:
+ * + length of value for each name entry,
+ * + maximum 4 bytes for the length field,
+ * + 1 byte for the tag/type.
+ */
+ buflen += cur->node.len + 4 + 1;
+
+ cur = cur->next;
+ }
+
+ /* Add the extra length field and tag */
+ buflen += 4 + 1;
+
+ /* Allocate buffer */
+ buf = mbedtls_calloc(1, buflen);
+ if (buf == NULL) {
+ return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+ }
+
+ mbedtls_platform_zeroize(buf, buflen);
+ p = buf + buflen;
+
+ /* Write ASN.1-based structure */
+ cur = san_list;
+ len = 0;
+ while (cur != NULL) {
+ MBEDTLS_ASN1_CHK_ADD(len,
+ mbedtls_asn1_write_raw_buffer(&p, buf,
+ (const unsigned char *) cur->node.name,
+ cur->node.len));
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, cur->node.len));
+ MBEDTLS_ASN1_CHK_ADD(len,
+ mbedtls_asn1_write_tag(&p, buf,
+ MBEDTLS_ASN1_CONTEXT_SPECIFIC |
+ cur->node.type));
+
+ cur = cur->next;
+ }
+
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
+ MBEDTLS_ASN1_CHK_ADD(len,
+ mbedtls_asn1_write_tag(&p, buf,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
+
+ ret = mbedtls_x509write_csr_set_extension(
+ ctx,
+ MBEDTLS_OID_SUBJECT_ALT_NAME,
+ MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
+ 0,
+ buf + buflen - len,
+ len);
+
+ mbedtls_free(buf);
+ return ret;
+}
+
int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage)
{
unsigned char buf[4] = { 0 };
diff --git a/programs/x509/cert_req.c b/programs/x509/cert_req.c
index 8ef5932..db80be3 100644
--- a/programs/x509/cert_req.c
+++ b/programs/x509/cert_req.c
@@ -63,6 +63,11 @@
" debug_level=%%d default: 0 (disabled)\n" \
" 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" \
+ " DNS:value\n" \
+ " URI:value\n" \
+ " OTHER:value\n" \
" key_usage=%%s default: (empty)\n" \
" Comma-separated-list of values:\n" \
" digital_signature\n" \
@@ -96,16 +101,17 @@
* global options
*/
struct options {
- const char *filename; /* filename of the key file */
- const char *password; /* password for the key file */
- int debug_level; /* level of debugging */
- const char *output_file; /* where to store the constructed key file */
- const char *subject_name; /* subject name for certificate request */
- unsigned char key_usage; /* key usage flags */
- int force_key_usage; /* Force adding the KeyUsage extension */
- unsigned char ns_cert_type; /* NS cert type */
- int force_ns_cert_type; /* Force adding NsCertType extension */
- mbedtls_md_type_t md_alg; /* Hash algorithm used for signature. */
+ const char *filename; /* filename of the key file */
+ const char *password; /* password for the key file */
+ int debug_level; /* level of debugging */
+ const char *output_file; /* where to store the constructed key file */
+ const char *subject_name; /* subject name for certificate request */
+ mbedtls_x509_san_list *san_list; /* subjectAltName for certificate request */
+ unsigned char key_usage; /* key usage flags */
+ int force_key_usage; /* Force adding the KeyUsage extension */
+ unsigned char ns_cert_type; /* NS cert type */
+ int force_ns_cert_type; /* Force adding NsCertType extension */
+ mbedtls_md_type_t md_alg; /* Hash algorithm used for signature. */
} opt;
int write_certificate_request(mbedtls_x509write_csr *req, const char *output_file,
@@ -145,11 +151,12 @@
mbedtls_pk_context key;
char buf[1024];
int i;
- char *p, *q, *r;
+ char *p, *q, *r, *r2;
mbedtls_x509write_csr req;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
const char *pers = "csr example app";
+ mbedtls_x509_san_list *cur, *prev;
/*
* Set to sane values
@@ -175,6 +182,7 @@
opt.ns_cert_type = DFL_NS_CERT_TYPE;
opt.force_ns_cert_type = DFL_FORCE_NS_CERT_TYPE;
opt.md_alg = DFL_MD_ALG;
+ opt.san_list = NULL;
for (i = 1; i < argc; i++) {
@@ -197,6 +205,52 @@
}
} else if (strcmp(p, "subject_name") == 0) {
opt.subject_name = q;
+ } else if (strcmp(p, "san") == 0) {
+ prev = NULL;
+
+ while (q != NULL) {
+ if ((r = strchr(q, ',')) != NULL) {
+ *r++ = '\0';
+ }
+
+ 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 ((r2 = strchr(q, ':')) != NULL) {
+ *r2++ = '\0';
+ }
+
+ 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, "OTHER") == 0) {
+ cur->node.type = MBEDTLS_X509_SAN_OTHER_NAME;
+ } else {
+ mbedtls_free(cur);
+ goto usage;
+ }
+
+ q = r2;
+
+ cur->node.name = q;
+ cur->node.len = strlen(q);
+
+ if (prev == NULL) {
+ opt.san_list = cur;
+ } else {
+ prev->next = cur;
+ }
+
+ prev = cur;
+ q = r;
+ }
+
} else if (strcmp(p, "md") == 0) {
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_string(q);
@@ -274,14 +328,39 @@
}
}
+ /* Set the MD algorithm to use for the signature in the CSR */
mbedtls_x509write_csr_set_md_alg(&req, opt.md_alg);
+ /* Set the Key Usage Extension flags in the CSR */
if (opt.key_usage || opt.force_key_usage == 1) {
- mbedtls_x509write_csr_set_key_usage(&req, opt.key_usage);
+ ret = mbedtls_x509write_csr_set_key_usage(&req, opt.key_usage);
+
+ if (ret != 0) {
+ mbedtls_printf(" failed\n ! mbedtls_x509write_csr_set_key_usage returned %d", ret);
+ goto exit;
+ }
}
+ /* Set the Cert Type flags in the CSR */
if (opt.ns_cert_type || opt.force_ns_cert_type == 1) {
- mbedtls_x509write_csr_set_ns_cert_type(&req, opt.ns_cert_type);
+ ret = mbedtls_x509write_csr_set_ns_cert_type(&req, opt.ns_cert_type);
+
+ if (ret != 0) {
+ mbedtls_printf(" failed\n ! mbedtls_x509write_csr_set_ns_cert_type returned %d", ret);
+ goto exit;
+ }
+ }
+
+ /* Set the SubjectAltName in the CSR */
+ if (opt.san_list != NULL) {
+ ret = mbedtls_x509write_csr_set_subject_alternative_name(&req, opt.san_list);
+
+ if (ret != 0) {
+ mbedtls_printf(
+ " failed\n ! mbedtls_x509write_csr_set_subject_alternative_name returned %d",
+ ret);
+ goto exit;
+ }
}
/*
@@ -363,6 +442,14 @@
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
+ cur = opt.san_list;
+ while (cur != NULL) {
+ prev = cur;
+ cur = cur->next;
+ mbedtls_free(prev);
+ }
+
+
mbedtls_exit(exit_code);
}
#endif /* MBEDTLS_X509_CSR_WRITE_C && MBEDTLS_PK_PARSE_C && MBEDTLS_FS_IO &&