/* * Copyright (c) 2006 - 2010 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "hx_locl.h" #include /** * @page page_ca Hx509 CA functions * * See the library functions here: @ref hx509_ca */ struct hx509_ca_tbs { hx509_name subject; SubjectPublicKeyInfo spki; ExtKeyUsage eku; GeneralNames san; unsigned key_usage; heim_integer serial; struct { unsigned int proxy:1; unsigned int ca:1; unsigned int key:1; unsigned int serial:1; unsigned int domaincontroller:1; unsigned int xUniqueID:1; } flags; time_t notBefore; time_t notAfter; int pathLenConstraint; /* both for CA and Proxy */ CRLDistributionPoints crldp; heim_bit_string subjectUniqueID; heim_bit_string issuerUniqueID; }; /** * Allocate an to-be-signed certificate object that will be converted * into an certificate. * * @param context A hx509 context. * @param tbs returned to-be-signed certicate object, free with * hx509_ca_tbs_free(). * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs) { *tbs = calloc(1, sizeof(**tbs)); if (*tbs == NULL) return ENOMEM; return 0; } /** * Free an To Be Signed object. * * @param tbs object to free. * * @ingroup hx509_ca */ void hx509_ca_tbs_free(hx509_ca_tbs *tbs) { if (tbs == NULL || *tbs == NULL) return; free_SubjectPublicKeyInfo(&(*tbs)->spki); free_GeneralNames(&(*tbs)->san); free_ExtKeyUsage(&(*tbs)->eku); der_free_heim_integer(&(*tbs)->serial); free_CRLDistributionPoints(&(*tbs)->crldp); der_free_bit_string(&(*tbs)->subjectUniqueID); der_free_bit_string(&(*tbs)->issuerUniqueID); hx509_name_free(&(*tbs)->subject); memset(*tbs, 0, sizeof(**tbs)); free(*tbs); *tbs = NULL; } /** * Set the absolute time when the certificate is valid from. If not * set the current time will be used. * * @param context A hx509 context. * @param tbs object to be signed. * @param t time the certificated will start to be valid * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_notBefore(hx509_context context, hx509_ca_tbs tbs, time_t t) { tbs->notBefore = t; return 0; } /** * Set the absolute time when the certificate is valid to. * * @param context A hx509 context. * @param tbs object to be signed. * @param t time when the certificate will expire * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_notAfter(hx509_context context, hx509_ca_tbs tbs, time_t t) { tbs->notAfter = t; return 0; } /** * Set the relative time when the certificiate is going to expire. * * @param context A hx509 context. * @param tbs object to be signed. * @param delta seconds to the certificate is going to expire. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_notAfter_lifetime(hx509_context context, hx509_ca_tbs tbs, time_t delta) { return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta); } static const struct units templatebits[] = { { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU }, { "KeyUsage", HX509_CA_TEMPLATE_KU }, { "SPKI", HX509_CA_TEMPLATE_SPKI }, { "notAfter", HX509_CA_TEMPLATE_NOTAFTER }, { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE }, { "serial", HX509_CA_TEMPLATE_SERIAL }, { "subject", HX509_CA_TEMPLATE_SUBJECT }, { NULL, 0 } }; /** * Make of template units, use to build flags argument to * hx509_ca_tbs_set_template() with parse_units(). * * @return an units structure. * * @ingroup hx509_ca */ const struct units * hx509_ca_tbs_template_units(void) { return templatebits; } /** * Initialize the to-be-signed certificate object from a template certifiate. * * @param context A hx509 context. * @param tbs object to be signed. * @param flags bit field selecting what to copy from the template * certifiate. * @param cert template certificate. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_template(hx509_context context, hx509_ca_tbs tbs, int flags, hx509_cert cert) { int ret; if (flags & HX509_CA_TEMPLATE_SUBJECT) { if (tbs->subject) hx509_name_free(&tbs->subject); ret = hx509_cert_get_subject(cert, &tbs->subject); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to get subject from template"); return ret; } } if (flags & HX509_CA_TEMPLATE_SERIAL) { der_free_heim_integer(&tbs->serial); ret = hx509_cert_get_serialnumber(cert, &tbs->serial); tbs->flags.serial = !ret; if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy serial number"); return ret; } } if (flags & HX509_CA_TEMPLATE_NOTBEFORE) tbs->notBefore = hx509_cert_get_notBefore(cert); if (flags & HX509_CA_TEMPLATE_NOTAFTER) tbs->notAfter = hx509_cert_get_notAfter(cert); if (flags & HX509_CA_TEMPLATE_SPKI) { free_SubjectPublicKeyInfo(&tbs->spki); ret = hx509_cert_get_SPKI(context, cert, &tbs->spki); tbs->flags.key = !ret; if (ret) return ret; } if (flags & HX509_CA_TEMPLATE_KU) { KeyUsage ku; ret = _hx509_cert_get_keyusage(context, cert, &ku); if (ret) return ret; tbs->key_usage = KeyUsage2int(ku); } if (flags & HX509_CA_TEMPLATE_EKU) { ExtKeyUsage eku; size_t i; ret = _hx509_cert_get_eku(context, cert, &eku); if (ret) return ret; for (i = 0; i < eku.len; i++) { ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]); if (ret) { free_ExtKeyUsage(&eku); return ret; } } free_ExtKeyUsage(&eku); } return 0; } /** * Make the to-be-signed certificate object a CA certificate. If the * pathLenConstraint is negative path length constraint is used. * * @param context A hx509 context. * @param tbs object to be signed. * @param pathLenConstraint path length constraint, negative, no * constraint. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_ca(hx509_context context, hx509_ca_tbs tbs, int pathLenConstraint) { tbs->flags.ca = 1; tbs->pathLenConstraint = pathLenConstraint; return 0; } /** * Make the to-be-signed certificate object a proxy certificate. If the * pathLenConstraint is negative path length constraint is used. * * @param context A hx509 context. * @param tbs object to be signed. * @param pathLenConstraint path length constraint, negative, no * constraint. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_proxy(hx509_context context, hx509_ca_tbs tbs, int pathLenConstraint) { tbs->flags.proxy = 1; tbs->pathLenConstraint = pathLenConstraint; return 0; } /** * Make the to-be-signed certificate object a windows domain controller certificate. * * @param context A hx509 context. * @param tbs object to be signed. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_domaincontroller(hx509_context context, hx509_ca_tbs tbs) { tbs->flags.domaincontroller = 1; return 0; } /** * Set the subject public key info (SPKI) in the to-be-signed certificate * object. SPKI is the public key and key related parameters in the * certificate. * * @param context A hx509 context. * @param tbs object to be signed. * @param spki subject public key info to use for the to-be-signed certificate object. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_spki(hx509_context context, hx509_ca_tbs tbs, const SubjectPublicKeyInfo *spki) { int ret; free_SubjectPublicKeyInfo(&tbs->spki); ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki); tbs->flags.key = !ret; return ret; } /** * Set the serial number to use for to-be-signed certificate object. * * @param context A hx509 context. * @param tbs object to be signed. * @param serialNumber serial number to use for the to-be-signed * certificate object. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_serialnumber(hx509_context context, hx509_ca_tbs tbs, const heim_integer *serialNumber) { int ret; der_free_heim_integer(&tbs->serial); ret = der_copy_heim_integer(serialNumber, &tbs->serial); tbs->flags.serial = !ret; return ret; } /** * An an extended key usage to the to-be-signed certificate object. * Duplicates will detected and not added. * * @param context A hx509 context. * @param tbs object to be signed. * @param oid extended key usage to add. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_eku(hx509_context context, hx509_ca_tbs tbs, const heim_oid *oid) { void *ptr; int ret; unsigned i; /* search for duplicates */ for (i = 0; i < tbs->eku.len; i++) { if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0) return 0; } ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1)); if (ptr == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } tbs->eku.val = ptr; ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } tbs->eku.len += 1; return 0; } /** * Add CRL distribution point URI to the to-be-signed certificate * object. * * @param context A hx509 context. * @param tbs object to be signed. * @param uri uri to the CRL. * @param issuername name of the issuer. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_crl_dp_uri(hx509_context context, hx509_ca_tbs tbs, const char *uri, hx509_name issuername) { DistributionPoint dp; int ret; memset(&dp, 0, sizeof(dp)); dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint)); { DistributionPointName name; GeneralName gn; size_t size; name.element = choice_DistributionPointName_fullName; name.u.fullName.len = 1; name.u.fullName.val = &gn; gn.element = choice_GeneralName_uniformResourceIdentifier; gn.u.uniformResourceIdentifier.data = rk_UNCONST(uri); gn.u.uniformResourceIdentifier.length = strlen(uri); ASN1_MALLOC_ENCODE(DistributionPointName, dp.distributionPoint->data, dp.distributionPoint->length, &name, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to encoded DistributionPointName"); goto out; } if (dp.distributionPoint->length != size) _hx509_abort("internal ASN.1 encoder error"); } if (issuername) { #if 1 /** * issuername not supported */ hx509_set_error_string(context, 0, EINVAL, "CRLDistributionPoints.name.issuername not yet supported"); return EINVAL; #else GeneralNames *crlissuer; GeneralName gn; Name n; crlissuer = calloc(1, sizeof(*crlissuer)); if (crlissuer == NULL) { return ENOMEM; } memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_directoryName; ret = hx509_name_to_Name(issuername, &n); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } gn.u.directoryName.element = n.element; gn.u.directoryName.u.rdnSequence = n.u.rdnSequence; ret = add_GeneralNames(&crlissuer, &gn); free_Name(&n); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } dp.cRLIssuer = &crlissuer; #endif } ret = add_CRLDistributionPoints(&tbs->crldp, &dp); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } out: free_DistributionPoint(&dp); return ret; } /** * Add Subject Alternative Name otherName to the to-be-signed * certificate object. * * @param context A hx509 context. * @param tbs object to be signed. * @param oid the oid of the OtherName. * @param os data in the other name. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_otherName(hx509_context context, hx509_ca_tbs tbs, const heim_oid *oid, const heim_octet_string *os) { GeneralName gn; memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_otherName; gn.u.otherName.type_id = *oid; gn.u.otherName.value = *os; return add_GeneralNames(&tbs->san, &gn); } /** * Add Kerberos Subject Alternative Name to the to-be-signed * certificate object. The principal string is a UTF8 string. * * @param context A hx509 context. * @param tbs object to be signed. * @param principal Kerberos principal to add to the certificate. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_pkinit(hx509_context context, hx509_ca_tbs tbs, const char *principal) { heim_octet_string os; KRB5PrincipalName p; size_t size; int ret; char *s = NULL; memset(&p, 0, sizeof(p)); /* parse principal */ { const char *str; char *q; int n; /* count number of component */ n = 1; for(str = principal; *str != '\0' && *str != '@'; str++){ if(*str=='\\'){ if(str[1] == '\0' || str[1] == '@') { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "trailing \\ in principal name"); goto out; } str++; } else if(*str == '/') n++; } p.principalName.name_string.val = calloc(n, sizeof(*p.principalName.name_string.val)); if (p.principalName.name_string.val == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc: out of memory"); goto out; } p.principalName.name_string.len = n; p.principalName.name_type = KRB5_NT_PRINCIPAL; q = s = strdup(principal); if (q == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc: out of memory"); goto out; } p.realm = strrchr(q, '@'); if (p.realm == NULL) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "Missing @ in principal"); goto out; }; *p.realm++ = '\0'; n = 0; while (q) { p.principalName.name_string.val[n++] = q; q = strchr(q, '/'); if (q) *q++ = '\0'; } } ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != os.length) _hx509_abort("internal ASN.1 encoder error"); ret = hx509_ca_tbs_add_san_otherName(context, tbs, &asn1_oid_id_pkinit_san, &os); free(os.data); out: if (p.principalName.name_string.val) free (p.principalName.name_string.val); if (s) free(s); return ret; } /* * */ static int add_utf8_san(hx509_context context, hx509_ca_tbs tbs, const heim_oid *oid, const char *string) { const PKIXXmppAddr ustring = (const PKIXXmppAddr)(intptr_t)string; heim_octet_string os; size_t size; int ret; os.length = 0; os.data = NULL; ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != os.length) _hx509_abort("internal ASN.1 encoder error"); ret = hx509_ca_tbs_add_san_otherName(context, tbs, oid, &os); free(os.data); out: return ret; } /** * Add Microsoft UPN Subject Alternative Name to the to-be-signed * certificate object. The principal string is a UTF8 string. * * @param context A hx509 context. * @param tbs object to be signed. * @param principal Microsoft UPN string. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_ms_upn(hx509_context context, hx509_ca_tbs tbs, const char *principal) { return add_utf8_san(context, tbs, &asn1_oid_id_pkinit_ms_san, principal); } /** * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed * certificate object. The jid is an UTF8 string. * * @param context A hx509 context. * @param tbs object to be signed. * @param jid string of an a jabber id in UTF8. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_jid(hx509_context context, hx509_ca_tbs tbs, const char *jid) { return add_utf8_san(context, tbs, &asn1_oid_id_pkix_on_xmppAddr, jid); } /** * Add a Subject Alternative Name hostname to to-be-signed certificate * object. A domain match starts with ., an exact match does not. * * Example of a an domain match: .domain.se matches the hostname * host.domain.se. * * @param context A hx509 context. * @param tbs object to be signed. * @param dnsname a hostame. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_hostname(hx509_context context, hx509_ca_tbs tbs, const char *dnsname) { GeneralName gn; memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_dNSName; gn.u.dNSName.data = rk_UNCONST(dnsname); gn.u.dNSName.length = strlen(dnsname); return add_GeneralNames(&tbs->san, &gn); } /** * Add a Subject Alternative Name rfc822 (email address) to * to-be-signed certificate object. * * @param context A hx509 context. * @param tbs object to be signed. * @param rfc822Name a string to a email address. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_add_san_rfc822name(hx509_context context, hx509_ca_tbs tbs, const char *rfc822Name) { GeneralName gn; memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_rfc822Name; gn.u.rfc822Name.data = rk_UNCONST(rfc822Name); gn.u.rfc822Name.length = strlen(rfc822Name); return add_GeneralNames(&tbs->san, &gn); } /** * Set the subject name of a to-be-signed certificate object. * * @param context A hx509 context. * @param tbs object to be signed. * @param subject the name to set a subject. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_subject(hx509_context context, hx509_ca_tbs tbs, hx509_name subject) { if (tbs->subject) hx509_name_free(&tbs->subject); return hx509_name_copy(context, subject, &tbs->subject); } /** * Set the issuerUniqueID and subjectUniqueID * * These are only supposed to be used considered with version 2 * certificates, replaced by the two extensions SubjectKeyIdentifier * and IssuerKeyIdentifier. This function is to allow application * using legacy protocol to issue them. * * @param context A hx509 context. * @param tbs object to be signed. * @param issuerUniqueID to be set * @param subjectUniqueID to be set * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_set_unique(hx509_context context, hx509_ca_tbs tbs, const heim_bit_string *subjectUniqueID, const heim_bit_string *issuerUniqueID) { int ret; der_free_bit_string(&tbs->subjectUniqueID); der_free_bit_string(&tbs->issuerUniqueID); if (subjectUniqueID) { ret = der_copy_bit_string(subjectUniqueID, &tbs->subjectUniqueID); if (ret) return ret; } if (issuerUniqueID) { ret = der_copy_bit_string(issuerUniqueID, &tbs->issuerUniqueID); if (ret) return ret; } return 0; } /** * Expand the the subject name in the to-be-signed certificate object * using hx509_name_expand(). * * @param context A hx509 context. * @param tbs object to be signed. * @param env enviroment variable to expand variables in the subject * name, see hx509_env_init(). * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_tbs_subject_expand(hx509_context context, hx509_ca_tbs tbs, hx509_env env) { return hx509_name_expand(context, tbs->subject, env); } /* * */ static int add_extension(hx509_context context, TBSCertificate *tbsc, int critical_flag, const heim_oid *oid, const heim_octet_string *data) { Extension ext; int ret; memset(&ext, 0, sizeof(ext)); if (critical_flag) { ext.critical = malloc(sizeof(*ext.critical)); if (ext.critical == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *ext.critical = TRUE; } ret = der_copy_oid(oid, &ext.extnID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_octet_string(data, &ext.extnValue); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = add_Extensions(tbsc->extensions, &ext); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } out: free_Extension(&ext); return ret; } static int build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject) { char *tstr; time_t t; int ret; ret = copy_Name(issuer, subject); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy subject name"); return ret; } t = time(NULL); asprintf(&tstr, "ts-%lu", (unsigned long)t); if (tstr == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Failed to copy subject name"); return ENOMEM; } /* prefix with CN=,...*/ ret = _hx509_name_modify(context, subject, 1, &asn1_oid_id_at_commonName, tstr); free(tstr); if (ret) free_Name(subject); return ret; } static int ca_sign(hx509_context context, hx509_ca_tbs tbs, hx509_private_key signer, const AuthorityKeyIdentifier *ai, const Name *issuername, hx509_cert *certificate) { heim_octet_string data; Certificate c; TBSCertificate *tbsc; size_t size; int ret; const AlgorithmIdentifier *sigalg; time_t notBefore; time_t notAfter; unsigned key_usage; sigalg = _hx509_crypto_default_sig_alg; memset(&c, 0, sizeof(c)); /* * Default values are: Valid since 24h ago, valid one year into * the future, KeyUsage digitalSignature and keyEncipherment set, * and keyCertSign for CA certificates. */ notBefore = tbs->notBefore; if (notBefore == 0) notBefore = time(NULL) - 3600 * 24; notAfter = tbs->notAfter; if (notAfter == 0) notAfter = time(NULL) + 3600 * 24 * 365; key_usage = tbs->key_usage; if (key_usage == 0) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.digitalSignature = 1; ku.keyEncipherment = 1; key_usage = KeyUsage2int(ku); } if (tbs->flags.ca) { KeyUsage ku; memset(&ku, 0, sizeof(ku)); ku.keyCertSign = 1; ku.cRLSign = 1; key_usage |= KeyUsage2int(ku); } /* * */ tbsc = &c.tbsCertificate; if (tbs->flags.key == 0) { ret = EINVAL; hx509_set_error_string(context, 0, ret, "No public key set"); return ret; } /* * Don't put restrictions on proxy certificate's subject name, it * will be generated below. */ if (!tbs->flags.proxy) { if (tbs->subject == NULL) { hx509_set_error_string(context, 0, EINVAL, "No subject name set"); return EINVAL; } if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) { hx509_set_error_string(context, 0, EINVAL, "NULL subject and no SubjectAltNames"); return EINVAL; } } if (tbs->flags.ca && tbs->flags.proxy) { hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA " "at the same time"); return EINVAL; } if (tbs->flags.proxy) { if (tbs->san.len > 0) { hx509_set_error_string(context, 0, EINVAL, "Proxy certificate is not allowed " "to have SubjectAltNames"); return EINVAL; } } /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */ tbsc->version = calloc(1, sizeof(*tbsc->version)); if (tbsc->version == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *tbsc->version = rfc3280_version_3; /* serialNumber CertificateSerialNumber, */ if (tbs->flags.serial) { ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } else { tbsc->serialNumber.length = 20; tbsc->serialNumber.data = malloc(tbsc->serialNumber.length); if (tbsc->serialNumber.data == NULL){ ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* XXX diffrent */ RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length); ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f; } /* signature AlgorithmIdentifier, */ ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg"); goto out; } /* issuer Name, */ if (issuername) ret = copy_Name(issuername, &tbsc->issuer); else ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy issuer name"); goto out; } /* validity Validity, */ tbsc->validity.notBefore.element = choice_Time_generalTime; tbsc->validity.notBefore.u.generalTime = notBefore; tbsc->validity.notAfter.element = choice_Time_generalTime; tbsc->validity.notAfter.u.generalTime = notAfter; /* subject Name, */ if (tbs->flags.proxy) { ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject); if (ret) goto out; } else { ret = hx509_name_to_Name(tbs->subject, &tbsc->subject); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy subject name"); goto out; } } /* subjectPublicKeyInfo SubjectPublicKeyInfo, */ ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to copy spki"); goto out; } /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */ if (tbs->issuerUniqueID.length) { tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID)); if (tbsc->issuerUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */ if (tbs->subjectUniqueID.length) { tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID)); if (tbsc->subjectUniqueID == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } /* extensions [3] EXPLICIT Extensions OPTIONAL */ tbsc->extensions = calloc(1, sizeof(*tbsc->extensions)); if (tbsc->extensions == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* Add the text BMP string Domaincontroller to the cert */ if (tbs->flags.domaincontroller) { data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d" "\x00\x61\x00\x69\x00\x6e\x00\x43" "\x00\x6f\x00\x6e\x00\x74\x00\x72" "\x00\x6f\x00\x6c\x00\x6c\x00\x65" "\x00\x72"); data.length = 34; ret = add_extension(context, tbsc, 0, &asn1_oid_id_ms_cert_enroll_domaincontroller, &data); if (ret) goto out; } /* add KeyUsage */ { KeyUsage ku; ku = int2KeyUsage(key_usage); ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 1, &asn1_oid_id_x509_ce_keyUsage, &data); free(data.data); if (ret) goto out; } /* add ExtendedKeyUsage */ if (tbs->eku.len > 0) { ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, &tbs->eku, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_extKeyUsage, &data); free(data.data); if (ret) goto out; } /* add Subject Alternative Name */ if (tbs->san.len > 0) { ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, &tbs->san, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectAltName, &data); free(data.data); if (ret) goto out; } /* Add Authority Key Identifier */ if (ai) { ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, ai, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_authorityKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add Subject Key Identifier */ { SubjectKeyIdentifier si; unsigned char hash[SHA_DIGEST_LENGTH]; { EVP_MD_CTX *ctx; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data, tbs->spki.subjectPublicKey.length / 8); EVP_DigestFinal_ex(ctx, hash, NULL); EVP_MD_CTX_destroy(ctx); } si.data = hash; si.length = sizeof(hash); ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, &si, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_x509_ce_subjectKeyIdentifier, &data); free(data.data); if (ret) goto out; } /* Add BasicConstraints */ { BasicConstraints bc; int aCA = 1; unsigned int path; memset(&bc, 0, sizeof(bc)); if (tbs->flags.ca) { bc.cA = &aCA; if (tbs->pathLenConstraint >= 0) { path = tbs->pathLenConstraint; bc.pathLenConstraint = &path; } } ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, &bc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); /* Critical if this is a CA */ ret = add_extension(context, tbsc, tbs->flags.ca, &asn1_oid_id_x509_ce_basicConstraints, &data); free(data.data); if (ret) goto out; } /* add Proxy */ if (tbs->flags.proxy) { ProxyCertInfo info; memset(&info, 0, sizeof(info)); if (tbs->pathLenConstraint >= 0) { info.pCPathLenConstraint = malloc(sizeof(*info.pCPathLenConstraint)); if (info.pCPathLenConstraint == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } *info.pCPathLenConstraint = tbs->pathLenConstraint; } ret = der_copy_oid(&asn1_oid_id_pkix_ppl_inheritAll, &info.proxyPolicy.policyLanguage); if (ret) { free_ProxyCertInfo(&info); hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, &info, &size, ret); free_ProxyCertInfo(&info); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, 0, &asn1_oid_id_pkix_pe_proxyCertInfo, &data); free(data.data); if (ret) goto out; } if (tbs->crldp.len) { ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length, &tbs->crldp, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } if (size != data.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_extension(context, tbsc, FALSE, &asn1_oid_id_x509_ce_cRLDistributionPoints, &data); free(data.data); if (ret) goto out; } ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "malloc out of memory"); goto out; } if (data.length != size) _hx509_abort("internal ASN.1 encoder error"); ret = _hx509_create_signature_bitstring(context, signer, sigalg, &data, &c.signatureAlgorithm, &c.signatureValue); free(data.data); if (ret) goto out; ret = hx509_cert_init(context, &c, certificate); if (ret) goto out; free_Certificate(&c); return 0; out: free_Certificate(&c); return ret; } static int get_AuthorityKeyIdentifier(hx509_context context, const Certificate *certificate, AuthorityKeyIdentifier *ai) { SubjectKeyIdentifier si; int ret; ret = _hx509_find_extension_subject_key_id(certificate, &si); if (ret == 0) { ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier)); if (ai->keyIdentifier == NULL) { free_SubjectKeyIdentifier(&si); ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ret = der_copy_octet_string(&si, ai->keyIdentifier); free_SubjectKeyIdentifier(&si); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } else { GeneralNames gns; GeneralName gn; Name name; memset(&gn, 0, sizeof(gn)); memset(&gns, 0, sizeof(gns)); memset(&name, 0, sizeof(name)); ai->authorityCertIssuer = calloc(1, sizeof(*ai->authorityCertIssuer)); if (ai->authorityCertIssuer == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ai->authorityCertSerialNumber = calloc(1, sizeof(*ai->authorityCertSerialNumber)); if (ai->authorityCertSerialNumber == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } /* * XXX unbreak when asn1 compiler handle IMPLICIT * * This is so horrible. */ ret = copy_Name(&certificate->tbsCertificate.subject, &name); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } memset(&gn, 0, sizeof(gn)); gn.element = choice_GeneralName_directoryName; gn.u.directoryName.element = choice_GeneralName_directoryName_rdnSequence; gn.u.directoryName.u.rdnSequence = name.u.rdnSequence; ret = add_GeneralNames(&gns, &gn); if (ret) { hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } ai->authorityCertIssuer->val = gns.val; ai->authorityCertIssuer->len = gns.len; ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber, ai->authorityCertSerialNumber); if (ai->authorityCertSerialNumber == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "Out of memory"); goto out; } } out: if (ret) free_AuthorityKeyIdentifier(ai); return ret; } /** * Sign a to-be-signed certificate object with a issuer certificate. * * The caller needs to at least have called the following functions on the * to-be-signed certificate object: * - hx509_ca_tbs_init() * - hx509_ca_tbs_set_subject() * - hx509_ca_tbs_set_spki() * * When done the to-be-signed certificate object should be freed with * hx509_ca_tbs_free(). * * When creating self-signed certificate use hx509_ca_sign_self() instead. * * @param context A hx509 context. * @param tbs object to be signed. * @param signer the CA certificate object to sign with (need private key). * @param certificate return cerificate, free with hx509_cert_free(). * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_sign(hx509_context context, hx509_ca_tbs tbs, hx509_cert signer, hx509_cert *certificate) { const Certificate *signer_cert; AuthorityKeyIdentifier ai; int ret; memset(&ai, 0, sizeof(ai)); signer_cert = _hx509_get_cert(signer); ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai); if (ret) goto out; ret = ca_sign(context, tbs, _hx509_cert_private_key(signer), &ai, &signer_cert->tbsCertificate.subject, certificate); out: free_AuthorityKeyIdentifier(&ai); return ret; } /** * Work just like hx509_ca_sign() but signs it-self. * * @param context A hx509 context. * @param tbs object to be signed. * @param signer private key to sign with. * @param certificate return cerificate, free with hx509_cert_free(). * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_ca */ int hx509_ca_sign_self(hx509_context context, hx509_ca_tbs tbs, hx509_private_key signer, hx509_cert *certificate) { return ca_sign(context, tbs, signer, NULL, NULL, certificate); }