/*
 * Determine if specified identity's cert's issuer and serial number match the
 * provided issuer and serial number. Returns nonzero on match, else returns zero.
 */
static int pkinit_issuer_sn_match(
    SecIdentityRef idRef,
    const CSSM_DATA *matchIssuerSerial)
{
    OSStatus ortn;
    SecCertificateRef certRef = NULL;
    CSSM_DATA INIT_CDATA(certIssuerSerial);
    int ourRtn = 0;

    assert(idRef != NULL);
    assert(matchIssuerSerial != NULL);

    /* Get this cert's issuer/serial number */
    ortn = SecIdentityCopyCertificate(idRef, &certRef);
    if(ortn) {
        pkiCssmErr("SecIdentityCopyCertificate", ortn);
        return 0;
    }
    /* subsequent errors to errOut: */
    ortn = pkinit_get_cert_issuer_sn(certRef, &certIssuerSerial);
    if(ortn) {
        pkiCssmErr("SecIdentityCopyCertificate", ortn);
        goto errOut;
    }
    ourRtn = pkiCompareCssmData(matchIssuerSerial, &certIssuerSerial) ? 1 : 0;
errOut:
    if(certRef != NULL) {
        CFRelease(certRef);
    }
    if(certIssuerSerial.Data != NULL) {
        free(certIssuerSerial.Data);
    }
    return ourRtn;
}
/*
 * Given a certificate, obtain the DER-encoded issuer and serial number. Result
 * is mallocd and must be freed by caller.
 */
static OSStatus pkinit_get_cert_issuer_sn(
    SecCertificateRef certRef,
    CSSM_DATA *issuerSerial)            /* mallocd and RETURNED */
{
    OSStatus ortn;
    CSSM_DATA certData;
    krb5_data INIT_KDATA(issuerSerialKrb);
    krb5_data certDataKrb;
    krb5_error_code krtn;

    assert(certRef != NULL);
    assert(issuerSerial != NULL);

    ortn = SecCertificateGetData(certRef, &certData);
    if(ortn) {
        pkiCssmErr("SecCertificateGetData", ortn);
        return ortn;
    }
    PKI_CSSM_TO_KRB_DATA(&certData, &certDataKrb);
    krtn = krb5int_pkinit_get_issuer_serial(&certDataKrb, &issuerSerialKrb);
    if(krtn) {
        return CSSMERR_CL_INVALID_DATA;
    }
    PKI_KRB_TO_CSSM_DATA(&issuerSerialKrb, issuerSerial);
    return noErr;
}
/*
 * Store the specified certificate (or, more likely, some platform-dependent
 * reference to it) as the specified principal's signing certificate. Passing
 * in NULL for the client_cert has the effect of deleting the relevant entry
 * in the cert storage.
 */
krb5_error_code krb5_pkinit_set_client_cert_from_signing_cert(
    const char                  *principal,     /* full principal string */
    krb5_pkinit_signing_cert_t  client_cert)
{
    SecIdentityRef idRef = (SecIdentityRef)client_cert;
    SecCertificateRef certRef = NULL;
    OSStatus ortn;
    krb5_error_code ourRtn = 0;

    if (NULL != idRef) {
        if (CFGetTypeID(idRef) != SecIdentityGetTypeID()) {
            ourRtn = KRB5KRB_ERR_GENERIC;
            goto fin;
        }
        /* Get the cert */
        ortn = SecIdentityCopyCertificate(idRef, &certRef);
        if (ortn) {
            pkiCssmErr("SecIdentityCopyCertificate", ortn);
            ourRtn = KRB5KRB_ERR_GENERIC;
            goto fin;
        }
    }
    ourRtn = krb5_pkinit_set_client_cert(principal, (krb5_pkinit_cert_t)certRef);
fin:
    if (certRef)
        CFRelease(certRef);
    return ourRtn;
}
/*
 * Obtain signing cert for specified principal. On successful return,
 * caller must eventually release the cert with krb5_pkinit_release_cert().
 */
krb5_error_code krb5_pkinit_get_client_cert(
    const char                  *principal,     /* full principal string */
    krb5_pkinit_signing_cert_t  *client_cert)
{
    CFDataRef issuerSerial = NULL;
    CSSM_DATA issuerSerialData;
    SecIdentityRef idRef = NULL;
    OSStatus ortn;
    CFDictionaryRef theDict = NULL;
    CFStringRef cfPrinc = NULL;
    krb5_error_code ourRtn = 0;

    if(principal == NULL) {
        return KRB5_PRINC_NOMATCH;
    }

    /* Is there a stored preference for PKINIT certs for this user? */
    ortn = pkinit_get_pref_dict(&theDict);
    if(ortn) {
        return KRB5_PRINC_NOMATCH;
    }

    /* Entry in the dictionary for specified principal? */
    cfPrinc = CFStringCreateWithCString(NULL, principal,
                                        kCFStringEncodingASCII);
    issuerSerial = (CFDataRef)CFDictionaryGetValue(theDict, cfPrinc);
    CFRelease(cfPrinc);
    if(issuerSerial == NULL) {
        pkiDebug("krb5_pkinit_get_client_cert: no identity found\n");
        ourRtn = KRB5_PRINC_NOMATCH;
        goto errOut;
    }
    if(CFGetTypeID(issuerSerial) != CFDataGetTypeID()) {
        pkiDebug("krb5_pkinit_get_client_cert: bad kPkinitClientCertKey value\n");
        ourRtn = KRB5_PRINC_NOMATCH;
        goto errOut;
    }

    issuerSerialData.Data = (uint8 *)CFDataGetBytePtr(issuerSerial);
    issuerSerialData.Length = CFDataGetLength(issuerSerial);

    /* find a cert with that issuer/serial number in default search list */
    ortn = pkinit_search_ident(NULL, CSSM_KEYUSE_SIGN | CSSM_KEYUSE_ENCRYPT,
                               &issuerSerialData, &idRef);
    if(ortn) {
        pkiDebug("krb5_pkinit_get_client_cert: no identity found!\n");
        pkiCssmErr("pkinit_search_ident", ortn);
        ourRtn = KRB5_PRINC_NOMATCH;
    }
    else {
        *client_cert = (krb5_pkinit_signing_cert_t)idRef;
    }
errOut:
    if(theDict) {
        CFRelease(theDict);
    }
    return ourRtn;
}
Exemple #5
0
/*
 * Convert CFArray of SecCertificateRefs to a mallocd array of krb5_datas.
 */
static krb5_error_code pkiCertArrayToKrb5Data(
    CFArrayRef  cf_certs,
    unsigned	*num_all_certs,
    krb5_data   **all_certs)	
{
    CFIndex num_certs;
    krb5_data *allCerts = NULL;
    krb5_error_code krtn = 0;
    unsigned dex;
    
    if(cf_certs == NULL) {
	*all_certs = NULL;
	return 0;
    }
    num_certs = CFArrayGetCount(cf_certs);
    *num_all_certs = (unsigned)num_certs;
    if(num_certs == 0) {
	*all_certs = NULL;
	return 0;
    }
    allCerts = (krb5_data *)malloc(sizeof(krb5_data) * num_certs);
    if(allCerts == NULL) {
	return ENOMEM;
    }
    for(dex=0; dex<num_certs; dex++) {	
	CSSM_DATA cert_data;
	OSStatus ortn;
	SecCertificateRef sec_cert;
	
	sec_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cf_certs, dex);
	ortn = SecCertificateGetData(sec_cert, &cert_data);
	if(ortn) {
	    pkiCssmErr("SecCertificateGetData", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    break;
	}
	krtn = pkiCssmDataToKrb5Data(&cert_data, &allCerts[dex]);
	if(krtn) {
	    break;
	}
    }
    if(krtn) {
	if(allCerts) {
	    free(allCerts);
	}
    }
    else {
	*all_certs = allCerts;
    }
    return krtn;
}
/*
 * In Mac OS terms, get the keychain on which a given identity resides.
 */
static krb5_error_code pkinit_cert_to_db(
    krb5_pkinit_signing_cert_t   idRef,
    krb5_pkinit_cert_db_t        *dbRef)
{
    SecKeychainRef kcRef = NULL;
    SecKeyRef keyRef = NULL;
    OSStatus ortn;

    /* that's an identity - get the associated key's keychain */
    ortn = SecIdentityCopyPrivateKey((SecIdentityRef)idRef, &keyRef);
    if(ortn) {
        pkiCssmErr("SecIdentityCopyPrivateKey", ortn);
        return ortn;
    }
    ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
    if(ortn) {
        pkiCssmErr("SecKeychainItemCopyKeychain", ortn);
    }
    else {
        *dbRef = (krb5_pkinit_cert_db_t)kcRef;
    }
    CFRelease(keyRef);
    return ortn;
}
Exemple #7
0
/*
 * Cook up a SecCertificateRef from a krb5_data.
 */
static OSStatus pkiKrb5DataToSecCert(
    const krb5_data *rawCert,
    SecCertificateRef *secCert)     /* RETURNED */
{
    CSSM_DATA certData;
    OSStatus ortn;
    
    assert((rawCert != NULL) && (secCert != NULL));
    
    certData.Data = (uint8 *)rawCert->data;
    certData.Length = rawCert->length;
    ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, 
	CSSM_CERT_ENCODING_DER, secCert);
    if(ortn) {
	pkiCssmErr("SecCertificateCreateFromData", ortn);
    }
    return ortn;
}
/*
 * Search specified keychain/array/NULL (NULL meaning the default search list) for
 * an Identity matching specified key usage and optional Issuer/Serial number.
 * If issuer/serial is specified and no identities match, or if no identities found
 * matching specified Key usage, errSecItemNotFound is returned.
 *
 * Caller must CFRelease a non-NULL returned idRef.
 */
static OSStatus pkinit_search_ident(
    CFTypeRef           keychainOrArray,
    CSSM_KEYUSE         keyUsage,
    const CSSM_DATA     *issuerSerial,  /* optional */
    SecIdentityRef      *foundId)       /* RETURNED */
{
    OSStatus ortn;
    SecIdentityRef idRef = NULL;
    SecIdentitySearchRef srchRef = NULL;

    ortn = SecIdentitySearchCreate(keychainOrArray, keyUsage, &srchRef);
    if(ortn) {
        pkiCssmErr("SecIdentitySearchCreate", ortn);
        return ortn;
    }
    do {
        ortn = SecIdentitySearchCopyNext(srchRef, &idRef);
        if(ortn != noErr) {
            break;
        }
        if(issuerSerial == NULL) {
            /* no match needed, we're done - this is the KDC cert case */
            break;
        }
        else if(pkinit_issuer_sn_match(idRef, issuerSerial)) {
            /* match, we're done */
            break;
        }
        /* finished with this one */
        CFRelease(idRef);
        idRef = NULL;
    } while(ortn == noErr);

    CFRelease(srchRef);
    if(idRef == NULL) {
        return errSecItemNotFound;
    }
    else {
        *foundId = idRef;
        return noErr;
    }
}
/*
 * Obtain the KDC signing cert, with optional CA and specific cert specifiers.
 * CAs and cert specifiers are in the form of DER-encoded issuerAndSerialNumbers.
 *
 * The client_spec argument is typically provided by the client as kdcPkId.
 */
krb5_error_code krb5_pkinit_get_kdc_cert(
    krb5_ui_4                   num_trusted_CAs,    /* sizeof *trusted_CAs */
    krb5_data                   *trusted_CAs,       /* optional */
    krb5_data                   *client_spec,       /* optional */
    krb5_pkinit_signing_cert_t *kdc_cert)
{
    SecIdentityRef idRef = NULL;
    OSStatus ortn;
    krb5_error_code ourRtn = 0;

    /* OS X: trusted_CAs and client_spec ignored */

    ortn = SecIdentityCopySystemIdentity(kSecIdentityDomainKerberosKDC,
                                         &idRef, NULL);
    if(ortn) {
        pkiCssmErr("SecIdentityCopySystemIdentity", ortn);
        return KRB5_PRINC_NOMATCH;
    }
    *kdc_cert = (krb5_pkinit_signing_cert_t)idRef;
    return ourRtn;
}
Exemple #10
0
/*
 * Parse a ContentInfo as best we can. All return fields are optional.
 * If signer_cert_status is NULL on entry, NO signature or cert evaluation 
 * will be performed. 
 */
krb5_error_code krb5int_pkinit_parse_cms_msg(
    const krb5_data	    *content_info,
    krb5_pkinit_cert_db_t   cert_db,		/* may be required for SignedData */
    krb5_boolean	    is_client_msg,	/* TRUE : msg is from client */
    krb5_boolean	    *is_signed,		/* RETURNED */
    krb5_boolean	    *is_encrypted,	/* RETURNED */
    krb5_data		    *raw_data,		/* RETURNED */
    krb5int_cms_content_type *inner_content_type,/* Returned, ContentType of */
						/*    EncapsulatedData */
    krb5_data		    *signer_cert,	/* RETURNED */
    krb5int_cert_sig_status *signer_cert_status,/* RETURNED */
    unsigned		    *num_all_certs,	/* size of *all_certs RETURNED */
    krb5_data		    **all_certs)	/* entire cert chain RETURNED */
{
    SecPolicySearchRef policy_search = NULL;
    SecPolicyRef policy = NULL;
    OSStatus ortn;
    krb5_error_code krtn = 0;
    CMSDecoderRef decoder = NULL;
    size_t num_signers;
    CMSSignerStatus signer_status;
    OSStatus cert_verify_status;
    CFArrayRef cf_all_certs = NULL;
    int msg_is_signed = 0;
    
    if(content_info == NULL) {
	pkiDebug("krb5int_pkinit_parse_cms_msg: no ContentInfo\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    ortn = CMSDecoderCreate(&decoder);
    if(ortn) {
	return ENOMEM;
    }
    ortn = CMSDecoderUpdateMessage(decoder, content_info->data, content_info->length);
    if(ortn) {
	/* no verify yet, must be bad message */
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }
    ortn = CMSDecoderFinalizeMessage(decoder);
    if(ortn) {
	pkiCssmErr("CMSDecoderFinalizeMessage", ortn);
	krtn = KRB5_PARSE_MALFORMED;
	goto errOut;
    }

    /* expect zero or one signers */
    ortn = CMSDecoderGetNumSigners(decoder, &num_signers);
    switch(num_signers) {
	case 0:
	    msg_is_signed = 0;
	    break;
	case 1:
	    msg_is_signed = 1;
	    break;
	default:
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
    }

    /*
     * We need a cert verify policy even if we're not actually evaluating 
     * the cert due to requirements in libsecurity_smime.
     */
    ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
	is_client_msg ? &CSSMOID_APPLE_TP_PKINIT_CLIENT : &CSSMOID_APPLE_TP_PKINIT_SERVER, 
	NULL, &policy_search);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCreate", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    ortn = SecPolicySearchCopyNext(policy_search, &policy);
    if(ortn) {
	pkiCssmErr("SecPolicySearchCopyNext", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    
    /* get some basic status that doesn't need heavyweight evaluation */
    if(msg_is_signed) {
	if(is_signed) {
	    *is_signed = TRUE;
	}
	if(inner_content_type) {
	    CSSM_OID ec_oid = {0, NULL};
	    CFDataRef ec_data = NULL;
	    
	    krb5int_cms_content_type ctype;
	    
	    ortn = CMSDecoderCopyEncapsulatedContentType(decoder, &ec_data);
	    if(ortn || (ec_data == NULL)) {
		pkiCssmErr("CMSDecoderCopyEncapsulatedContentType", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ec_oid.Data = (uint8 *)CFDataGetBytePtr(ec_data);
	    ec_oid.Length = CFDataGetLength(ec_data);
	    if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_Data)) {
		ctype = ECT_Data;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_SignedData)) {
		ctype = ECT_SignedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EnvelopedData)) {
		ctype = ECT_EnvelopedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EncryptedData)) {
		ctype = ECT_EncryptedData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_AUTH_DATA)) {
		ctype = ECT_PkAuthData;
	    }
	    else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_RKEY_DATA)) {
		ctype = ECT_PkReplyKeyKata;
	    }
	    else {
		ctype = ECT_Other;
	    }
	    *inner_content_type = ctype;
	    CFRelease(ec_data);
	}
	
	/* 
	 * Get SignedData's certs if the caller wants them
	 */
	if(all_certs) {	    
	    ortn = CMSDecoderCopyAllCerts(decoder, &cf_all_certs);
	    if(ortn) {
		pkiCssmErr("CMSDecoderCopyAllCerts", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiCertArrayToKrb5Data(cf_all_certs, num_all_certs, all_certs);
	    if(krtn) {
		goto errOut;
	    }
	}
	
	/* optional signer cert */
	if(signer_cert) {
	    SecCertificateRef sec_signer_cert = NULL;
	    CSSM_DATA cert_data;

	    ortn = CMSDecoderCopySignerCert(decoder, 0, &sec_signer_cert);
	    if(ortn) {
		/* should never happen if it's signed */
		pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    ortn = SecCertificateGetData(sec_signer_cert, &cert_data);
	    if(ortn) {
		pkiCssmErr("SecCertificateGetData", ortn);
		CFRelease(sec_signer_cert);
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	    }
	    krtn = pkiDataToKrb5Data(cert_data.Data, cert_data.Length, signer_cert);
	    CFRelease(sec_signer_cert);
	    if(krtn) {
		goto errOut;
	    }
	}
    }
    else {
	/* not signed */
	if(is_signed) {
	    *is_signed = FALSE;
	}
	if(inner_content_type) {
	    *inner_content_type = ECT_Other;
	}
	if(signer_cert) {
	    signer_cert->data = NULL;
	    signer_cert->length = 0;
	}
	if(signer_cert_status) {
	    *signer_cert_status = pki_not_signed;
	}
	if(num_all_certs) {
	    *num_all_certs = 0;
	}
	if(all_certs) {
	    *all_certs = NULL;
	}
    }
    if(is_encrypted) {
	Boolean bencr;
	ortn = CMSDecoderIsContentEncrypted(decoder, &bencr);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_CRYPTO_INTERNAL;
	    goto errOut;
	}
	*is_encrypted = bencr ? TRUE : FALSE;
    }
    
    /* 
     * Verify signature and cert. The actual verify operation is optional,
     * per our signer_cert_status argument, but we do this anyway if we need
     * to get the signer cert.
     */
    if((signer_cert_status != NULL) || (signer_cert != NULL)) {
	
	ortn = CMSDecoderCopySignerStatus(decoder, 
	    0,					    /* signerIndex */
	    policy,
	    signer_cert_status ? TRUE : FALSE,	    /* evaluateSecTrust */
	    &signer_status,
	    NULL,				    /* secTrust - not needed */
	    &cert_verify_status);
	if(ortn) {
	    /* gross error - subsequent processing impossible */
	    pkiCssmErr("CMSDecoderCopySignerStatus", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
    }
    /* obtain & return status */
    if(signer_cert_status) {
	*signer_cert_status = pkiInferSigStatus(signer_status, cert_verify_status);
    }
    
    /* finally, the payload */
    if(raw_data) {
	CFDataRef cf_content = NULL;
	
	ortn = CMSDecoderCopyContent(decoder, &cf_content);
	if(ortn) {
	    pkiCssmErr("CMSDecoderCopyContent", ortn);
	    krtn = KRB5_PARSE_MALFORMED;
	    goto errOut;
	}
	krtn = pkiCfDataToKrb5Data(cf_content, raw_data);
	CFRELEASE(cf_content);
    }
errOut:
    CFRELEASE(policy_search);
    CFRELEASE(policy);
    CFRELEASE(cf_all_certs);
    CFRELEASE(decoder);
    return krtn;
}
Exemple #11
0
/*
 * Create a CMS message: either encrypted (EnvelopedData), signed 
 * (SignedData), or both (EnvelopedData(SignedData(content)).
 *
 * The message is signed iff signing_cert is non-NULL.
 * The message is encrypted iff recip_cert is non-NULL.
 *
 * The content_type argument specifies to the eContentType
 * for a SignedData's EncapsulatedContentInfo. 
 */
krb5_error_code krb5int_pkinit_create_cms_msg(
    const krb5_data		*content,	/* Content */
    krb5_pkinit_signing_cert_t	signing_cert,	/* optional: signed by this cert */
    const krb5_data		*recip_cert,	/* optional: encrypted with this cert */
    krb5int_cms_content_type	content_type,   /* OID for EncapsulatedData */
    krb5_ui_4			num_cms_types,	/* optional, unused here */
    const krb5int_algorithm_id	*cms_types,	/* optional, unused here */
    krb5_data			*content_info)  /* contents mallocd and RETURNED */
{
    krb5_error_code krtn;
    OSStatus ortn;
    SecCertificateRef sec_recip = NULL;
    CFDataRef cf_content = NULL;
    const CSSM_OID *eContentOid = NULL;
    
    if((signing_cert == NULL) && (recip_cert == NULL)) {
	/* must have one or the other */
	pkiDebug("krb5int_pkinit_create_cms_msg: no signer or recipient\n");
	return KRB5_CRYPTO_INTERNAL;
    }
    
    /* 
     * Optional signer cert. Note signing_cert, if present, is 
     * a SecIdentityRef. 
     */
    if(recip_cert) {
	if(pkiKrb5DataToSecCert(recip_cert, &sec_recip)) {
	    krtn = ASN1_BAD_FORMAT;
	    goto errOut;
	}
    }
    
    /* optional eContentType */
    if(signing_cert) {
	switch(content_type) {
	    case ECT_PkAuthData:
		eContentOid = &_CSSMOID_PKINIT_AUTH_DATA;
		break;
	    case ECT_PkReplyKeyKata:
		eContentOid = &_CSSMOID_PKINIT_RKEY_DATA;
		break;
	    case ECT_Data:
		/* the only standard/default case we allow */
		break;
	    default:
		/* others: no can do */
		pkiDebug("krb5int_pkinit_create_cms_msg: bad contentType\n");
		krtn = KRB5_CRYPTO_INTERNAL;
		goto errOut;
	}
    }
    
    /* GO */
    ortn = CMSEncode((SecIdentityRef)signing_cert, sec_recip,
	eContentOid, 
	FALSE,		/* detachedContent */
	kCMSAttrNone,	/* no signed attributes that I know of */
	content->data, content->length,
	&cf_content);
    if(ortn) {
	pkiCssmErr("CMSEncode", ortn);
	krtn = KRB5_CRYPTO_INTERNAL;
	goto errOut;
    }
    krtn = pkiCfDataToKrb5Data(cf_content, content_info);
errOut:
    CFRELEASE(sec_recip);
    CFRELEASE(cf_content);
    return krtn;
}
/*
 * Parse PA-PK-AS-REP message. Optionally evaluates the message's certificate chain.
 * Optionally returns various components.
 */
krb5_error_code krb5int_pkinit_as_rep_parse(
    krb5_context                context,
    const krb5_data             *as_rep,
    krb5_pkinit_signing_cert_t   client_cert,   /* required */
    krb5_keyblock               *key_block,     /* RETURNED */
    krb5_checksum               *checksum,      /* checksum of corresponding AS-REQ */
                                                /*   contents mallocd and RETURNED */
    krb5int_cert_sig_status     *cert_status,   /* RETURNED */

    /*
     * Cert fields, all optionally RETURNED.
     *
     * signer_cert is the full X.509 leaf cert from the incoming SignedData.
     * all_certs is an array of all of the certs in the incoming SignedData,
     *    in full X.509 form.
     */
    krb5_data               *signer_cert,   /* content mallocd */
    unsigned                *num_all_certs, /* sizeof *all_certs */
    krb5_data               **all_certs)    /* krb5_data's and their content mallocd */
{
    krb5_data reply_key_pack = {0, 0, NULL};
    krb5_error_code krtn;
    krb5_data enc_key_pack = {0, 0, NULL};
    krb5_data dh_signed_data = {0, 0, NULL};
    krb5int_cms_content_type content_type;
    krb5_pkinit_cert_db_t cert_db = NULL;
    krb5_boolean is_signed;
    krb5_boolean is_encrypted;

    assert((as_rep != NULL) && (checksum != NULL) &&
           (key_block != NULL) && (cert_status != NULL));

    /*
     * Decode the top-level PA-PK-AS-REP
     */
    krtn = krb5int_pkinit_pa_pk_as_rep_decode(as_rep, &dh_signed_data, &enc_key_pack);
    if(krtn) {
        pkiCssmErr("krb5int_pkinit_pa_pk_as_rep_decode", krtn);
        return krtn;
    }
    if(dh_signed_data.data) {
        /* not for this implementation... */
        pkiDebug("krb5int_pkinit_as_rep_parse: unexpected dh_signed_data\n");
        krtn = ASN1_BAD_FORMAT;
        goto err_out;
    }
    if(enc_key_pack.data == NULL) {
        /* REQUIRED for this implementation... */
        pkiDebug("krb5int_pkinit_as_rep_parse: no enc_key_pack\n");
        krtn = ASN1_BAD_FORMAT;
        goto err_out;
    }

    krtn = krb5_pkinit_get_client_cert_db(NULL, client_cert, &cert_db);
    if(krtn) {
        pkiDebug("krb5int_pkinit_as_rep_parse: error in krb5_pkinit_get_client_cert_db\n");
        goto err_out;
    }

    /*
     * enc_key_pack is an EnvelopedData(SignedData(keyPack), encrypted
     * with our cert (which krb5int_pkinit_parse_content_info() finds
     * implicitly).
     */
    krtn = krb5int_pkinit_parse_cms_msg(&enc_key_pack, cert_db, FALSE,
                                        &is_signed, &is_encrypted,
                                        &reply_key_pack, &content_type,
                                        signer_cert, cert_status, num_all_certs, all_certs);
    if(krtn) {
        pkiDebug("krb5int_pkinit_as_rep_parse: error decoding EnvelopedData\n");
        goto err_out;
    }
    if(!is_encrypted || !is_signed) {
        pkiDebug("krb5int_pkinit_as_rep_parse: not signed and encrypted!\n");
        krtn = KRB5_PARSE_MALFORMED;
        goto err_out;
    }
    if(content_type != ECT_PkReplyKeyKata) {
        pkiDebug("replyKeyPack eContentType %d!\n", (int)content_type);
        krtn = KRB5_PARSE_MALFORMED;
        goto err_out;
    }

    /*
     * Finally, decode that inner content as the ReplyKeyPack which contains
     * the actual key and nonce
     */
    krtn = krb5int_pkinit_reply_key_pack_decode(&reply_key_pack, key_block, checksum);
    if(krtn) {
        pkiDebug("krb5int_pkinit_as_rep_parse: error decoding ReplyKeyPack\n");
    }

err_out:
    /* free temp mallocd data that we didn't pass back to caller */
    if(reply_key_pack.data) {
        free(reply_key_pack.data);
    }
    if(enc_key_pack.data) {
        free(enc_key_pack.data);
    }
    if(dh_signed_data.data) {
        free(dh_signed_data.data);
    }
    if(cert_db) {
        krb5_pkinit_release_cert_db(cert_db);
    }
    return krtn;
}
/*
 * Given a DER encoded certificate, obtain the associated IssuerAndSerialNumber.
 */
krb5_error_code krb5int_pkinit_get_issuer_serial(
    const krb5_data *cert,
    krb5_data       *issuer_and_serial)
{
    CSSM_HANDLE cacheHand = 0;
    CSSM_RETURN crtn = CSSM_OK;
    CSSM_DATA certData = { cert->length, (uint8 *)cert->data };
    CSSM_HANDLE resultHand = 0;
    CSSM_DATA_PTR derIssuer = NULL;
    CSSM_DATA_PTR serial;
    krb5_data krb_serial;
    krb5_data krb_issuer;
    uint32 numFields;
    krb5_error_code ourRtn = 0;

    CSSM_CL_HANDLE clHand = pkiClStartup();
    if(clHand == 0) {
	return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
    }
    /* subsequent errors to errOut: */

    crtn = CSSM_CL_CertCache(clHand, &certData, &cacheHand);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertCache", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }

    /* obtain the two fields; issuer is DER encoded */
    crtn = CSSM_CL_CertGetFirstCachedFieldValue(clHand, cacheHand,
	&CSSMOID_X509V1IssuerNameStd, &resultHand, &numFields, &derIssuer);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertGetFirstCachedFieldValue(issuer)", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }
    crtn = CSSM_CL_CertGetFirstCachedFieldValue(clHand, cacheHand,
	&CSSMOID_X509V1SerialNumber, &resultHand, &numFields, &serial);
    if(crtn) {
	pkiCssmErr("CSSM_CL_CertGetFirstCachedFieldValue(serial)", crtn);
	ourRtn = ASN1_PARSE_ERROR;
	goto errOut;
    }
    PKI_CSSM_TO_KRB_DATA(derIssuer, &krb_issuer);
    PKI_CSSM_TO_KRB_DATA(serial, &krb_serial);
    ourRtn = krb5int_pkinit_issuer_serial_encode(&krb_issuer, &krb_serial, issuer_and_serial);

errOut:
    if(derIssuer) {
	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerNameStd, derIssuer);
    }
    if(serial) {
	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SerialNumber, serial);
    }
    if(cacheHand) {
	CSSM_CL_CertAbortCache(clHand, cacheHand);
    }
    if(clHand) {
	pkiClDetachUnload(clHand);
    }
    return ourRtn;
}