int hx509_validate_cert(hx509_context context, hx509_validate_ctx ctx, hx509_cert cert) { Certificate *c = _hx509_get_cert(cert); TBSCertificate *t = &c->tbsCertificate; hx509_name issuer, subject; char *str; struct cert_status status; int ret; memset(&status, 0, sizeof(status)); if (_hx509_cert_get_version(c) != 3) validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Not version 3 certificate\n"); if ((t->version == NULL || *t->version < 2) && t->extensions) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Not version 3 certificate with extensions\n"); if (_hx509_cert_get_version(c) >= 3 && t->extensions == NULL) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Version 3 certificate without extensions\n"); ret = hx509_cert_get_subject(cert, &subject); if (ret) abort(); hx509_name_to_string(subject, &str); validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "subject name: %s\n", str); free(str); ret = hx509_cert_get_issuer(cert, &issuer); if (ret) abort(); hx509_name_to_string(issuer, &str); validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "issuer name: %s\n", str); free(str); if (hx509_name_cmp(subject, issuer) == 0) { status.selfsigned = 1; validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tis a self-signed certificate\n"); } validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Validity:\n"); Time2string(&t->validity.notBefore, &str); validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotBefore %s\n", str); free(str); Time2string(&t->validity.notAfter, &str); validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotAfter %s\n", str); free(str); if (t->extensions) { int i, j; if (t->extensions->len == 0) { validate_print(ctx, HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE, "The empty extensions list is not " "allowed by PKIX\n"); } for (i = 0; i < t->extensions->len; i++) { for (j = 0; check_extension[j].name; j++) if (der_heim_oid_cmp(check_extension[j].oid, &t->extensions->val[i].extnID) == 0) break; if (check_extension[j].name == NULL) { int flags = HX509_VALIDATE_F_VERBOSE; if (t->extensions->val[i].critical) flags |= HX509_VALIDATE_F_VALIDATE; validate_print(ctx, flags, "don't know what "); if (t->extensions->val[i].critical) validate_print(ctx, flags, "and is CRITICAL "); if (ctx->flags & flags) hx509_oid_print(&t->extensions->val[i].extnID, validate_vprint, ctx); validate_print(ctx, flags, " is\n"); continue; } validate_print(ctx, HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE, "checking extention: %s\n", check_extension[j].name); (*check_extension[j].func)(ctx, &status, check_extension[j].cf, &t->extensions->val[i]); } } else validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extentions\n"); if (status.isca) { if (!status.haveSKI) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "CA certificate have no SubjectKeyIdentifier\n"); } else { if (!status.haveAKI) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Is not CA and doesn't have " "AuthorityKeyIdentifier\n"); } if (!status.haveSKI) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Doesn't have SubjectKeyIdentifier\n"); if (status.isproxy && status.isca) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Proxy and CA at the same time!\n"); if (status.isproxy) { if (status.haveSAN) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Proxy and have SAN\n"); if (status.haveIAN) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Proxy and have IAN\n"); } if (hx509_name_is_null_p(subject) && !status.haveSAN) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "NULL subject DN and doesn't have a SAN\n"); if (!status.selfsigned && !status.haveCRLDP) validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Not a CA nor PROXY and doesn't have" "CRL Dist Point\n"); if (status.selfsigned) { ret = _hx509_verify_signature_bitstring(context, cert, &c->signatureAlgorithm, &c->tbsCertificate._save, &c->signatureValue); if (ret == 0) validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Self-signed certificate was self-signed\n"); else validate_print(ctx, HX509_VALIDATE_F_VALIDATE, "Self-signed certificate NOT really self-signed!\n"); } hx509_name_free(&subject); hx509_name_free(&issuer); return 0; }
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 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 "