static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader) { CssmClient::CL cl(gGuidAppleX509CL); const CssmData cert(const_cast<UInt8 *>(CFDataGetBytePtr(certificate)), CFDataGetLength(certificate)); CSSM_KEY_PTR key; if (CSSM_RETURN rv = CSSM_CL_CertGetKeyInfo(cl->handle(), &cert, &key)) CssmError::throwMe(rv); Security::CssmClient::CSP fCSP(gGuidAppleCSP); // Set it up so the cl is used to free key and key->KeyData // CssmAutoData _keyData(cl.allocator()); // _keyData.set(CssmData::overlay(key->KeyData)); CssmAutoData _key(cl.allocator()); _key.set(reinterpret_cast<uint8 *>(key), sizeof(*key)); CssmClient::Key cKey(fCSP, *key); /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the * associated key blob. * Key is specified in CSSM_CSP_CreatePassThroughContext. * Hash is allocated by the CSP, in the App's memory, and returned * in *outData. */ CssmClient::PassThrough passThrough(fCSP); passThrough.key(key); void *outData; passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData); CssmData *cssmData = reinterpret_cast<CssmData *>(outData); assert(cssmData->Length <= sizeof(outHeader.publicKeyHash)); outHeader.publicKeyHashSize = (uint32_t)cssmData->Length; memcpy(outHeader.publicKeyHash, cssmData->Data, cssmData->Length); fCSP.allocator().free(cssmData->Data); fCSP.allocator().free(cssmData); /* Now encrypt the blob with the public key. */ CssmClient::Encrypt encrypt(fCSP, key->KeyHeader.AlgorithmId); encrypt.key(cKey); CssmData clearBuf(outHeader.encryptedBlob, sizeof(outHeader.encryptedBlob)); CssmAutoData remData(fCSP.allocator()); encrypt.padding(CSSM_PADDING_PKCS1); outHeader.encryptedBlobSize = (uint32_t)encrypt.encrypt(inBlob, clearBuf, remData.get()); if (outHeader.encryptedBlobSize > sizeof(outHeader.encryptedBlob)) secinfo("FDERecovery", "encodePrivateKeyHeader: encrypted blob too big: %d", outHeader.encryptedBlobSize); }
OSStatus parseIncomingCerts( SSLContext *ctx, CFArrayRef certs, SSLCertificate **destCert, /* &ctx->{localCert,encryptCert} */ CSSM_KEY_PTR *pubKey, /* &ctx->signingPubKey, etc. */ SecKeyRef *privKeyRef, /* &ctx->signingPrivKeyRef, etc. */ CSSM_ALGORITHMS *signerAlg) /* optional */ { CFIndex numCerts; CFIndex cert; SSLCertificate *certChain = NULL; SSLCertificate *thisSslCert; OSStatus ortn; SecIdentityRef identity; SecCertificateRef certRef; SecKeyRef keyRef; CSSM_DATA certData; CSSM_CL_HANDLE clHand; // carefully derive from a SecCertificateRef CSSM_RETURN crtn; CSSM_KEY_PTR *pubKey; SecKeyRef *privKeyRef; assert(ctx != NULL); assert(destCert != NULL); /* though its referent may be NULL */ assert(sslPubKey != NULL); assert(sslPrivKeyRef != NULL); pubKey = &sslPubKey->key; privKeyRef = &sslPrivKey->key; sslDeleteCertificateChain(*destCert, ctx); *destCert = NULL; *pubKey = NULL; *privKeyRef = NULL; if(certs == NULL) { sslErrorLog("parseIncomingCerts: NULL incoming cert array\n"); return errSSLBadCert; } numCerts = CFArrayGetCount(certs); if(numCerts == 0) { sslErrorLog("parseIncomingCerts: empty incoming cert array\n"); return errSSLBadCert; } /* * Certs[0] is an SecIdentityRef from which we extract subject cert, * privKeyRef, pubKey. * * 1. ensure the first element is a SecIdentityRef. */ identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0); if(identity == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (1)\n"); return paramErr; } if(CFGetTypeID(identity) != SecIdentityGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (2)\n"); return paramErr; } /* * 2. Extract cert, keys and convert to local format. */ ortn = SecIdentityCopyCertificate(identity, &certRef); if(ortn) { sslErrorLog("parseIncomingCerts: bad cert array (3)\n"); return ortn; } ortn = secCertToSslCert(ctx, certRef, &thisSslCert); if(ortn) { sslErrorLog("parseIncomingCerts: bad cert array (4)\n"); return ortn; } /* enqueue onto head of cert chain */ thisSslCert->next = certChain; certChain = thisSslCert; if(signerAlg != NULL) { ortn = sslCertSignerAlg(certRef, signerAlg); if(ortn) { return ortn; } } /* fetch private key from identity */ ortn = SecIdentityCopyPrivateKey(identity, &keyRef); if(ortn) { sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n", (int)ortn); return ortn; } *privKeyRef = keyRef; /* obtain public key from cert */ ortn = SecCertificateGetCLHandle(certRef, &clHand); if(ortn) { sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n", (int)ortn); return ortn; } certData.Data = thisSslCert->derCert.data; certData.Length = thisSslCert->derCert.length; crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, pubKey); if(crtn) { sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n"); return (OSStatus)crtn; } /* OK, that's the subject cert. Fetch optional remaining certs. */ /* * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates. * Incoming certs have root last; SSLCertificate chain has root * first. */ for(cert=1; cert<numCerts; cert++) { certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, cert); if(certRef == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (5)\n"); return paramErr; } if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (6)\n"); return paramErr; } /* Extract cert, convert to local format. */ ortn = secCertToSslCert(ctx, certRef, &thisSslCert); if(ortn) { sslErrorLog("parseIncomingCerts: bad cert array (7)\n"); return ortn; } /* enqueue onto head of cert chain */ thisSslCert->next = certChain; certChain = thisSslCert; } /* SUCCESS */ *destCert = certChain; return noErr; /* free certChain, everything in it, other vars, return ortn */ sslDeleteCertificateChain(certChain, ctx); /* FIXME - anything else? */ return ortn; }
int main(int argc, char **argv) { CSSM_CL_HANDLE clHand; // CL handle CSSM_X509_NAME *subjName; CSSM_X509_NAME *rootName; CSSM_X509_TIME *notBefore; // UTC-style "not before" time CSSM_X509_TIME *notAfter; // UTC-style "not after" time CSSM_DATA_PTR rawCert; // from CSSM_CL_CertCreateTemplate CSSM_DATA signedRootCert; // from CSSM_CL_CertSign CSSM_DATA signedSubjCert; // from CSSM_CL_CertSign CSSM_CSP_HANDLE cspHand; // CSP handle CSSM_KEY subjPubKey; // subject's RSA public key blob CSSM_KEY subjPrivKey; // subject's RSA private key - ref format CSSM_KEY rootPubKey; // root's RSA public key blob CSSM_KEY rootPrivKey; // root's RSA private key - ref format CSSM_RETURN crtn; CSSM_KEY_PTR extractRootKey; // from CSSM_CL_CertGetKeyInfo() CSSM_KEY_PTR extractSubjKey; // ditto CSSM_CC_HANDLE signContext; // for signing/verifying the cert unsigned badByte; int arg; unsigned errorCount = 0; /* user-spec'd variables */ CSSM_BOOL writeBlobs = CSSM_FALSE; CSSM_ALGORITHMS keyAlg = KEY_ALG_DEFAULT; CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT; uint32 keySizeInBits = CSP_KEY_SIZE_DEFAULT; /* * Two extensions. Subject has one (KeyUsage); root has KeyUsage and * BasicConstraints. */ CSSM_X509_EXTENSION exts[2]; CE_KeyUsage keyUsage; CE_BasicConstraints bc; for(arg=1; arg<argc; arg++) { switch(argv[arg][0]) { case 'w': writeBlobs = CSSM_TRUE; break; case 'a': if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) { usage(argv); } switch(argv[arg][2]) { case 's': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_SHA1WithRSA; break; case 'm': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_MD5WithRSA; break; case 'f': keyAlg = CSSM_ALGID_FEE; sigAlg = CSSM_ALGID_FEE_MD5; break; case 'F': keyAlg = CSSM_ALGID_FEE; sigAlg = CSSM_ALGID_FEE_SHA1; break; case 'e': keyAlg = CSSM_ALGID_FEE; sigAlg = CSSM_ALGID_SHA1WithECDSA; break; case 'E': keyAlg = CSSM_ALGID_ECDSA; sigAlg = CSSM_ALGID_SHA1WithECDSA; break; case '7': keyAlg = CSSM_ALGID_ECDSA; sigAlg = CSSM_ALGID_SHA256WithECDSA; break; case '8': keyAlg = CSSM_ALGID_ECDSA; sigAlg = CSSM_ALGID_SHA384WithECDSA; break; case '9': keyAlg = CSSM_ALGID_ECDSA; sigAlg = CSSM_ALGID_SHA512WithECDSA; break; case '2': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_SHA224WithRSA; break; case '6': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_SHA256WithRSA; break; case '3': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_SHA384WithRSA; break; case '5': keyAlg = CSSM_ALGID_RSA; sigAlg = CSSM_ALGID_SHA512WithRSA; break; default: usage(argv); } break; case 'k': keySizeInBits = atoi(&argv[arg][2]); break; default: usage(argv); } } /* connect to CL and CSP */ clHand = clStartup(); if(clHand == 0) { return 0; } cspHand = cspStartup(); if(cspHand == 0) { return 0; } /* subsequent errors to abort: to detach */ /* cook up an RSA key pair for the subject */ crtn = cspGenKeyPair(cspHand, keyAlg, SUBJ_KEY_LABEL, strlen(SUBJ_KEY_LABEL), keySizeInBits, &subjPubKey, CSSM_FALSE, // pubIsRef - should work both ways, but not yet CSSM_KEYUSE_VERIFY, CSSM_KEYBLOB_RAW_FORMAT_NONE, &subjPrivKey, CSSM_FALSE, // privIsRef CSSM_KEYUSE_SIGN, CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_FALSE); if(crtn) { errorCount++; goto abort; } if(writeBlobs) { writeFile(SUBJ_PRIV_KEY_FILE, subjPrivKey.KeyData.Data, subjPrivKey.KeyData.Length); printf("...wrote %lu bytes to %s\n", subjPrivKey.KeyData.Length, SUBJ_PRIV_KEY_FILE); } /* and the root */ crtn = cspGenKeyPair(cspHand, keyAlg, ROOT_KEY_LABEL, strlen(ROOT_KEY_LABEL), keySizeInBits, &rootPubKey, CSSM_FALSE, // pubIsRef - should work both ways, but not yet CSSM_KEYUSE_VERIFY, CSSM_KEYBLOB_RAW_FORMAT_NONE, &rootPrivKey, CSSM_FALSE, // privIsRef CSSM_KEYUSE_SIGN, CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_FALSE); if(crtn) { errorCount++; goto abort; } if(writeBlobs) { writeFile(ROOT_PRIV_KEY_FILE, rootPrivKey.KeyData.Data, rootPrivKey.KeyData.Length); printf("...wrote %lu bytes to %s\n", rootPrivKey.KeyData.Length, ROOT_PRIV_KEY_FILE); } if(compareKeyData(&rootPubKey, &subjPubKey)) { printf("**WARNING: Identical root and subj keys!\n"); } /* * Cook up various cert fields. * First, the RDNs for subject and issuer. */ rootName = CB_BuildX509Name(rootRdn, NUM_ROOT_NAMES); subjName = CB_BuildX509Name(subjRdn, NUM_SUBJ_NAMES); if((rootName == NULL) || (subjName == NULL)) { printf("CB_BuildX509Name failure"); errorCount++; goto abort; } /* not before/after in generalized time format */ notBefore = CB_BuildX509Time(0); notAfter = CB_BuildX509Time(10000); /* A KeyUsage extension for both certs */ exts[0].extnId = CSSMOID_KeyUsage; exts[0].critical = CSSM_FALSE; exts[0].format = CSSM_X509_DATAFORMAT_PARSED; keyUsage = CE_KU_DigitalSignature | CE_KU_KeyCertSign | CE_KU_KeyEncipherment | CE_KU_DataEncipherment; exts[0].value.parsedValue = &keyUsage; exts[0].BERvalue.Data = NULL; exts[0].BERvalue.Length = 0; /* BasicConstraints for root only */ exts[1].extnId = CSSMOID_BasicConstraints; exts[1].critical = CSSM_TRUE; exts[1].format = CSSM_X509_DATAFORMAT_PARSED; bc.cA = CSSM_TRUE; bc.pathLenConstraintPresent = CSSM_TRUE; bc.pathLenConstraint = 2; exts[1].value.parsedValue = &bc; exts[1].BERvalue.Data = NULL; exts[1].BERvalue.Length = 0; /* cook up root cert */ printf("Creating root cert...\n"); rawCert = CB_MakeCertTemplate(clHand, 0x12345678, // serial number rootName, rootName, notBefore, notAfter, &rootPubKey, sigAlg, NULL, // subjUniqueId NULL, // issuerUniqueId exts, // extensions 2); // numExtensions if(rawCert == NULL) { errorCount++; goto abort; } if(writeBlobs) { writeFile(ROOT_TBS_FILE_NAME, rawCert->Data, rawCert->Length); printf("...wrote %lu bytes to %s\n", rawCert->Length, ROOT_TBS_FILE_NAME); } /* Self-sign; this is a root cert */ crtn = CSSM_CSP_CreateSignatureContext(cspHand, sigAlg, NULL, // AccessCred &rootPrivKey, &signContext); if(crtn) { printError("CSSM_CSP_CreateSignatureContext", crtn); errorCount++; goto abort; } signedRootCert.Data = NULL; signedRootCert.Length = 0; crtn = CSSM_CL_CertSign(clHand, signContext, rawCert, // CertToBeSigned NULL, // SignScope 0, // ScopeSize, &signedRootCert); if(crtn) { printError("CSSM_CL_CertSign", crtn); errorCount++; goto abort; } crtn = CSSM_DeleteContext(signContext); if(crtn) { printError("CSSM_DeleteContext", crtn); errorCount++; goto abort; } appFreeCssmData(rawCert, CSSM_TRUE); if(writeBlobs) { writeFile(ROOT_CERT_FILE_NAME, signedRootCert.Data, signedRootCert.Length); printf("...wrote %lu bytes to %s\n", signedRootCert.Length, ROOT_CERT_FILE_NAME); } /* now a subject cert signed by the root cert */ printf("Creating subject cert...\n"); rawCert = CB_MakeCertTemplate(clHand, 0x8765, // serial number rootName, subjName, notBefore, notAfter, &subjPubKey, sigAlg, NULL, // subjUniqueId NULL, // issuerUniqueId exts, // extensions 1); // numExtensions if(rawCert == NULL) { errorCount++; goto abort; } if(writeBlobs) { writeFile(SUBJ_TBS_FILE_NAME, rawCert->Data, rawCert->Length); printf("...wrote %lu bytes to %s\n", rawCert->Length, SUBJ_TBS_FILE_NAME); } /* sign by root */ crtn = CSSM_CSP_CreateSignatureContext(cspHand, sigAlg, NULL, // AccessCred &rootPrivKey, &signContext); if(crtn) { printError("CSSM_CSP_CreateSignatureContext", crtn); errorCount++; goto abort; } signedSubjCert.Data = NULL; signedSubjCert.Length = 0; crtn = CSSM_CL_CertSign(clHand, signContext, rawCert, // CertToBeSigned NULL, // SignScope 0, // ScopeSize, &signedSubjCert); if(crtn) { printError("CSSM_CL_CertSign", crtn); errorCount++; goto abort; } crtn = CSSM_DeleteContext(signContext); if(crtn) { printError("CSSM_DeleteContext", crtn); errorCount++; goto abort; } appFreeCssmData(rawCert, CSSM_TRUE); if(writeBlobs) { writeFile(SUBJ_CERT_FILE_NAME, signedSubjCert.Data, signedSubjCert.Length); printf("...wrote %lu bytes to %s\n", signedSubjCert.Length, SUBJ_CERT_FILE_NAME); } /* Free the stuff we allocd to get here */ CB_FreeX509Name(rootName); CB_FreeX509Name(subjName); CB_FreeX509Time(notBefore); CB_FreeX509Time(notAfter); /* * Extract public keys from the two certs, verify. */ crtn = CSSM_CL_CertGetKeyInfo(clHand, &signedSubjCert, &extractSubjKey); if(crtn) { printError("CSSM_CL_CertGetKeyInfo", crtn); } else { /* compare key data - header is different. * Known header differences: * -- CspID - CSSM_CL_CertGetKeyInfo returns a key with NULL for * this field * -- Format. rootPubKey : 6 (CSSM_KEYBLOB_RAW_FORMAT_BSAFE) * extractRootKey : 1 (CSSM_KEYBLOB_RAW_FORMAT_PKCS1) * -- KeyAttr. rootPubKey : 0x20 (CSSM_KEYATTR_EXTRACTABLE) * extractRootKey : 0x0 */ if(!compareKeyData(extractSubjKey, &subjPubKey)) { printf("***CSSM_CL_CertGetKeyInfo(signedSubjCert) returned bad key data\n"); } if(extractSubjKey->KeyHeader.LogicalKeySizeInBits != subjPubKey.KeyHeader.LogicalKeySizeInBits) { printf("***EffectiveKeySizeInBits mismatch: extract %u subj %u\n", (unsigned)extractSubjKey->KeyHeader.LogicalKeySizeInBits, (unsigned)subjPubKey.KeyHeader.LogicalKeySizeInBits); } } crtn = CSSM_CL_CertGetKeyInfo(clHand, &signedRootCert, &extractRootKey); if(crtn) { printError("CSSM_CL_CertGetKeyInfo", crtn); } else { if(!compareKeyData(extractRootKey, &rootPubKey)) { printf("***CSSM_CL_CertGetKeyInfo(signedRootCert) returned bad key data\n"); } } /* * Verify: */ printf("Verifying certificates...\n"); /* * Verify root cert by root pub key, should succeed. */ if(verifyCert(clHand, cspHand, &signedRootCert, NULL, &rootPubKey, sigAlg, CSSM_OK, "Verify(root by root key)")) { errorCount++; /* continue */ } /* * Verify root cert by root cert, should succeed. */ if(verifyCert(clHand, cspHand, &signedRootCert, &signedRootCert, NULL, CSSM_ALGID_NONE, // sigAlg not used here CSSM_OK, "Verify(root by root cert)")) { errorCount++; /* continue */ } /* * Verify subject cert by root pub key, should succeed. */ if(verifyCert(clHand, cspHand, &signedSubjCert, NULL, &rootPubKey, sigAlg, CSSM_OK, "Verify(subj by root key)")) { errorCount++; /* continue */ } /* * Verify subject cert by root cert, should succeed. */ if(verifyCert(clHand, cspHand, &signedSubjCert, &signedRootCert, NULL, CSSM_ALGID_NONE, // sigAlg not used here CSSM_OK, "Verify(subj by root cert)")) { errorCount++; /* continue */ } /* * Verify subject cert by root cert AND key, should succeed. */ if(verifyCert(clHand, cspHand, &signedSubjCert, &signedRootCert, &rootPubKey, sigAlg, CSSM_OK, "Verify(subj by root cert and key)")) { errorCount++; /* continue */ } /* * Verify subject cert by extracted root pub key, should succeed. */ if(verifyCert(clHand, cspHand, &signedSubjCert, NULL, extractRootKey, sigAlg, CSSM_OK, "Verify(subj by extracted root key)")) { errorCount++; /* continue */ } /* * Verify subject cert by subject pub key, should fail. */ if(verifyCert(clHand, cspHand, &signedSubjCert, NULL, &subjPubKey, sigAlg, CSSMERR_CL_VERIFICATION_FAILURE, "Verify(subj by subj key)")) { errorCount++; /* continue */ } /* * Verify subject cert by subject cert, should fail. */ if(verifyCert(clHand, cspHand, &signedSubjCert, &signedSubjCert, NULL, CSSM_ALGID_NONE, // sigAlg not used here CSSMERR_CL_VERIFICATION_FAILURE, "Verify(subj by subj cert)")) { errorCount++; /* continue */ } /* * Verify erroneous subject cert by root pub key, should fail. */ badByte = genRand(1, signedSubjCert.Length - 1); signedSubjCert.Data[badByte] ^= 0x55; if(verifyCert(clHand, cspHand, &signedSubjCert, NULL, &rootPubKey, sigAlg, CSSMERR_CL_VERIFICATION_FAILURE, "Verify(bad subj by root key)")) { errorCount++; /* continue */ } /* free/delete certs and keys */ appFreeCssmData(&signedSubjCert, CSSM_FALSE); appFreeCssmData(&signedRootCert, CSSM_FALSE); cspFreeKey(cspHand, &rootPubKey); cspFreeKey(cspHand, &subjPubKey); /* These don't work because CSSM_CL_CertGetKeyInfo() gives keys with * a bogus GUID. This may be a problem with the Apple CSP... * cspFreeKey(cspHand, extractRootKey); cspFreeKey(cspHand, extractSubjKey); * * do it this way instead...*/ CSSM_FREE(extractRootKey->KeyData.Data); CSSM_FREE(extractSubjKey->KeyData.Data); /* need to do this regardless...*/ CSSM_FREE(extractRootKey); CSSM_FREE(extractSubjKey); abort: if(cspHand != 0) { CSSM_ModuleDetach(cspHand); } if(errorCount) { printf("Signer/Subject test failed with %d errors\n", errorCount); } else { printf("Signer/Subject test succeeded\n"); } return 0; }