static int set_altname(X509 *crt, ...) { int ret = 0; GENERAL_NAMES *gens = NULL; GENERAL_NAME *gen = NULL; ASN1_IA5STRING *ia5 = NULL; va_list ap; va_start(ap, crt); gens = sk_GENERAL_NAME_new_null(); if (gens == NULL) goto out; while (1) { int type; const char *name; type = va_arg(ap, int); if (type == 0) break; name = va_arg(ap, const char *); gen = GENERAL_NAME_new(); if (gen == NULL) goto out; ia5 = ASN1_IA5STRING_new(); if (ia5 == NULL) goto out; if (!ASN1_STRING_set(ia5, name, -1)) goto out; switch (type) { case GEN_EMAIL: case GEN_DNS: GENERAL_NAME_set0_value(gen, type, ia5); ia5 = NULL; break; default: abort(); } sk_GENERAL_NAME_push(gens, gen); gen = NULL; } if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0)) goto out; ret = 1; out: ASN1_IA5STRING_free(ia5); GENERAL_NAME_free(gen); GENERAL_NAMES_free(gens); va_end(ap); return ret; }
LWS_VISIBLE LWS_EXTERN int lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, const char *san_b) { GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); GENERAL_NAME *gen = NULL; ASN1_IA5STRING *ia5 = NULL; X509_NAME *name; if (!gens) return 1; vhost->tls.ss = lws_zalloc(sizeof(*vhost->tls.ss), "sni cert"); if (!vhost->tls.ss) { GENERAL_NAMES_free(gens); return 1; } vhost->tls.ss->x509 = X509_new(); if (!vhost->tls.ss->x509) goto bail; ASN1_INTEGER_set(X509_get_serialNumber(vhost->tls.ss->x509), 1); X509_gmtime_adj(X509_get_notBefore(vhost->tls.ss->x509), 0); X509_gmtime_adj(X509_get_notAfter(vhost->tls.ss->x509), 3600); vhost->tls.ss->pkey = EVP_PKEY_new(); if (!vhost->tls.ss->pkey) goto bail0; if (lws_tls_openssl_rsa_new_key(&vhost->tls.ss->rsa, 4096)) goto bail1; if (!EVP_PKEY_assign_RSA(vhost->tls.ss->pkey, vhost->tls.ss->rsa)) goto bail2; X509_set_pubkey(vhost->tls.ss->x509, vhost->tls.ss->pkey); name = X509_get_subject_name(vhost->tls.ss->x509); X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"GB", -1, -1, 0); X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"somecompany", -1, -1, 0); if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (unsigned char *)"temp.acme.invalid", -1, -1, 0) != 1) { lwsl_notice("failed to add CN\n"); goto bail2; } X509_set_issuer_name(vhost->tls.ss->x509, name); /* add the SAN payloads */ gen = GENERAL_NAME_new(); ia5 = ASN1_IA5STRING_new(); if (!ASN1_STRING_set(ia5, san_a, -1)) { lwsl_notice("failed to set ia5\n"); GENERAL_NAME_free(gen); goto bail2; } GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); sk_GENERAL_NAME_push(gens, gen); if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, gens, 0, X509V3_ADD_APPEND) != 1) goto bail2; GENERAL_NAMES_free(gens); if (san_b && san_b[0]) { gens = sk_GENERAL_NAME_new_null(); gen = GENERAL_NAME_new(); ia5 = ASN1_IA5STRING_new(); if (!ASN1_STRING_set(ia5, san_a, -1)) { lwsl_notice("failed to set ia5\n"); GENERAL_NAME_free(gen); goto bail2; } GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); sk_GENERAL_NAME_push(gens, gen); if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, gens, 0, X509V3_ADD_APPEND) != 1) goto bail2; GENERAL_NAMES_free(gens); } /* sign it with our private key */ if (!X509_sign(vhost->tls.ss->x509, vhost->tls.ss->pkey, EVP_sha256())) goto bail2; #if 0 {/* useful to take a sample of a working cert for mbedtls to crib */ FILE *fp = fopen("/tmp/acme-temp-cert", "w+"); i2d_X509_fp(fp, vhost->tls.ss->x509); fclose(fp); } #endif /* tell the vhost to use our crafted certificate */ SSL_CTX_use_certificate(vhost->tls.ssl_ctx, vhost->tls.ss->x509); /* and to use our generated private key */ SSL_CTX_use_PrivateKey(vhost->tls.ssl_ctx, vhost->tls.ss->pkey); return 0; bail2: RSA_free(vhost->tls.ss->rsa); bail1: EVP_PKEY_free(vhost->tls.ss->pkey); bail0: X509_free(vhost->tls.ss->x509); bail: lws_free(vhost->tls.ss); GENERAL_NAMES_free(gens); return 1; }