/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define CERTFILE_TEMPNAME "/tmp/user.certXXXXXX" #define CRLFILE_TEMPNAME "/tmp/crlXXXXXX" #define X509_FORMAT_VERSION 2 static KMF_RETURN sign_cert(KMF_HANDLE_T, const KMF_DATA *, KMF_KEY_HANDLE *, KMF_OID *, KMF_DATA *); static KMF_RETURN verify_cert_with_key(KMF_HANDLE_T, KMF_DATA *, const KMF_DATA *); static KMF_RETURN verify_cert_with_cert(KMF_HANDLE_T, const KMF_DATA *, const KMF_DATA *); static KMF_RETURN get_keyalg_from_cert(KMF_DATA *cert, KMF_KEY_ALG *keyalg) { KMF_RETURN rv; KMF_X509_CERTIFICATE *SignerCert = NULL; KMF_ALGORITHM_INDEX AlgorithmId; rv = DerDecodeSignedCertificate(cert, &SignerCert); if (rv != KMF_OK) return (rv); /* Get the algorithm info from the signer certificate */ AlgorithmId = x509_algoid_to_algid( &SignerCert->signature.algorithmIdentifier.algorithm); switch (AlgorithmId) { case KMF_ALGID_MD5WithRSA: case KMF_ALGID_SHA1WithRSA: case KMF_ALGID_SHA256WithRSA: case KMF_ALGID_SHA384WithRSA: case KMF_ALGID_SHA512WithRSA: *keyalg = KMF_RSA; break; case KMF_ALGID_SHA1WithDSA: case KMF_ALGID_SHA256WithDSA: *keyalg = KMF_DSA; break; case KMF_ALGID_SHA1WithECDSA: case KMF_ALGID_SHA256WithECDSA: case KMF_ALGID_SHA384WithECDSA: case KMF_ALGID_SHA512WithECDSA: case KMF_ALGID_ECDSA: *keyalg = KMF_ECDSA; break; default: rv = KMF_ERR_BAD_ALGORITHM; } kmf_free_signed_cert(SignerCert); free(SignerCert); return (rv); } /* * Name: kmf_find_prikey_by_cert * * Description: * This function finds the corresponding private key in keystore * for a certificate */ KMF_RETURN kmf_find_prikey_by_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_KEY_ALG keyalg; KMF_KEY_HANDLE *key = NULL; KMF_DATA *cert = NULL; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_KEY_HANDLE_ATTR, TRUE, sizeof (KMF_KEY_HANDLE), sizeof (KMF_KEY_HANDLE)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); /* * First, get the key algorithm info from the certificate and saves it * in the returned key handle. */ cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); if (cert == NULL) return (KMF_ERR_BAD_PARAMETER); ret = get_keyalg_from_cert(cert, &keyalg); if (ret != KMF_OK) return (ret); key = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); if (key == NULL) return (KMF_ERR_BAD_PARAMETER); key->keyalg = keyalg; /* Call the plugin to do the work. */ ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->FindPrikeyByCert == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->FindPrikeyByCert(handle, numattr, attrlist)); } KMF_RETURN check_key_usage(void *handle, const KMF_DATA *cert, const KMF_KU_PURPOSE purpose) { KMF_X509EXT_BASICCONSTRAINTS constraint; KMF_BOOL critical = B_FALSE; KMF_X509EXT_KEY_USAGE keyusage; KMF_RETURN ret = KMF_OK; if (handle == NULL || cert == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&constraint, 0, sizeof (KMF_X509EXT_BASICCONSTRAINTS)); (void) memset(&keyusage, 0, sizeof (KMF_X509EXT_KEY_USAGE)); ret = kmf_get_cert_ku(cert, &keyusage); if (ret != KMF_OK) /* * If absent or error, the cert is assumed to be invalid * for all key usage checking. */ return (ret); switch (purpose) { case KMF_KU_SIGN_CERT: /* * RFC 3280: * The keyCertSign bit is asserted when the subject * public key is used for verifying a signature on * public key certificates. If the keyCertSign bit * is asserted, then the cA bit in the basic constraints * extension (section 4.2.1.10) MUST also be asserted. * The basic constraints extension MUST appear as a * critical extension in all CA certificates that * contain public keys used to validate digital * signatures on certificates. */ if (keyusage.KeyUsageBits & KMF_keyCertSign) { ret = kmf_get_cert_basic_constraint(cert, &critical, &constraint); if (ret != KMF_OK) return (ret); if ((!critical) || (!constraint.cA)) return (KMF_ERR_KEYUSAGE); } else { return (KMF_ERR_KEYUSAGE); } break; case KMF_KU_SIGN_DATA: /* * RFC 3280: * The digitalSignature bit is asserted when the subject * public key is used with a digital signature mechanism * to support security services other than certificate * signing(bit 5), or CRL signing(bit 6). */ if (!(keyusage.KeyUsageBits & KMF_digitalSignature)) return (KMF_ERR_KEYUSAGE); break; case KMF_KU_ENCRYPT_DATA: /* * RFC 3280: * The dataEncipherment bit is asserted when the subject * public key is used for enciphering user data, other than * cryptographic keys. */ if (!(keyusage.KeyUsageBits & KMF_dataEncipherment)) return (KMF_ERR_KEYUSAGE); break; default: return (KMF_ERR_BAD_PARAMETER); } return (KMF_OK); } KMF_RETURN kmf_find_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_COUNT_ATTR, FALSE, sizeof (uint32_t), sizeof (uint32_t)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->FindCert == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->FindCert(handle, numattr, attrlist)); } #define NODATA(d) (d.Data == NULL || d.Length == 0) KMF_RETURN kmf_encode_cert_record(KMF_X509_CERTIFICATE *CertData, KMF_DATA *encodedCert) { KMF_RETURN ret; KMF_X509_TBS_CERT *tbs_cert; if (CertData == NULL || encodedCert == NULL) return (KMF_ERR_BAD_PARAMETER); /* * Validate that all required fields are present. */ tbs_cert = &(CertData->certificate); if (NODATA(tbs_cert->version) || NODATA(tbs_cert->signature.algorithm) || NODATA(tbs_cert->subjectPublicKeyInfo.subjectPublicKey) || tbs_cert->serialNumber.val == NULL || tbs_cert->serialNumber.len == 0 || tbs_cert->subject.numberOfRDNs == 0 || tbs_cert->issuer.numberOfRDNs == 0) { return (KMF_ERR_INCOMPLETE_TBS_CERT); } encodedCert->Length = 0; encodedCert->Data = NULL; /* Pack the new certificate */ ret = DerEncodeSignedCertificate(CertData, encodedCert); return (ret); } /* * This function is used to setup the attribute list before calling * kmf_find_prikey_by_cert(). This function is used by * kmf_decrypt_with_cert * kmf_sign_cert * kmf_sign_data * * The attribute list in these callers contain all the attributes * needed by kmf_find_prikey_by_cert(), except the * KMF_KEY_HANDLE attribute and the KMF_CERT_DATA_ATTR attribute. * These 2 attributes need to be added or reset. * * The caller should free the new_attrlist after use it. */ static KMF_RETURN setup_findprikey_attrlist(KMF_ATTRIBUTE *src_attrlist, int src_num, KMF_ATTRIBUTE **new_attrlist, int *new_num, KMF_KEY_HANDLE *key, KMF_DATA *cert) { KMF_ATTRIBUTE *attrlist = NULL; int cur_num = src_num; int index; int i; if (src_attrlist == NULL || new_num == NULL || key == NULL || cert == NULL) return (KMF_ERR_BAD_PARAMETER); /* Create a new attribute list with 2 more elements */ attrlist = (KMF_ATTRIBUTE *) malloc( (src_num + 2) * sizeof (KMF_ATTRIBUTE)); if (attrlist == NULL) return (KMF_ERR_MEMORY); /* Copy the src_attrlist to the new list */ for (i = 0; i < src_num; i++) { attrlist[i].type = src_attrlist[i].type; attrlist[i].pValue = src_attrlist[i].pValue; attrlist[i].valueLen = src_attrlist[i].valueLen; } /* Add or reset the key handle attribute */ index = kmf_find_attr(KMF_KEY_HANDLE_ATTR, attrlist, cur_num); if (index == -1) { /* not found; add it */ kmf_set_attr_at_index(attrlist, cur_num, KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE)); cur_num++; } else { /* found; just reset it */ kmf_set_attr_at_index(attrlist, index, KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE)); } /* add or reset the cert data attribute */ index = kmf_find_attr(KMF_CERT_DATA_ATTR, attrlist, cur_num); if (index == -1) { /* not found; add it */ kmf_set_attr_at_index(attrlist, cur_num, KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA)); cur_num++; } else { /* found; just reset it */ kmf_set_attr_at_index(attrlist, index, KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA)); } *new_attrlist = attrlist; *new_num = cur_num; return (KMF_OK); } /* * Determine a default signature type to use based on * the key algorithm. */ static KMF_OID * get_default_signoid(KMF_KEY_HANDLE *key) { KMF_OID *oid; switch (key->keyalg) { case KMF_RSA: oid = (KMF_OID *)&KMFOID_SHA256WithRSA; break; case KMF_DSA: /* NSS doesnt support DSA-SHA2 hashes yet */ if (key->kstype == KMF_KEYSTORE_NSS) oid = (KMF_OID *)&KMFOID_X9CM_DSAWithSHA1; else oid = (KMF_OID *)&KMFOID_SHA256WithDSA; break; case KMF_ECDSA: oid = (KMF_OID *)&KMFOID_SHA256WithECDSA; break; default: oid = NULL; break; } return (oid); } /* * This is to check to see if a certificate being signed has * the keyCertSign KeyUsage bit set, and if so, make sure the * "BasicConstraints" extension is also set accordingly. */ static KMF_RETURN check_for_basic_constraint(KMF_DATA *cert) { KMF_RETURN rv = KMF_OK; KMF_X509EXT_KEY_USAGE keyUsage; KMF_X509_CERTIFICATE *x509cert = NULL; rv = kmf_get_cert_ku((const KMF_DATA *)cert, &keyUsage); if (rv == KMF_OK) { KMF_X509EXT_BASICCONSTRAINTS basicConstraint; KMF_BOOL critical; /* If keyCertSign is set, look for basicConstraints */ if (keyUsage.KeyUsageBits & KMF_keyCertSign) rv = kmf_get_cert_basic_constraint( (const KMF_DATA *)cert, &critical, &basicConstraint); /* * If we got KMF_OK (or an error), then return * because the extension is already present. We * only want to continue with this function if * the extension is NOT found. */ if (rv != KMF_ERR_EXTENSION_NOT_FOUND) return (rv); /* * Don't limit the pathLen (for now). * This should probably be a policy setting in the * future. */ basicConstraint.cA = TRUE; basicConstraint.pathLenConstraintPresent = FALSE; /* * Decode the DER cert data into the internal * X.509 structure we need to set extensions. */ rv = DerDecodeSignedCertificate(cert, &x509cert); if (rv != KMF_OK) return (rv); /* * Add the missing basic constraint. */ rv = kmf_set_cert_basic_constraint(x509cert, TRUE, &basicConstraint); if (rv != KMF_OK) { kmf_free_signed_cert(x509cert); free(x509cert); return (rv); } /* Free the old cert data record */ kmf_free_data(cert); /* Re-encode the cert with the extension */ rv = kmf_encode_cert_record(x509cert, cert); /* cleanup */ kmf_free_signed_cert(x509cert); free(x509cert); } if (rv == KMF_ERR_EXTENSION_NOT_FOUND) rv = KMF_OK; return (rv); } /* * Name: kmf_sign_cert * * Description: * This function signs a certificate using the signer cert and * returns a signed and DER-encoded certificate. * * The following types of certificate data can be submitted to be signed: * KMF_TBS_CERT_DATA_ATTR - a KMF_DATA ptr is provided in the attrlist * and is signed directly. * KMF_X509_CERTIFICATE_ATTR - a KMF_X509_CERTIFICATE record is provided * in the attribute list. This is converted to raw KMF_DATA * prior to signing. * * The key for the signing operation can be provided as a KMF_KEY_HANDLE_ATTR * or the caller may choose to provide a KMF_SIGNER_CERT_ATTR (KMF_DATA *). * If the latter, this function will then attempt to find the private key * associated with the certificate. The private key must be stored in * the same keystore as the signer certificate. */ KMF_RETURN kmf_sign_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret; int new_numattr = numattr + 1; KMF_ATTRIBUTE *new_attrlist = NULL; KMF_DATA *signer_cert = NULL; KMF_DATA *tbs_cert = NULL; /* to be signed cert */ KMF_DATA *signed_cert = NULL; KMF_DATA unsignedCert = { 0, NULL }; KMF_KEY_HANDLE sign_key, *sign_key_ptr; int freethekey = 0; KMF_POLICY_RECORD *policy; KMF_OID *oid = NULL; KMF_X509_CERTIFICATE *x509cert; KMF_X509_TBS_CERT *decodedTbsCert = NULL; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); /* Get the signer cert and check its keyUsage */ signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist, numattr); sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); /* * Only accept 1 or the other, not both. */ if (signer_cert == NULL && sign_key_ptr == NULL) return (KMF_ERR_BAD_PARAMETER); if (signer_cert != NULL && sign_key_ptr != NULL) return (KMF_ERR_BAD_PARAMETER); oid = kmf_get_attr_ptr(KMF_OID_ATTR, attrlist, numattr); if (oid == NULL) { /* * If the signature OID was not given, check * for an algorithm index identifier instead. */ KMF_ALGORITHM_INDEX AlgId; ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, numattr, &AlgId, NULL); if (ret == KMF_OK) oid = x509_algid_to_algoid(AlgId); } if (signer_cert != NULL) { policy = handle->policy; ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_CERT); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* * Find the private key from the signer certificate by calling * kmf_find_prikey_by_cert(). */ ret = setup_findprikey_attrlist(attrlist, numattr, &new_attrlist, &new_numattr, &sign_key, signer_cert); if (ret != KMF_OK) goto out; ret = kmf_find_prikey_by_cert(handle, new_numattr, new_attrlist); if (ret != KMF_OK) { goto out; } sign_key_ptr = &sign_key; freethekey = 1; } tbs_cert = kmf_get_attr_ptr(KMF_TBS_CERT_DATA_ATTR, attrlist, numattr); if (tbs_cert == NULL) { x509cert = kmf_get_attr_ptr(KMF_X509_CERTIFICATE_ATTR, attrlist, numattr); if (x509cert == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto out; } ret = kmf_encode_cert_record(x509cert, &unsignedCert); if (ret != KMF_OK) goto out; tbs_cert = &unsignedCert; } /* * Check for the keyCertSign bit in the KeyUsage extn. If it is set, * then the basicConstraints must also be present and be * marked critical. */ ret = check_for_basic_constraint(tbs_cert); if (ret) goto out; if (oid == NULL) { /* * If OID is not known yet, use a default value * based on the signers key type. */ oid = get_default_signoid(sign_key_ptr); } signed_cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); if (signed_cert == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto out; } ret = sign_cert(handle, tbs_cert, sign_key_ptr, oid, signed_cert); out: if (new_attrlist) (void) free(new_attrlist); /* If we had to find the key, free it here. */ if (freethekey) kmf_free_kmf_key(handle, &sign_key); kmf_free_data(&unsignedCert); if (decodedTbsCert != NULL) { kmf_free_tbs_cert(decodedTbsCert); free(decodedTbsCert); } return (ret); } /* * Name: kmf_sign_data * * Description: * This function signs a block of data using the signer cert and * returns the the signature in output */ KMF_RETURN kmf_sign_data(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_ATTRIBUTE *new_attrlist = NULL; int new_numattr = numattr; KMF_DATA *signer_cert = NULL; KMF_DATA *tbs_data = NULL; /* to be signed data */ KMF_DATA *output = NULL; KMF_KEY_HANDLE sign_key, *sign_key_ptr; KMF_ALGORITHM_INDEX AlgId = KMF_ALGID_NONE; KMF_DATA signature = { 0, NULL }; KMF_OID *oid; KMF_POLICY_RECORD *policy; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_OUT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); /* Get the signer cert and check its keyUsage. */ signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist, numattr); sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); if (signer_cert == NULL && sign_key_ptr == NULL) return (KMF_ERR_BAD_PARAMETER); /* * If a signer cert was given, use it to find the private key * to use for signing the data. */ if (signer_cert != NULL) { ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA); /* * Signing generic data does not require the * KeyUsage extension. */ policy = handle->policy; if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* * Find the private key from the signer certificate. */ ret = setup_findprikey_attrlist(attrlist, numattr, &new_attrlist, &new_numattr, &sign_key, signer_cert); if (ret != KMF_OK) { goto cleanup; } ret = kmf_find_prikey_by_cert(handle, new_numattr, new_attrlist); if (ret != KMF_OK) { goto cleanup; } sign_key_ptr = &sign_key; } /* Get the tbs_data and signed_data attributes now */ tbs_data = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, numattr); if (tbs_data == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto cleanup; } output = kmf_get_attr_ptr(KMF_OUT_DATA_ATTR, attrlist, numattr); if (output == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto cleanup; } /* * Get the algorithm index attribute and its oid. If this attribute * is not provided, then we use a default value. */ oid = kmf_get_attr_ptr(KMF_OID_ATTR, attrlist, numattr); if (oid == NULL) { ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, numattr, &AlgId, NULL); /* If there was no Algorithm ID, use default based on key */ if (ret != KMF_OK) oid = get_default_signoid(sign_key_ptr); else oid = x509_algid_to_algoid(AlgId); } if (sign_key_ptr->keyp == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto cleanup; } /* Now call the plugin function to sign it */ plugin = FindPlugin(handle, sign_key_ptr->kstype); if (plugin == NULL || plugin->funclist->SignData == NULL) { ret = KMF_ERR_PLUGIN_NOTFOUND; goto cleanup; } ret = plugin->funclist->SignData(handle, sign_key_ptr, oid, tbs_data, output); if (ret != KMF_OK) goto cleanup; /* * For DSA, NSS returns an encoded signature. Decode the * signature and expect a 40-byte DSA signature. */ if (plugin->type == KMF_KEYSTORE_NSS && (IsEqualOid(oid, (KMF_OID *)&KMFOID_X9CM_DSAWithSHA1) || IsEqualOid(oid, (KMF_OID *)&KMFOID_SHA256WithDSA))) { ret = DerDecodeDSASignature(output, &signature); if (ret != KMF_OK) goto cleanup; output->Length = signature.Length; (void) memcpy(output->Data, signature.Data, signature.Length); } cleanup: if (new_attrlist != NULL) free(new_attrlist); if (signature.Data) free(signature.Data); if (signer_cert != NULL && sign_key_ptr != NULL) kmf_free_kmf_key(handle, sign_key_ptr); return (ret); } /* * kmf_verify_data * * This routine will try to verify a block of data using * either a public key or a certificate as the source * of the verification (the key). * * The caller may provider either a KMF_KEY_HANDLE_ATTR or * a KMF_SIGNER_CERT_DATA_ATTR (with a KMF_DATA record) to * use for the key to the verification step. If a certificate * is used and that certificate has the KeyUsage extension, * the SIGN-DATA bit must be set. Also, if a certificate * is used, the verification will be done in a specific * keystore mechanism. * * If a KMF_KEY_HANDLE is given in the attribute list, the * verification will occur in the framework itself using * PKCS#11 C_Verify functions. */ KMF_RETURN kmf_verify_data(KMF_HANDLE_T handle, int num_args, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret = KMF_OK; KMF_PLUGIN *plugin; KMF_KEYSTORE_TYPE kstype; uint32_t len; KMF_DATA derkey = { 0, NULL }; KMF_KEY_HANDLE *KMFKey; KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_NONE; KMF_DATA *indata; KMF_DATA *insig; KMF_DATA *signer_cert; KMF_X509_SPKI spki; KMF_POLICY_RECORD *policy; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_IN_SIGN_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, num_args, attrlist); if (ret != KMF_OK) return (ret); len = sizeof (kstype); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_args, &kstype, &len); if (ret != KMF_OK) return (ret); KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, num_args); signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist, num_args); if (KMFKey == NULL && signer_cert == NULL) { return (KMF_ERR_BAD_PARAMETER); } len = sizeof (sigAlg); ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, num_args, &sigAlg, &len); /* We only need the algorithm index if we don't have a signer cert. */ if (ret != KMF_OK && signer_cert == NULL) return (ret); indata = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, num_args); if (indata == NULL) return (KMF_ERR_BAD_PARAMETER); insig = kmf_get_attr_ptr(KMF_IN_SIGN_ATTR, attrlist, num_args); if (insig == NULL) return (KMF_ERR_BAD_PARAMETER); /* If the caller passed a signer cert instead of a key use it. */ if (signer_cert != NULL) { KMF_X509_CERTIFICATE *SignerCert = NULL; policy = handle->policy; ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* Decode the signer cert so we can get the SPKI data */ ret = DerDecodeSignedCertificate(signer_cert, &SignerCert); if (ret != KMF_OK) return (ret); /* If no algorithm specified, use the certs signature alg */ if (sigAlg == KMF_ALGID_NONE) sigAlg = x509_algoid_to_algid(CERT_ALG_OID(SignerCert)); if (sigAlg == KMF_ALGID_NONE) { kmf_free_signed_cert(SignerCert); free(SignerCert); return (KMF_ERR_BAD_ALGORITHM); } /* * Verify the data locally (i.e. using PKCS#11). * The verify operation uses a public key and does not * require access to a specific keystore. Save time * (and code) by just using the frameworks implementation * of the verify operation using crypto framework * APIs. */ ret = PKCS_VerifyData(handle, sigAlg, &SignerCert->certificate.subjectPublicKeyInfo, indata, insig); kmf_free_signed_cert(SignerCert); free(SignerCert); } else { /* Retrieve public key data from keystore */ plugin = FindPlugin(handle, kstype); if (plugin != NULL && plugin->funclist->EncodePubkeyData != NULL) { ret = plugin->funclist->EncodePubkeyData(handle, KMFKey, &derkey); } else { return (KMF_ERR_PLUGIN_NOTFOUND); } ret = DerDecodeSPKI(&derkey, &spki); if (ret == KMF_OK) ret = PKCS_VerifyData(handle, sigAlg, &spki, indata, insig); if (derkey.Data != NULL) free(derkey.Data); kmf_free_algoid(&spki.algorithm); kmf_free_data(&spki.subjectPublicKey); } return (ret); } /* * Name: kmf_verify_cert * * Description: * This function verifies that the a certificate was signed * using a specific private key and that the certificate has not * been altered since it was signed using that private key * The public key used for verification may be given in the * attribute list as a KMF_KEY_HANDLE or the caller may give * just the signing certificate (as KMF_SIGNER_CERT_DATA_ATTR) * from which the public key needed for verification can be * derived. * * Parameters: * handle(input) - opaque handle for KMF session * numattr - number of attributes in the list * attrlist - KMF_ATTRIBUTES * * Returns: * A KMF_RETURN value indicating success or specifying a particular * error condition. The value KMF_OK indicates success. All other * values represent an error condition. */ KMF_RETURN kmf_verify_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret; KMF_DATA derkey = { 0, NULL }; KMF_PLUGIN *plugin; KMF_KEY_HANDLE *KMFKey; KMF_DATA *CertToBeVerified; KMF_DATA *SignerCert; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); SignerCert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist, numattr); /* * Caller must provide at least a key handle or a cert to use * as the "key" for verification. */ if (KMFKey == NULL && SignerCert == NULL) return (KMF_ERR_BAD_PARAMETER); CertToBeVerified = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); if (CertToBeVerified == NULL) return (KMF_ERR_BAD_PARAMETER); if (SignerCert != NULL) { ret = verify_cert_with_cert(handle, CertToBeVerified, SignerCert); } else { /* * The keystore must extract the pubkey data because * the framework doesn't have access to the raw key bytes * that are needed to construct the DER encoded public * key information needed for the verify operation. */ plugin = FindPlugin(handle, KMFKey->kstype); if (plugin != NULL && plugin->funclist->EncodePubkeyData != NULL) { ret = plugin->funclist->EncodePubkeyData(handle, KMFKey, &derkey); } else { return (KMF_ERR_PLUGIN_NOTFOUND); } if (ret == KMF_OK && derkey.Length > 0) { ret = verify_cert_with_key(handle, &derkey, CertToBeVerified); if (derkey.Data != NULL) free(derkey.Data); } } return (ret); } /* * Name: kmf_encrypt * * Description: * Uses the public key from the cert to encrypt the plaintext * into the ciphertext. * * Parameters: * handle(input) - opaque handle for KMF session * cert(input) - pointer to a DER encoded certificate for encryption * by using its public key * plaintext(input) - pointer to the plaintext to be encrypted * ciphertext(output) - pointer to the ciphertext contains * encrypted data * * Returns: * A KMF_RETURN value indicating success or specifying a particular * error condition. * The value KMF_OK indicates success. All other values represent * an error condition. * */ KMF_RETURN kmf_encrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret; KMF_X509_CERTIFICATE *x509cert = NULL; KMF_X509_SPKI *pubkey; KMF_OID *alg; KMF_ALGORITHM_INDEX algid; KMF_DATA *cert; KMF_DATA *plaintext; KMF_DATA *ciphertext; KMF_POLICY_RECORD *policy; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist, numattr); ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist, numattr); if (cert == NULL || plaintext == NULL || ciphertext == NULL) return (KMF_ERR_BAD_PARAMETER); /* check the keyUsage of the certificate */ policy = handle->policy; ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* Decode the cert so we can get the SPKI data */ if ((ret = DerDecodeSignedCertificate(cert, &x509cert)) != KMF_OK) return (ret); /* Get the public key info from the certificate */ pubkey = &x509cert->certificate.subjectPublicKeyInfo; /* Use the algorithm in SPKI to encrypt data */ alg = &pubkey->algorithm.algorithm; algid = x509_algoid_to_algid(alg); /* [EC]DSA does not support encrypt */ if (algid == KMF_ALGID_DSA || algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA || algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA || algid == KMF_ALGID_NONE) { kmf_free_signed_cert(x509cert); free(x509cert); return (KMF_ERR_BAD_ALGORITHM); } /* * Encrypt using the crypto framework (not the KMF plugin mechanism). */ ret = PKCS_EncryptData(handle, algid, pubkey, plaintext, ciphertext); kmf_free_signed_cert(x509cert); free(x509cert); return (ret); } /* * Name: kmf_decrypt * * Description: * Uses the private key associated with the cert to decrypt * the ciphertext into the plaintext. */ KMF_RETURN kmf_decrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret; KMF_X509_CERTIFICATE *x509cert = NULL; KMF_X509_SPKI *spki_ptr; KMF_PLUGIN *plugin; KMF_ALGORITHM_INDEX AlgorithmId; KMF_ATTRIBUTE *new_attrlist = NULL; int new_numattr; KMF_DATA *cert = NULL; KMF_DATA *ciphertext = NULL; KMF_DATA *plaintext = NULL; KMF_KEY_HANDLE prikey; KMF_POLICY_RECORD *policy; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); /* Get the cert and check its keyUsage */ cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); if (cert == NULL) return (KMF_ERR_BAD_PARAMETER); /* check the keyUsage of the certificate */ policy = handle->policy; ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* Get the ciphertext and plaintext attributes */ ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist, numattr); if (ciphertext == NULL) return (KMF_ERR_BAD_PARAMETER); plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist, numattr); if (plaintext == NULL) return (KMF_ERR_BAD_PARAMETER); /* * Retrieve the private key from the keystore based on * the certificate. */ ret = setup_findprikey_attrlist(attrlist, numattr, &new_attrlist, &new_numattr, &prikey, cert); if (ret != KMF_OK) goto cleanup; ret = kmf_find_prikey_by_cert(handle, new_numattr, new_attrlist); if (ret != KMF_OK) goto cleanup; /* Decode the cert so we can get the alogorithm */ ret = DerDecodeSignedCertificate(cert, &x509cert); if (ret != KMF_OK) goto cleanup; spki_ptr = &x509cert->certificate.subjectPublicKeyInfo; AlgorithmId = x509_algoid_to_algid((KMF_OID *) &spki_ptr->algorithm.algorithm); /* [EC]DSA does not support decrypt */ if (AlgorithmId == KMF_ALGID_DSA || AlgorithmId == KMF_ALGID_ECDSA) { ret = KMF_ERR_BAD_ALGORITHM; goto cleanup; } plugin = FindPlugin(handle, prikey.kstype); if (plugin != NULL && plugin->funclist->DecryptData != NULL) { ret = plugin->funclist->DecryptData(handle, &prikey, &spki_ptr->algorithm.algorithm, ciphertext, plaintext); } else { ret = KMF_ERR_PLUGIN_NOTFOUND; } cleanup: if (new_attrlist != NULL) free(new_attrlist); kmf_free_kmf_key(handle, &prikey); kmf_free_signed_cert(x509cert); free(x509cert); return (ret); } KMF_RETURN kmf_store_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->StoreCert == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->StoreCert(handle, numattr, attrlist)); } KMF_RETURN kmf_import_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_FILENAME_ATTR, TRUE, 1, 0}, }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->ImportCert == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->ImportCert(handle, numattr, attrlist)); } KMF_RETURN kmf_delete_cert_from_keystore(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->DeleteCert == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->DeleteCert(handle, numattr, attrlist)); } /* * This function gets the CRL URI entries from the certificate's Distribution * points extension, and downloads the CRL file. The function also returns * the URI string and the format of the CRL file. The caller should free * the space allocated for the returned URI string. */ static KMF_RETURN cert_get_crl(KMF_HANDLE_T handle, const KMF_DATA *cert, char *proxy, char *filename, char **retn_uri, KMF_ENCODE_FORMAT *format) { KMF_RETURN ret = KMF_OK; KMF_X509EXT_CRLDISTPOINTS crl_dps; boolean_t done = B_FALSE; char uri[1024]; char *proxyname = NULL; char *proxy_port_s = NULL; int proxy_port = 0; int i, j; char *path = NULL; if (handle == NULL || cert == NULL || filename == NULL || retn_uri == NULL || format == NULL) return (KMF_ERR_BAD_PARAMETER); /* Get the proxy info */ if (proxy != NULL) { proxyname = strtok(proxy, ":"); proxy_port_s = strtok(NULL, "\0"); if (proxy_port_s != NULL) { proxy_port = strtol(proxy_port_s, NULL, 0); } else { proxy_port = 8080; /* default */ } } /* * Get the CRL URI from the certificate's CRL Distribution * Points extension and download the CRL file. There maybe more than * one CRL URI entries in the DP extension, so we will continue * the process until a CRL file is sucessfully downloaded or we * are running out the CRL URI's. */ ret = kmf_get_cert_crl_dist_pts((const KMF_DATA *)cert, &crl_dps); if (ret != KMF_OK) goto out; for (i = 0; i < crl_dps.number; i++) { KMF_CRL_DIST_POINT *dp = &(crl_dps.dplist[i]); KMF_GENERALNAMES *fullname = &(dp->name.full_name); KMF_DATA *data; if (done) break; for (j = 0; j < fullname->number; j++) { data = &(fullname->namelist[j].name); (void) memcpy(uri, data->Data, data->Length); uri[data->Length] = '\0'; ret = kmf_download_crl(handle, uri, proxyname, proxy_port, 30, filename, format); if (ret == KMF_OK) { done = B_TRUE; path = malloc(data->Length + 1); if (path == NULL) { ret = KMF_ERR_MEMORY; goto out; } (void) strncpy(path, uri, data->Length); *retn_uri = path; break; } } } out: kmf_free_crl_dist_pts(&crl_dps); return (ret); } static KMF_RETURN check_crl_validity(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE kstype, char *crlfilename, KMF_DATA *issuer_cert) { KMF_RETURN ret = KMF_OK; KMF_POLICY_RECORD *policy; if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; /* * NSS CRL is not file based, and its signature * has been verified during CRL import. * We only check CRL validity for file-based CRLs, * NSS handles these checks internally. */ if (kstype == KMF_KEYSTORE_NSS) return (KMF_OK); /* * Check the CRL signature if needed. */ if (!policy->validation_info.crl_info.ignore_crl_sign) { ret = kmf_verify_crl_file(handle, crlfilename, issuer_cert); if (ret != KMF_OK) return (ret); } /* * Check the CRL validity if needed. */ if (!policy->validation_info.crl_info.ignore_crl_date) { ret = kmf_check_crl_date(handle, crlfilename); if (ret != KMF_OK) return (ret); } return (ret); } static KMF_RETURN cert_crl_check(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype, KMF_DATA *user_cert, KMF_DATA *issuer_cert) { KMF_POLICY_RECORD *policy; KMF_RETURN ret = KMF_OK; KMF_ATTRIBUTE attrlist[16]; int numattr = 0; int fd; boolean_t crlchk; char user_certfile[MAXPATHLEN]; char crlfile_tmp[MAXPATHLEN]; char *basefilename = NULL; char *dir = NULL; char *crlfilename = NULL; char *proxy = NULL; char *uri = NULL; KMF_ENCODE_FORMAT format; if (handle == NULL || kstype == NULL || user_cert == NULL || issuer_cert == NULL) return (KMF_ERR_BAD_PARAMETER); if (!is_valid_keystore_type(*kstype)) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; /* * If the get-crl-uri policy is TRUE, then download the CRL * file first. The newly downloaded file will be stored in the * NSS internal database for NSS keystore, and stored in a file for * the File-based CRL plugins (OpenSSL and PKCS11). * * For file-based plugins, if the get-crl-uri policy is FALSE, * then the caller should provide a CRL file in the policy. * Also, after this step is done, the "crlfilename" variable should * contain the proper CRL file to be used for the rest of CRL * validation process. */ basefilename = policy->validation_info.crl_info.basefilename; dir = policy->validation_info.crl_info.directory; if (policy->validation_info.crl_info.get_crl_uri) { /* * Check to see if we already have this CRL. */ if (basefilename == NULL) basefilename = basename(uri); crlfilename = get_fullpath(dir == NULL ? "./" : dir, basefilename); if (crlfilename == NULL) { ret = KMF_ERR_BAD_CRLFILE; goto cleanup; } /* * If this file already exists and is valid, we don't need to * download a new one. */ if ((fd = open(crlfilename, O_RDONLY)) != -1) { (void) close(fd); if ((ret = check_crl_validity(handle, *kstype, crlfilename, issuer_cert)) == KMF_OK) { goto checkcrl; } } /* * Create a temporary file to hold the new CRL file initially. */ (void) strlcpy(crlfile_tmp, CRLFILE_TEMPNAME, sizeof (crlfile_tmp)); if (mkstemp(crlfile_tmp) == -1) { ret = KMF_ERR_INTERNAL; goto cleanup; } /* * Get the URI entry from the certificate's CRL distribution * points extension and download the CRL file. */ proxy = policy->validation_info.crl_info.proxy; ret = cert_get_crl(handle, user_cert, proxy, crlfile_tmp, &uri, &format); if (ret != KMF_OK) { (void) unlink(crlfile_tmp); goto cleanup; } /* * If we just downloaded one, make sure it is OK. */ if ((ret = check_crl_validity(handle, *kstype, crlfile_tmp, issuer_cert)) != KMF_OK) return (ret); /* Cache the CRL file. */ if (*kstype == KMF_KEYSTORE_NSS) { /* * For NSS keystore, import this CRL file into th * internal database. */ numattr = 0; kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (kstype)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_CRL_FILENAME_ATTR, crlfile_tmp, strlen(crlfile_tmp)); numattr++; crlchk = B_FALSE; kmf_set_attr_at_index(attrlist, numattr, KMF_CRL_CHECK_ATTR, &crlchk, sizeof (boolean_t)); numattr++; ret = kmf_import_crl(handle, numattr, attrlist); (void) unlink(crlfile_tmp); if (ret != KMF_OK) goto cleanup; } else { if (rename(crlfile_tmp, crlfilename) == -1) { (void) unlink(crlfile_tmp); ret = KMF_ERR_WRITE_FILE; goto cleanup; } } } else { /* * If the get_crl_uri policy is FALSE, for File-based CRL * plugins, get the input CRL file from the policy. */ if (*kstype != KMF_KEYSTORE_NSS) { if (basefilename == NULL) { ret = KMF_ERR_BAD_PARAMETER; goto cleanup; } crlfilename = get_fullpath(dir == NULL ? "./" : dir, basefilename); if (crlfilename == NULL) { ret = KMF_ERR_BAD_CRLFILE; goto cleanup; } /* * Make sure this CRL is still valid. */ if ((ret = check_crl_validity(handle, *kstype, crlfilename, issuer_cert)) != KMF_OK) return (ret); } } checkcrl: /* * Check the CRL revocation for the certificate. */ numattr = 0; kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (kstype)); numattr++; switch (*kstype) { case KMF_KEYSTORE_NSS: kmf_set_attr_at_index(attrlist, numattr, KMF_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA)); numattr++; break; case KMF_KEYSTORE_PK11TOKEN: case KMF_KEYSTORE_OPENSSL: /* * Create temporary file to hold the user certificate. */ (void) strlcpy(user_certfile, CERTFILE_TEMPNAME, sizeof (user_certfile)); if (mkstemp(user_certfile) == -1) { ret = KMF_ERR_INTERNAL; goto cleanup; } ret = kmf_create_cert_file(user_cert, KMF_FORMAT_ASN1, user_certfile); if (ret != KMF_OK) { goto cleanup; } kmf_set_attr_at_index(attrlist, numattr, KMF_CERT_FILENAME_ATTR, user_certfile, strlen(user_certfile)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_CRL_FILENAME_ATTR, crlfilename, strlen(crlfilename)); numattr++; break; default: ret = KMF_ERR_PLUGIN_NOTFOUND; goto cleanup; } ret = kmf_find_cert_in_crl(handle, numattr, attrlist); if (ret == KMF_ERR_NOT_REVOKED) { ret = KMF_OK; } cleanup: (void) unlink(user_certfile); if (crlfilename != NULL) free(crlfilename); if (uri != NULL) free(uri); return (ret); } static KMF_RETURN cert_ocsp_check(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype, KMF_DATA *user_cert, KMF_DATA *issuer_cert, KMF_DATA *response, char *slotlabel, char *dirpath) { KMF_RETURN ret = KMF_OK; KMF_POLICY_RECORD *policy; KMF_DATA *new_response = NULL; boolean_t ignore_response_sign = B_FALSE; uint32_t ltime = 0; KMF_DATA *signer_cert = NULL; KMF_BIGINT sernum = { NULL, 0 }; int response_status; int reason; int cert_status; KMF_ATTRIBUTE attrlist[32]; int numattr; if (handle == NULL || kstype == NULL || user_cert == NULL || issuer_cert == NULL) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; /* * Get the response lifetime from policy. */ if (policy->VAL_OCSP_BASIC.response_lifetime != NULL && (str2lifetime(policy->VAL_OCSP_BASIC.response_lifetime, <ime) < 0)) return (KMF_ERR_OCSP_RESPONSE_LIFETIME); /* * Get the ignore_response_sign policy. * * If ignore_response_sign is FALSE, we need to verify the response. * Find the OCSP Responder certificate if it is specified in the OCSP * policy. */ ignore_response_sign = policy->VAL_OCSP_BASIC.ignore_response_sign; if (ignore_response_sign == B_FALSE && policy->VAL_OCSP.has_resp_cert == B_TRUE) { char *signer_name; KMF_X509_DER_CERT signer_retrcert; uchar_t *bytes = NULL; size_t bytelen; uint32_t num = 0; KMF_ATTRIBUTE fc_attrlist[16]; int fc_numattr = 0; char *dir = "./"; if (policy->VAL_OCSP_RESP_CERT.name == NULL || policy->VAL_OCSP_RESP_CERT.serial == NULL) return (KMF_ERR_POLICY_NOT_FOUND); signer_cert = malloc(sizeof (KMF_DATA)); if (signer_cert == NULL) { ret = KMF_ERR_MEMORY; goto out; } (void) memset(signer_cert, 0, sizeof (KMF_DATA)); signer_name = policy->VAL_OCSP_RESP_CERT.name; ret = kmf_hexstr_to_bytes( (uchar_t *)policy->VAL_OCSP_RESP_CERT.serial, &bytes, &bytelen); if (ret != KMF_OK || bytes == NULL) { ret = KMF_ERR_OCSP_POLICY; goto out; } sernum.val = bytes; sernum.len = bytelen; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (KMF_KEYSTORE_TYPE)); fc_numattr++; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_SUBJECT_NAME_ATTR, signer_name, strlen(signer_name)); fc_numattr++; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_BIGINT_ATTR, &sernum, sizeof (KMF_BIGINT)); fc_numattr++; if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel)); fc_numattr++; } if (*kstype == KMF_KEYSTORE_OPENSSL) { if (dirpath == NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_DIRPATH_ATTR, dir, strlen(dir)); fc_numattr++; } else { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_DIRPATH_ATTR, dirpath, strlen(dirpath)); fc_numattr++; } } num = 0; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_COUNT_ATTR, &num, sizeof (uint32_t)); fc_numattr++; ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret != KMF_OK || num != 1) { if (num == 0) ret = KMF_ERR_CERT_NOT_FOUND; if (num > 0) ret = KMF_ERR_CERT_MULTIPLE_FOUND; goto out; } (void) memset(&signer_retrcert, 0, sizeof (KMF_X509_DER_CERT)); kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_X509_DER_CERT_ATTR, &signer_retrcert, sizeof (KMF_X509_DER_CERT)); fc_numattr++; ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret == KMF_OK) { signer_cert->Length = signer_retrcert.certificate.Length; signer_cert->Data = signer_retrcert.certificate.Data; } else { goto out; } } /* * If the caller provides an OCSP response, we will use it directly. * Otherwise, we will try to fetch an OCSP response for the given * certificate now. */ if (response == NULL) { new_response = (KMF_DATA *) malloc(sizeof (KMF_DATA)); if (new_response == NULL) { ret = KMF_ERR_MEMORY; goto out; } new_response->Data = NULL; new_response->Length = 0; ret = kmf_get_ocsp_for_cert(handle, user_cert, issuer_cert, new_response); if (ret != KMF_OK) goto out; } /* * Process the OCSP response and retrieve the certificate status. */ numattr = 0; kmf_set_attr_at_index(attrlist, numattr, KMF_ISSUER_CERT_DATA_ATTR, issuer_cert, sizeof (KMF_DATA)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_USER_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA)); numattr++; if (signer_cert != NULL) { kmf_set_attr_at_index(attrlist, numattr, KMF_SIGNER_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA)); numattr++; } kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_DATA_ATTR, response == NULL ? new_response : response, sizeof (KMF_DATA)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_RESPONSE_LIFETIME_ATTR, <ime, sizeof (uint32_t)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_IGNORE_RESPONSE_SIGN_ATTR, &ignore_response_sign, sizeof (boolean_t)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_STATUS_ATTR, &response_status, sizeof (int)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_REASON_ATTR, &reason, sizeof (int)); numattr++; kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_CERT_STATUS_ATTR, &cert_status, sizeof (int)); numattr++; ret = kmf_get_ocsp_status_for_cert(handle, numattr, attrlist); if (ret == KMF_OK) { switch (cert_status) { case OCSP_GOOD: break; case OCSP_UNKNOWN: ret = KMF_ERR_OCSP_UNKNOWN_CERT; break; case OCSP_REVOKED: ret = KMF_ERR_OCSP_REVOKED; break; } } out: if (new_response) { kmf_free_data(new_response); free(new_response); } if (signer_cert) { kmf_free_data(signer_cert); free(signer_cert); } if (sernum.val != NULL) free(sernum.val); return (ret); } static KMF_RETURN cert_ku_check(KMF_HANDLE_T handle, KMF_DATA *cert) { KMF_POLICY_RECORD *policy; KMF_X509EXT_KEY_USAGE keyusage; KMF_RETURN ret = KMF_OK; KMF_X509EXT_BASICCONSTRAINTS constraint; KMF_BOOL critical = B_FALSE; if (handle == NULL || cert == NULL) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; (void) memset(&keyusage, 0, sizeof (keyusage)); ret = kmf_get_cert_ku(cert, &keyusage); if (ret == KMF_ERR_EXTENSION_NOT_FOUND) { if (policy->ku_bits) { /* keyusage is not set in cert but is set in policy */ return (KMF_ERR_KEYUSAGE); } else { /* no keyusage set in both cert and policy */ return (KMF_OK); } } if (ret != KMF_OK) { /* real error */ return (ret); } /* * If KeyCertSign is set, then constraints.cA must be TRUE and * marked critical. */ if ((keyusage.KeyUsageBits & KMF_keyCertSign)) { (void) memset(&constraint, 0, sizeof (constraint)); ret = kmf_get_cert_basic_constraint(cert, &critical, &constraint); if (ret != KMF_OK) { /* real error */ return (ret); } if (!constraint.cA || !critical) return (KMF_ERR_KEYUSAGE); } /* * Rule: if the KU bit is set in policy, the corresponding KU bit * must be set in the certificate (but not vice versa). */ if ((policy->ku_bits & keyusage.KeyUsageBits) == policy->ku_bits) { return (KMF_OK); } else { return (KMF_ERR_KEYUSAGE); } } static KMF_RETURN cert_eku_check(KMF_HANDLE_T handle, KMF_DATA *cert) { KMF_POLICY_RECORD *policy; KMF_RETURN ret = KMF_OK; KMF_X509EXT_EKU eku; uint16_t cert_eku = 0, policy_eku = 0; int i; if (handle == NULL || cert == NULL) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; /* * If the policy does not have any EKU, then there is * nothing further to check. */ if (policy->eku_set.eku_count == 0) return (KMF_OK); ret = kmf_get_cert_eku(cert, &eku); if ((ret != KMF_ERR_EXTENSION_NOT_FOUND) && (ret != KMF_OK)) { /* real error */ return (ret); } if (ret == KMF_ERR_EXTENSION_NOT_FOUND) { cert_eku = 0; } else { /* * Build the EKU bitmap based on the certificate */ for (i = 0; i < eku.nEKUs; i++) { if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) { cert_eku |= KMF_EKU_SERVERAUTH; } else if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) { cert_eku |= KMF_EKU_CLIENTAUTH; } else if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) { cert_eku |= KMF_EKU_CODESIGNING; } else if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) { cert_eku |= KMF_EKU_EMAIL; } else if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) { cert_eku |= KMF_EKU_TIMESTAMP; } else if (IsEqualOid(&eku.keyPurposeIdList[i], (KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) { cert_eku |= KMF_EKU_OCSPSIGNING; } else if (!policy->ignore_unknown_ekus) { return (KMF_ERR_KEYUSAGE); } } /* for */ } /* * Build the EKU bitmap based on the policy */ for (i = 0; i < policy->eku_set.eku_count; i++) { if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) { policy_eku |= KMF_EKU_SERVERAUTH; } else if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) { policy_eku |= KMF_EKU_CLIENTAUTH; } else if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) { policy_eku |= KMF_EKU_CODESIGNING; } else if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) { policy_eku |= KMF_EKU_EMAIL; } else if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) { policy_eku |= KMF_EKU_TIMESTAMP; } else if (IsEqualOid(&policy->eku_set.ekulist[i], (KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) { policy_eku |= KMF_EKU_OCSPSIGNING; } else if (!policy->ignore_unknown_ekus) { return (KMF_ERR_KEYUSAGE); } } /* for */ /* * Rule: if the EKU OID is set in policy, the corresponding EKU OID * must be set in the certificate (but not vice versa). */ if ((policy_eku & cert_eku) == policy_eku) { return (KMF_OK); } else { return (KMF_ERR_KEYUSAGE); } } static KMF_RETURN find_issuer_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype, char *user_issuer, KMF_DATA *issuer_cert, char *slotlabel, char *dirpath) { KMF_RETURN ret = KMF_OK; KMF_X509_DER_CERT *certlist = NULL; uint32_t i, num = 0; time_t t_notbefore; time_t t_notafter; time_t latest; KMF_DATA tmp_cert = { 0, NULL }; KMF_ATTRIBUTE fc_attrlist[16]; int fc_numattr = 0; char *dir = "./"; if (handle == NULL || kstype == NULL || user_issuer == NULL || issuer_cert == NULL) return (KMF_ERR_BAD_PARAMETER); if (!is_valid_keystore_type(*kstype)) return (KMF_ERR_BAD_PARAMETER); kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (KMF_KEYSTORE_TYPE)); fc_numattr++; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_SUBJECT_NAME_ATTR, user_issuer, strlen(user_issuer)); fc_numattr++; if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel)); fc_numattr++; } if (*kstype == KMF_KEYSTORE_OPENSSL) { if (dirpath == NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_DIRPATH_ATTR, dir, strlen(dir)); fc_numattr++; } else { kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_DIRPATH_ATTR, dirpath, strlen(dirpath)); fc_numattr++; } } num = 0; kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_COUNT_ATTR, &num, sizeof (uint32_t)); fc_numattr++; ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret == KMF_OK && num > 0) { certlist = (KMF_X509_DER_CERT *)malloc(num * sizeof (KMF_X509_DER_CERT)); if (certlist == NULL) { ret = KMF_ERR_MEMORY; goto out; } kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_X509_DER_CERT_ATTR, certlist, sizeof (KMF_X509_DER_CERT)); fc_numattr++; ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret != KMF_OK) { free(certlist); certlist = NULL; goto out; } } else { goto out; } if (num == 1) { /* only one issuer cert is found */ tmp_cert.Length = certlist[0].certificate.Length; tmp_cert.Data = certlist[0].certificate.Data; } else { /* * More than one issuer certs are found. We will * pick the latest one. */ latest = 0; for (i = 0; i < num; i++) { ret = kmf_get_cert_validity(&certlist[i].certificate, &t_notbefore, &t_notafter); if (ret != KMF_OK) { ret = KMF_ERR_VALIDITY_PERIOD; goto out; } if (t_notbefore > latest) { tmp_cert.Length = certlist[i].certificate.Length; tmp_cert.Data = certlist[i].certificate.Data; latest = t_notbefore; } } } issuer_cert->Length = tmp_cert.Length; issuer_cert->Data = malloc(tmp_cert.Length); if (issuer_cert->Data == NULL) { ret = KMF_ERR_MEMORY; goto out; } (void) memcpy(issuer_cert->Data, tmp_cert.Data, tmp_cert.Length); out: if (certlist != NULL) { for (i = 0; i < num; i++) kmf_free_kmf_cert(handle, &certlist[i]); free(certlist); } return (ret); } static KMF_RETURN find_ta_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype, KMF_DATA *ta_cert, KMF_X509_NAME *user_issuerDN, char *slotlabel, char *dirpath) { KMF_POLICY_RECORD *policy; KMF_RETURN ret = KMF_OK; uint32_t num = 0; char *ta_name; KMF_BIGINT serial = { NULL, 0 }; uchar_t *bytes = NULL; size_t bytelen; KMF_X509_DER_CERT ta_retrCert; char *ta_subject = NULL; KMF_X509_NAME ta_subjectDN; KMF_ATTRIBUTE fc_attrlist[16]; int fc_numattr = 0; char *dir = "./"; if (handle == NULL || kstype == NULL || ta_cert == NULL || user_issuerDN == NULL) return (KMF_ERR_BAD_PARAMETER); if (!is_valid_keystore_type(*kstype)) return (KMF_ERR_BAD_PARAMETER); /* Get the TA name and serial number from the policy */ policy = handle->policy; ta_name = policy->ta_name; /* * Use name and serial from policy. */ ret = kmf_hexstr_to_bytes((uchar_t *)policy->ta_serial, &bytes, &bytelen); if (ret != KMF_OK || bytes == NULL) { ret = KMF_ERR_TA_POLICY; goto out; } serial.val = bytes; serial.len = bytelen; /* set up fc_attrlist for kmf_find_cert */ kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_BIGINT_ATTR, &serial, sizeof (KMF_BIGINT)); kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_SUBJECT_NAME_ATTR, ta_name, strlen(ta_name)); kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (KMF_KEYSTORE_TYPE)); if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel)); } if (*kstype == KMF_KEYSTORE_OPENSSL) { if (dirpath == NULL) { kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_DIRPATH_ATTR, dir, strlen(dir)); } else { kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_DIRPATH_ATTR, dirpath, strlen(dirpath)); } } num = 0; kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_COUNT_ATTR, &num, sizeof (uint32_t)); ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret != KMF_OK || num != 1) { if (num == 0) ret = KMF_ERR_CERT_NOT_FOUND; if (num > 1) ret = KMF_ERR_CERT_MULTIPLE_FOUND; goto out; } kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_X509_DER_CERT_ATTR, &ta_retrCert, sizeof (KMF_X509_DER_CERT)); fc_numattr++; ret = kmf_find_cert(handle, fc_numattr, fc_attrlist); if (ret == KMF_OK) { ta_cert->Length = ta_retrCert.certificate.Length; ta_cert->Data = malloc(ta_retrCert.certificate.Length); if (ta_cert->Data == NULL) { ret = KMF_ERR_MEMORY; goto out; } (void) memcpy(ta_cert->Data, ta_retrCert.certificate.Data, ta_retrCert.certificate.Length); } else { goto out; } /* * The found TA's name must be matching with issuer name in * subscriber's certificate. */ (void) memset(&ta_subjectDN, 0, sizeof (ta_subjectDN)); ret = kmf_get_cert_subject_str(handle, ta_cert, &ta_subject); if (ret != KMF_OK) goto out; ret = kmf_dn_parser(ta_subject, &ta_subjectDN); if (ret != KMF_OK) goto out; if (kmf_compare_rdns(user_issuerDN, &ta_subjectDN) != 0) ret = KMF_ERR_CERT_NOT_FOUND; kmf_free_dn(&ta_subjectDN); /* Make sure the TA cert has the correct extensions */ if (ret == KMF_OK) { ret = check_key_usage(handle, ta_cert, KMF_KU_SIGN_CERT); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; } out: if (ta_retrCert.certificate.Data) kmf_free_kmf_cert(handle, &ta_retrCert); if ((ret != KMF_OK)) kmf_free_data(ta_cert); if (ta_subject != NULL) free(ta_subject); if (serial.val != NULL) free(serial.val); return (ret); } KMF_RETURN kmf_validate_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE *kstype = NULL; KMF_DATA *pcert = NULL; int *result = NULL; char *slotlabel = NULL; char *dirpath = NULL; KMF_DATA *ocsp_response = NULL; KMF_DATA ta_cert = { 0, NULL }; KMF_DATA issuer_cert = { 0, NULL }; char *user_issuer = NULL, *user_subject = NULL; KMF_X509_NAME user_issuerDN, user_subjectDN; boolean_t self_signed = B_FALSE; KMF_POLICY_RECORD *policy; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}, {KMF_VALIDATE_RESULT_ATTR, FALSE, 1, sizeof (int)} }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); policy = handle->policy; /* Get the attribute values */ kstype = kmf_get_attr_ptr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr); pcert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); result = kmf_get_attr_ptr(KMF_VALIDATE_RESULT_ATTR, attrlist, numattr); if (kstype == NULL || pcert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); slotlabel = kmf_get_attr_ptr(KMF_TOKEN_LABEL_ATTR, attrlist, numattr); dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); ocsp_response = kmf_get_attr_ptr(KMF_OCSP_RESPONSE_DATA_ATTR, attrlist, numattr); /* Initialize the returned result */ *result = KMF_CERT_VALIDATE_OK; /* * Get the issuer information from the input certficate first. */ if ((ret = kmf_get_cert_issuer_str(handle, pcert, &user_issuer)) != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_USER; } else if ((ret = kmf_dn_parser(user_issuer, &user_issuerDN)) != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_USER; } /* * Check if the certificate is a self-signed cert. */ if ((ret = kmf_get_cert_subject_str(handle, pcert, &user_subject)) != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_USER; } else if ((ret = kmf_dn_parser(user_subject, &user_subjectDN)) != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_USER; } if ((*result & KMF_CERT_VALIDATE_ERR_USER) == 0 && (kmf_compare_rdns(&user_issuerDN, &user_subjectDN)) == 0) { /* * this is a self-signed cert */ self_signed = B_TRUE; } kmf_free_dn(&user_subjectDN); /* * Check KeyUsage extension of the subscriber's certificate */ ret = cert_ku_check(handle, pcert); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_KEYUSAGE; } /* * Validate Extended KeyUsage extension */ ret = cert_eku_check(handle, pcert); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_EXT_KEYUSAGE; } /* * Check the certificate's validity period * * This step is needed when "ignore_date" in policy is set * to false. */ if (!policy->ignore_date) { /* * Validate expiration date */ ret = kmf_check_cert_date(handle, pcert); if (ret != KMF_OK) *result |= KMF_CERT_VALIDATE_ERR_TIME; } /* * When "ignore_trust_anchor" in policy is set to FALSE, * we will try to find the TA cert based on the TA policy * attributes. * * TA's subject name (ta_name) and serial number (ta_serial) * are defined as optional attributes in policy dtd, but they * should exist in policy when "ignore_trust_anchor" is set * to FALSE. The policy verification code has enforced that. * * The serial number may be NULL if the ta_name == "search" * which indicates that KMF should try to locate the issuer * of the subject cert instead of using a specific TA name. */ if (policy->ignore_trust_anchor) { goto check_revocation; } /* * Verify the signature of subscriber's certificate using * TA certificate. */ if (self_signed) { ret = verify_cert_with_cert(handle, pcert, pcert); if (ret != KMF_OK) *result |= KMF_CERT_VALIDATE_ERR_SIGNATURE; } else if (user_issuer != NULL) { if (policy->ta_name != NULL && strcasecmp(policy->ta_name, "search") == 0) { ret = find_issuer_cert(handle, kstype, user_issuer, &issuer_cert, slotlabel, dirpath); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_TA; } else { ta_cert = issuer_cert; /* used later */ } } else { /* * If we didnt find the user_issuer string, we * won't have a "user_issuerDN" either. */ ret = find_ta_cert(handle, kstype, &ta_cert, &user_issuerDN, slotlabel, dirpath); } if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_TA; } /* Only verify if we got the TA without an error. */ if ((*result & KMF_CERT_VALIDATE_ERR_TA) == 0) { ret = verify_cert_with_cert(handle, pcert, &ta_cert); if (ret != KMF_OK) *result |= KMF_CERT_VALIDATE_ERR_SIGNATURE; } } else { /* No issuer was found, so we cannot find a trust anchor */ *result |= KMF_CERT_VALIDATE_ERR_TA; } check_revocation: /* * Check certificate revocation */ if (self_signed) { /* skip revocation checking */ goto out; } /* * When CRL or OCSP revocation method is set in the policy, * we will try to find the issuer of the subscriber certificate * using the issuer name of the subscriber certificate. The * issuer certificate will be used to do the CRL checking * and OCSP checking. */ if (!(policy->revocation & KMF_REVOCATION_METHOD_CRL) && !(policy->revocation & KMF_REVOCATION_METHOD_OCSP)) { goto out; } /* * If we did not find the issuer cert earlier * (when policy->ta_name == "search"), get it here. * We need the issuer cert if the revocation method is * CRL or OCSP. */ if (issuer_cert.Length == 0 && policy->revocation & KMF_REVOCATION_METHOD_CRL || policy->revocation & KMF_REVOCATION_METHOD_OCSP) { ret = find_issuer_cert(handle, kstype, user_issuer, &issuer_cert, slotlabel, dirpath); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_ISSUER; } } if (policy->revocation & KMF_REVOCATION_METHOD_CRL && (*result & KMF_CERT_VALIDATE_ERR_ISSUER) == 0) { ret = cert_crl_check(handle, kstype, pcert, &issuer_cert); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_CRL; } } if (policy->revocation & KMF_REVOCATION_METHOD_OCSP && (*result & KMF_CERT_VALIDATE_ERR_ISSUER) == 0) { ret = cert_ocsp_check(handle, kstype, pcert, &issuer_cert, ocsp_response, slotlabel, dirpath); if (ret != KMF_OK) { *result |= KMF_CERT_VALIDATE_ERR_OCSP; } } out: if (user_issuer) { kmf_free_dn(&user_issuerDN); free(user_issuer); } if (user_subject) free(user_subject); /* * If we did not copy ta_cert to issuer_cert, free it. */ if (issuer_cert.Data && issuer_cert.Data != ta_cert.Data) kmf_free_data(&issuer_cert); kmf_free_data(&ta_cert); /* * If we got an error flag from any of the checks, * remap the return code to a generic "CERT_VALIDATION" * error so the caller knows to check the individual flags. */ if (*result != 0) ret = KMF_ERR_CERT_VALIDATION; return (ret); } KMF_RETURN kmf_create_cert_file(const KMF_DATA *certdata, KMF_ENCODE_FORMAT format, char *certfile) { KMF_RETURN rv = KMF_OK; int fd = -1; KMF_DATA pemdata = { 0, NULL }; if (certdata == NULL || certfile == NULL) return (KMF_ERR_BAD_PARAMETER); if (format != KMF_FORMAT_PEM && format != KMF_FORMAT_ASN1) return (KMF_ERR_BAD_PARAMETER); if (format == KMF_FORMAT_PEM) { int len; rv = kmf_der_to_pem(KMF_CERT, certdata->Data, certdata->Length, &pemdata.Data, &len); if (rv != KMF_OK) goto cleanup; pemdata.Length = (size_t)len; } if ((fd = open(certfile, O_CREAT | O_RDWR | O_TRUNC, 0644)) == -1) { rv = KMF_ERR_OPEN_FILE; goto cleanup; } if (format == KMF_FORMAT_PEM) { if (write(fd, pemdata.Data, pemdata.Length) != pemdata.Length) { rv = KMF_ERR_WRITE_FILE; } } else { if (write(fd, certdata->Data, certdata->Length) != certdata->Length) { rv = KMF_ERR_WRITE_FILE; } } cleanup: if (fd != -1) (void) close(fd); kmf_free_data(&pemdata); return (rv); } /* * kmf_is_cert_data * * Determine if a KMF_DATA buffer contains an encoded X.509 certificate. * * Return: * KMF_OK if it is a certificate * KMF_ERR_ENCODING (or other error) if not. */ KMF_RETURN kmf_is_cert_data(KMF_DATA *data, KMF_ENCODE_FORMAT *fmt) { KMF_RETURN rv = KMF_OK; KMF_X509_CERTIFICATE *x509 = NULL; KMF_DATA oldpem = { 0, NULL }; uchar_t *d = NULL; int len = 0; if (data == NULL || fmt == NULL) return (KMF_ERR_BAD_PARAMETER); rv = kmf_get_data_format(data, fmt); if (rv != KMF_OK) return (rv); switch (*fmt) { case KMF_FORMAT_ASN1: rv = DerDecodeSignedCertificate(data, &x509); break; case KMF_FORMAT_PEM: /* Convert to ASN.1 DER first */ rv = kmf_pem_to_der(data->Data, data->Length, &d, &len); if (rv != KMF_OK) return (rv); oldpem.Data = d; oldpem.Length = len; rv = DerDecodeSignedCertificate(&oldpem, &x509); kmf_free_data(&oldpem); break; case KMF_FORMAT_PKCS12: case KMF_FORMAT_UNDEF: default: return (KMF_ERR_ENCODING); } if (x509 != NULL) { kmf_free_signed_cert(x509); free(x509); } return (rv); } KMF_RETURN kmf_is_cert_file(KMF_HANDLE_T handle, char *filename, KMF_ENCODE_FORMAT *pformat) { KMF_RETURN ret; KMF_DATA filedata; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (filename == NULL || pformat == NULL) return (KMF_ERR_BAD_PARAMETER); ret = kmf_read_input_file(handle, filename, &filedata); if (ret != KMF_OK) return (ret); ret = kmf_is_cert_data(&filedata, pformat); if (ret == KMF_ERR_BAD_CERT_FORMAT) ret = KMF_ERR_BAD_CERTFILE; kmf_free_data(&filedata); return (ret); } /* * This function checks the validity period of a der-encoded certificate. */ KMF_RETURN kmf_check_cert_date(KMF_HANDLE_T handle, const KMF_DATA *cert) { KMF_RETURN rv; struct tm *gmt; time_t t_now; time_t t_notbefore; time_t t_notafter; KMF_POLICY_RECORD *policy; uint32_t adj; CLEAR_ERROR(handle, rv); if (rv != KMF_OK) return (rv); if (cert == NULL || cert->Data == NULL || cert->Length == 0) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; rv = kmf_get_cert_validity(cert, &t_notbefore, &t_notafter); if (rv != KMF_OK) return (rv); /* * Get the current time. The time returned from time() is local which * cannot be used directly. It must be converted to UTC/GMT first. */ t_now = time(NULL); gmt = gmtime(&t_now); t_now = mktime(gmt); /* * Adjust the validity time */ if (policy->validity_adjusttime != NULL) { if (str2lifetime(policy->validity_adjusttime, &adj) < 0) return (KMF_ERR_VALIDITY_PERIOD); } else { adj = 0; } t_notafter += adj; t_notbefore -= adj; if (t_now <= t_notafter && t_now >= t_notbefore) { rv = KMF_OK; } else { rv = KMF_ERR_VALIDITY_PERIOD; } return (rv); } KMF_RETURN kmf_export_pk12(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_PLUGIN *plugin; KMF_RETURN ret = KMF_OK; KMF_KEYSTORE_TYPE kstype; KMF_ATTRIBUTE_TESTER required_attrs[] = { {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, {KMF_OUTPUT_FILENAME_ATTR, TRUE, 1, 0}, }; int num_req_attrs = sizeof (required_attrs) / sizeof (KMF_ATTRIBUTE_TESTER); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); CLEAR_ERROR(handle, ret); ret = test_attributes(num_req_attrs, required_attrs, 0, NULL, numattr, attrlist); if (ret != KMF_OK) return (ret); ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr, &kstype, NULL); if (ret != KMF_OK) return (ret); plugin = FindPlugin(handle, kstype); if (plugin == NULL || plugin->funclist->ExportPK12 == NULL) return (KMF_ERR_PLUGIN_NOTFOUND); return (plugin->funclist->ExportPK12(handle, numattr, attrlist)); } KMF_RETURN kmf_build_pk12(KMF_HANDLE_T handle, int numcerts, KMF_X509_DER_CERT *certlist, int numkeys, KMF_KEY_HANDLE *keylist, KMF_CREDENTIAL *p12cred, char *filename) { KMF_RETURN rv; KMF_PLUGIN *plugin; KMF_RETURN (*buildpk12)(KMF_HANDLE *, int, KMF_X509_DER_CERT *, int, KMF_KEY_HANDLE *, KMF_CREDENTIAL *, char *); CLEAR_ERROR(handle, rv); if (rv != KMF_OK) return (rv); if (filename == NULL || p12cred == NULL || (certlist == NULL && keylist == NULL)) return (KMF_ERR_BAD_PARAMETER); plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL); if (plugin == NULL || plugin->dldesc == NULL) { return (KMF_ERR_PLUGIN_NOTFOUND); } buildpk12 = (KMF_RETURN(*)())dlsym(plugin->dldesc, "openssl_build_pk12"); if (buildpk12 == NULL) { return (KMF_ERR_FUNCTION_NOT_FOUND); } rv = buildpk12(handle, numcerts, certlist, numkeys, keylist, p12cred, filename); return (rv); } KMF_RETURN kmf_import_objects(KMF_HANDLE_T handle, char *filename, KMF_CREDENTIAL *cred, KMF_X509_DER_CERT **certs, int *ncerts, KMF_RAW_KEY_DATA **rawkeys, int *nkeys) { KMF_RETURN rv; KMF_PLUGIN *plugin; KMF_RETURN (*import_objects)(KMF_HANDLE *, char *, KMF_CREDENTIAL *, KMF_X509_DER_CERT **, int *, KMF_RAW_KEY_DATA **, int *); CLEAR_ERROR(handle, rv); if (rv != KMF_OK) return (rv); if (filename == NULL || cred == NULL || certs == NULL || ncerts == NULL ||rawkeys == NULL || nkeys == NULL) return (KMF_ERR_BAD_PARAMETER); /* * Use the Keypair reader from the OpenSSL plugin. */ plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL); if (plugin == NULL || plugin->dldesc == NULL) { return (KMF_ERR_PLUGIN_NOTFOUND); } import_objects = (KMF_RETURN(*)())dlsym(plugin->dldesc, "openssl_import_objects"); if (import_objects == NULL) { return (KMF_ERR_FUNCTION_NOT_FOUND); } /* Use OpenSSL interfaces to get raw key and cert data */ rv = import_objects(handle, filename, cred, certs, ncerts, rawkeys, nkeys); return (rv); } KMF_BOOL IsEqualOid(KMF_OID *Oid1, KMF_OID *Oid2) { return ((Oid1->Length == Oid2->Length) && !memcmp(Oid1->Data, Oid2->Data, Oid1->Length)); } static KMF_RETURN set_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid, KMF_OID *newoid) { if (destid == NULL || newoid == NULL) return (KMF_ERR_BAD_PARAMETER); destid->algorithm.Length = newoid->Length; destid->algorithm.Data = malloc(destid->algorithm.Length); if (destid->algorithm.Data == NULL) return (KMF_ERR_MEMORY); (void) memcpy(destid->algorithm.Data, newoid->Data, destid->algorithm.Length); return (KMF_OK); } KMF_RETURN copy_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid, KMF_X509_ALGORITHM_IDENTIFIER *srcid) { KMF_RETURN ret = KMF_OK; if (!destid || !srcid) return (KMF_ERR_BAD_PARAMETER); destid->algorithm.Length = srcid->algorithm.Length; destid->algorithm.Data = malloc(destid->algorithm.Length); if (destid->algorithm.Data == NULL) return (KMF_ERR_MEMORY); (void) memcpy(destid->algorithm.Data, srcid->algorithm.Data, destid->algorithm.Length); destid->parameters.Length = srcid->parameters.Length; if (destid->parameters.Length > 0) { destid->parameters.Data = malloc(destid->parameters.Length); if (destid->parameters.Data == NULL) return (KMF_ERR_MEMORY); (void) memcpy(destid->parameters.Data, srcid->parameters.Data, destid->parameters.Length); } else { destid->parameters.Data = NULL; } return (ret); } static KMF_RETURN sign_cert(KMF_HANDLE_T handle, const KMF_DATA *SubjectCert, KMF_KEY_HANDLE *Signkey, KMF_OID *signature_oid, KMF_DATA *SignedCert) { KMF_X509_CERTIFICATE *subj_cert = NULL; KMF_DATA data_to_sign = { 0, NULL }; KMF_DATA signed_data = { 0, NULL }; KMF_RETURN ret = KMF_OK; KMF_ALGORITHM_INDEX algid; int i = 0; KMF_ATTRIBUTE attrlist[8]; if (!SignedCert) return (KMF_ERR_BAD_PARAMETER); SignedCert->Length = 0; SignedCert->Data = NULL; if (!SubjectCert) return (KMF_ERR_BAD_PARAMETER); if (!SubjectCert->Data || !SubjectCert->Length) return (KMF_ERR_BAD_PARAMETER); /* * Shortcut - just extract the already encoded TBS cert data from * the original data buffer. Since we haven't changed anything, * there is no need to re-encode it. */ ret = ExtractX509CertParts((KMF_DATA *)SubjectCert, &data_to_sign, NULL); if (ret != KMF_OK) { goto cleanup; } /* Estimate the signed data length generously */ signed_data.Length = data_to_sign.Length*2; signed_data.Data = calloc(1, signed_data.Length); if (!signed_data.Data) { ret = KMF_ERR_MEMORY; goto cleanup; } /* * If we got here OK, decode into a structure and then re-encode * the complete certificate. */ ret = DerDecodeSignedCertificate(SubjectCert, &subj_cert); if (ret != KMF_OK) { goto cleanup; } /* We are re-signing this cert, so clear out old signature data */ if (!IsEqualOid(&subj_cert->signature.algorithmIdentifier.algorithm, signature_oid)) { kmf_free_algoid(&subj_cert->signature.algorithmIdentifier); ret = set_algoid(&subj_cert->signature.algorithmIdentifier, signature_oid); if (ret != KMF_OK) goto cleanup; ret = set_algoid(&subj_cert->certificate.signature, signature_oid); if (ret) goto cleanup; /* Free the previous "data to be signed" block */ kmf_free_data(&data_to_sign); /* * We changed the cert (updated the signature OID), so we * need to re-encode it so the correct data gets signed. */ ret = DerEncodeTbsCertificate(&subj_cert->certificate, &data_to_sign); if (ret != KMF_OK) goto cleanup; } kmf_set_attr_at_index(attrlist, i, KMF_KEYSTORE_TYPE_ATTR, &Signkey->kstype, sizeof (KMF_KEYSTORE_TYPE)); i++; kmf_set_attr_at_index(attrlist, i, KMF_KEY_HANDLE_ATTR, Signkey, sizeof (KMF_KEY_HANDLE)); i++; kmf_set_attr_at_index(attrlist, i, KMF_DATA_ATTR, &data_to_sign, sizeof (KMF_DATA)); i++; kmf_set_attr_at_index(attrlist, i, KMF_OUT_DATA_ATTR, &signed_data, sizeof (KMF_DATA)); i++; kmf_set_attr_at_index(attrlist, i, KMF_OID_ATTR, signature_oid, sizeof (KMF_OID)); i++; /* Sign the data */ ret = kmf_sign_data(handle, i, attrlist); if (ret != KMF_OK) goto cleanup; algid = x509_algoid_to_algid(signature_oid); if (algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA) { /* ASN.1 encode ECDSA signature */ KMF_DATA signature; ret = DerEncodeECDSASignature(&signed_data, &signature); kmf_free_data(&signed_data); if (ret != KMF_OK) goto cleanup; subj_cert->signature.encrypted = signature; } else if (algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA) { /* * For DSA, kmf_sign_data() returns a 40-byte * signature. We must encode the signature correctly. */ KMF_DATA signature; ret = DerEncodeDSASignature(&signed_data, &signature); kmf_free_data(&signed_data); if (ret != KMF_OK) goto cleanup; subj_cert->signature.encrypted = signature; } else { ret = copy_data(&subj_cert->signature.encrypted, &signed_data); kmf_free_data(&signed_data); if (ret != KMF_OK) goto cleanup; } /* Now, re-encode the cert with the new signature */ ret = DerEncodeSignedCertificate(subj_cert, SignedCert); cleanup: /* Cleanup & return */ if (ret != KMF_OK) kmf_free_data(SignedCert); kmf_free_data(&data_to_sign); if (subj_cert != NULL) { kmf_free_signed_cert(subj_cert); free(subj_cert); } return (ret); } static KMF_RETURN verify_cert_with_key(KMF_HANDLE_T handle, KMF_DATA *derkey, const KMF_DATA *CertToBeVerified) { KMF_RETURN ret = KMF_OK; KMF_X509_CERTIFICATE *signed_cert = NULL; KMF_X509_SPKI spki; KMF_DATA data_to_verify = { 0, NULL }; KMF_DATA signed_data = { 0, NULL }; KMF_DATA signature = { 0, NULL }; KMF_ALGORITHM_INDEX algid; /* check the caller and do other setup for this SPI call */ if (handle == NULL || CertToBeVerified == NULL || derkey == NULL || derkey->Data == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&spki, 0, sizeof (KMF_X509_SPKI)); ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerified, &data_to_verify, &signed_data); if (ret != KMF_OK) goto cleanup; ret = DerDecodeSPKI(derkey, &spki); if (ret != KMF_OK) goto cleanup; /* Decode the signer cert so we can get the Algorithm data */ ret = DerDecodeSignedCertificate(CertToBeVerified, &signed_cert); if (ret != KMF_OK) return (ret); algid = x509_algoid_to_algid(CERT_SIG_OID(signed_cert)); if (algid == KMF_ALGID_NONE) return (KMF_ERR_BAD_ALGORITHM); if (algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA) { ret = DerDecodeDSASignature(&signed_data, &signature); if (ret != KMF_OK) goto cleanup; } else if (algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA) { ret = DerDecodeECDSASignature(&signed_data, &signature); if (ret != KMF_OK) goto cleanup; } else { signature.Data = signed_data.Data; signature.Length = signed_data.Length; } ret = PKCS_VerifyData(handle, algid, &spki, &data_to_verify, &signature); cleanup: if (data_to_verify.Data != NULL) free(data_to_verify.Data); if (signed_data.Data != NULL) free(signed_data.Data); if (signed_cert) { kmf_free_signed_cert(signed_cert); free(signed_cert); } if (algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA || algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA) { free(signature.Data); } kmf_free_algoid(&spki.algorithm); kmf_free_data(&spki.subjectPublicKey); return (ret); } /* * Use a signer cert to verify another certificate's signature. * This code forces the use of the PKCS11 mechanism for the verify * operation for the Cryptographic Framework's FIPS-140 boundary. */ static KMF_RETURN verify_cert_with_cert(KMF_HANDLE_T handle, const KMF_DATA *CertToBeVerifiedData, const KMF_DATA *SignerCertData) { KMF_RETURN ret = KMF_OK; KMF_X509_CERTIFICATE *SignerCert = NULL; KMF_X509_CERTIFICATE *ToBeVerifiedCert = NULL; KMF_DATA data_to_verify = { 0, NULL }; KMF_DATA signed_data = { 0, NULL }; KMF_DATA signature; KMF_ALGORITHM_INDEX algid; KMF_POLICY_RECORD *policy; if (handle == NULL || !CertToBeVerifiedData || !CertToBeVerifiedData->Data || !CertToBeVerifiedData->Length) return (KMF_ERR_BAD_PARAMETER); if (!SignerCertData || !SignerCertData->Data || !SignerCertData->Length) return (KMF_ERR_BAD_PARAMETER); policy = handle->policy; /* Make sure the signer has proper key usage bits */ ret = check_key_usage(handle, SignerCertData, KMF_KU_SIGN_CERT); if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0) ret = KMF_OK; if (ret != KMF_OK) return (ret); /* Decode the cert into parts for verification */ ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerifiedData, &data_to_verify, &signed_data); if (ret != KMF_OK) goto cleanup; /* Decode the to-be-verified cert so we know what algorithm to use */ ret = DerDecodeSignedCertificate(CertToBeVerifiedData, &ToBeVerifiedCert); if (ret != KMF_OK) goto cleanup; algid = x509_algoid_to_algid(CERT_SIG_OID(ToBeVerifiedCert)); if (algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA) { ret = DerDecodeDSASignature(&signed_data, &signature); if (ret != KMF_OK) goto cleanup; } else if (algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA) { ret = DerDecodeECDSASignature(&signed_data, &signature); if (ret != KMF_OK) goto cleanup; } else { signature.Data = signed_data.Data; signature.Length = signed_data.Length; } ret = DerDecodeSignedCertificate(SignerCertData, &SignerCert); if (ret != KMF_OK) goto cleanup; /* * Force use of PKCS11 API for kcfd/libelfsign. This is * required for the Cryptographic Framework's FIPS-140 boundary. */ ret = PKCS_VerifyData(handle, algid, &SignerCert->certificate.subjectPublicKeyInfo, &data_to_verify, &signature); cleanup: kmf_free_data(&data_to_verify); kmf_free_data(&signed_data); if (SignerCert) { kmf_free_signed_cert(SignerCert); free(SignerCert); } if (ToBeVerifiedCert) { kmf_free_signed_cert(ToBeVerifiedCert); free(ToBeVerifiedCert); } if (algid == KMF_ALGID_SHA1WithDSA || algid == KMF_ALGID_SHA256WithDSA || algid == KMF_ALGID_SHA1WithECDSA || algid == KMF_ALGID_SHA256WithECDSA || algid == KMF_ALGID_SHA384WithECDSA || algid == KMF_ALGID_SHA512WithECDSA) { free(signature.Data); } return (ret); }