/* Reads certificates from the list of known locations into store. Stops when * any location contains any certificates, to prevent spending unnecessary time * adding redundant certificates, e.g. when both a certificate bundle and * individual certificates exist in the same directory. */ static void read_trusted_roots_from_known_locations(HCERTSTORE store) { HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if (from) { DWORD i; BOOL ret = FALSE; #ifdef HAVE_SECURITY_SECURITY_H OSStatus status; CFArrayRef rootCerts; status = SecTrustCopyAnchorCertificates(&rootCerts); if (status == noErr) { int i; for (i = 0; i < CFArrayGetCount(rootCerts); i++) { SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(rootCerts, i); CFDataRef certData; if ((status = SecKeychainItemExport(cert, kSecFormatX509Cert, 0, NULL, &certData)) == noErr) { if (CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, CFDataGetBytePtr(certData), CFDataGetLength(certData), CERT_STORE_ADD_NEW, NULL)) ret = TRUE; else WARN("adding root cert %d failed: %08x\n", i, GetLastError()); CFRelease(certData); } else WARN("could not export certificate %d to X509 format: 0x%08x\n", i, (unsigned int)status); } CFRelease(rootCerts); } #endif for (i = 0; !ret && i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]); i++) ret = import_certs_from_path(CRYPT_knownLocations[i], from, TRUE); check_and_store_certs(from, store); } CertCloseStore(from, 0); }
SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, PCCERT_CONTEXT *cert) { struct mac_session* s = (struct mac_session*)session; SECURITY_STATUS ret = SEC_E_INTERNAL_ERROR; CFArrayRef certs; OSStatus status; TRACE("(%p/%p, %p)\n", s, s->context, cert); status = SSLCopyPeerCertificates(s->context, &certs); if (status == noErr && certs) { SecCertificateRef mac_cert; CFDataRef data; if (CFArrayGetCount(certs) && (mac_cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0)) && (SecKeychainItemExport(mac_cert, kSecFormatX509Cert, 0, NULL, &data) == noErr)) { *cert = CertCreateCertificateContext(X509_ASN_ENCODING, CFDataGetBytePtr(data), CFDataGetLength(data)); if (*cert) ret = SEC_E_OK; else { ret = GetLastError(); WARN("CertCreateCertificateContext failed: %x\n", ret); } CFRelease(data); } else WARN("Couldn't extract certificate data\n"); CFRelease(certs); } else WARN("SSLCopyPeerCertificates failed: %ld\n", (long)status); return ret; }
SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store, PCCERT_CONTEXT *ret_cert) { struct mac_session* s = (struct mac_session*)session; SECURITY_STATUS ret = SEC_E_OK; PCCERT_CONTEXT cert = NULL; SecCertificateRef mac_cert; CFArrayRef cert_array; int status; CFIndex cnt, i; CFDataRef data; BOOL res; TRACE("(%p/%p, %p)\n", s, s->context, cert); #ifdef HAVE_SSLCOPYPEERCERTIFICATES status = SSLCopyPeerCertificates(s->context, &cert_array); #else status = SSLGetPeerCertificates(s->context, &cert_array); #endif if (status != noErr || !cert_array) { WARN("SSLCopyPeerCertificates failed: %d\n", status); return SEC_E_INTERNAL_ERROR; } cnt = CFArrayGetCount(cert_array); for (i=0; i < cnt; i++) { if (!(mac_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_array, i)) || (SecKeychainItemExport(mac_cert, kSecFormatX509Cert, 0, NULL, &data) != noErr)) { WARN("Couldn't extract certificate data\n"); ret = SEC_E_INTERNAL_ERROR; break; } res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, CFDataGetBytePtr(data), CFDataGetLength(data), CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert); CFRelease(data); if (!res) { ret = GetLastError(); WARN("CertAddEncodedCertificateToStore failed: %x\n", ret); break; } } #ifndef HAVE_SSLCOPYPEERCERTIFICATES /* This is why SSLGetPeerCertificates was deprecated */ CFArrayApplyFunction(cert_array, CFRangeMake(0, CFArrayGetCount(cert_array)), schan_imp_cf_release, NULL); #endif CFRelease(cert_array); if (ret != SEC_E_OK) { if(cert) CertFreeCertificateContext(cert); return ret; } *ret_cert = cert; return SEC_E_OK; }
int main(int argc, char **argv) { bool verbose = false; int arg; while ((arg = getopt(argc, argv, "vh")) != -1) { switch (arg) { case 'v': verbose = true; break; case 'h': usage(argv); } } if(optind != argc) { usage(argv); } printNoDialog(); /* initial setup */ verboseDisp(verbose, "deleting keychain"); unlink(KEYCHAIN_NAME); verboseDisp(verbose, "creating keychain"); SecKeychainRef kcRef = NULL; OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, false, NULL, &kcRef); if(ortn) { cssmPerror("SecKeychainCreate", ortn); exit(1); } /* * 1. Generate key pair with cleartext public key. * Ensure we can use the public key when keychain is locked with no * user interaction. */ /* generate key pair, cleartext public key */ verboseDisp(verbose, "creating key pair, cleartext public key"); SecKeyRef pubKeyRef = NULL; SecKeyRef privKeyRef = NULL; if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) { exit(1); } /* Use generated cleartext public key with locked keychain */ verboseDisp(verbose, "locking keychain, exporting public key"); SecKeychainLock(kcRef); CFDataRef exportData = NULL; ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); if(ortn) { cssmPerror("SecKeychainCreate", ortn); exit(1); } CFRelease(exportData); verboseDisp(verbose, "locking keychain, encrypting with public key"); SecKeychainLock(kcRef); if(pubKeyEncrypt(pubKeyRef)) { exit(1); } /* reset */ verboseDisp(verbose, "deleting keys"); ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } CFRelease(pubKeyRef); CFRelease(privKeyRef); /* * 2. Generate key pair with encrypted public key. * Ensure that user interaction is required when we use the public key * when keychain is locked. */ verboseDisp(verbose, "programmatically unlocking keychain"); ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } /* generate key pair, encrypted public key */ verboseDisp(verbose, "creating key pair, encrypted public key"); if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) { exit(1); } /* Use generated encrypted public key with locked keychain */ verboseDisp(verbose, "locking keychain, exporting public key"); SecKeychainLock(kcRef); printExpectDialog(); ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); if(ortn) { cssmPerror("SecKeychainCreate", ortn); exit(1); } /* we'll use that exported blob later to test import */ if(!didGetDialog()) { exit(1); } verboseDisp(verbose, "locking keychain, encrypting with public key"); SecKeychainLock(kcRef); printExpectDialog(); if(pubKeyEncrypt(pubKeyRef)) { exit(1); } if(!didGetDialog()) { exit(1); } /* reset */ printNoDialog(); verboseDisp(verbose, "locking keychain"); SecKeychainLock(kcRef); verboseDisp(verbose, "deleting keys"); ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } CFRelease(pubKeyRef); CFRelease(privKeyRef); /* * 3. Import public key, storing in cleartext. Ensure that the import * doesn't require unlock, and ensure we can use the public key * when keychain is locked with no user interaction. */ printNoDialog(); verboseDisp(verbose, "locking keychain"); SecKeychainLock(kcRef); /* import public key - default is in the clear */ verboseDisp(verbose, "importing public key, store in the clear (default)"); CFArrayRef outArray = NULL; SecExternalFormat format = kSecFormatOpenSSL; SecExternalItemType type = kSecItemTypePublicKey; ortn = SecKeychainItemImport(exportData, NULL, &format, &type, 0, NULL, kcRef, &outArray); if(ortn) { cssmPerror("SecKeychainItemImport", ortn); exit(1); } CFRelease(exportData); if(CFArrayGetCount(outArray) != 1) { printf("***Unexpected outArray size (%ld) after import\n", (long)CFArrayGetCount(outArray)); exit(1); } pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { printf("***Unexpected item type after import\n"); exit(1); } /* Use imported cleartext public key with locked keychain */ verboseDisp(verbose, "locking keychain, exporting public key"); SecKeychainLock(kcRef); exportData = NULL; ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); if(ortn) { cssmPerror("SecKeychainItemExport", ortn); exit(1); } /* we'll use exportData again */ verboseDisp(verbose, "locking keychain, encrypting with public key"); SecKeychainLock(kcRef); if(pubKeyEncrypt(pubKeyRef)) { exit(1); } /* reset */ verboseDisp(verbose, "deleting key"); ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); if(ortn) { cssmPerror("SecKeychainItemDelete", ortn); exit(1); } CFRelease(pubKeyRef); /* * Import public key, storing in encrypted form. * Ensure that user interaction is required when we use the public key * when keychain is locked. */ /* import public key, encrypted in the keychain */ SecKeyImportExportParameters impExpParams; memset(&impExpParams, 0, sizeof(impExpParams)); impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT; verboseDisp(verbose, "importing public key, store encrypted"); printExpectDialog(); outArray = NULL; format = kSecFormatOpenSSL; type = kSecItemTypePublicKey; ortn = SecKeychainItemImport(exportData, NULL, &format, &type, 0, &impExpParams, kcRef, &outArray); if(ortn) { cssmPerror("SecKeychainItemImport", ortn); exit(1); } if(!didGetDialog()) { exit(1); } CFRelease(exportData); if(CFArrayGetCount(outArray) != 1) { printf("***Unexpected outArray size (%ld) after import\n", (long)CFArrayGetCount(outArray)); exit(1); } pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { printf("***Unexpected item type after import\n"); exit(1); } /* Use imported encrypted public key with locked keychain */ verboseDisp(verbose, "locking keychain, exporting public key"); SecKeychainLock(kcRef); printExpectDialog(); ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); if(ortn) { cssmPerror("SecKeychainItemExport", ortn); exit(1); } if(!didGetDialog()) { exit(1); } CFRelease(exportData); verboseDisp(verbose, "locking keychain, encrypting with public key"); SecKeychainLock(kcRef); printExpectDialog(); if(pubKeyEncrypt(pubKeyRef)) { exit(1); } if(!didGetDialog()) { exit(1); } SecKeychainDelete(kcRef); printf("...test succeeded.\n"); return 0; }
static int testExtractable( SecKeychainRef keychain, Boolean extractable, Boolean explicit) { OSStatus status; SecKeyRef publicKeyRef = NULL; SecKeyRef privateKeyRef = NULL; CFStringRef label = (extractable) ? CFSTR("test-extractable-YES") : CFSTR("test-extractable-NO"); Boolean *extractablePtr = (explicit) ? &extractable : NULL; status = GenerateRSAKeyPair(keychain, label, 1024, // size extractablePtr, &publicKeyRef, &privateKeyRef); if (status != noErr) { //errx(EXIT_FAILURE, "Unable to get key pair (err = %d)", status); return status; } // check that the attributes of the generated private key are what we think they are const CSSM_KEY *cssmPrivKey; status = SecKeyGetCSSMKey(privateKeyRef, &cssmPrivKey); ok_status(status, "%s: SecKeyGetCSSMKey", testName); if (status != noErr) { //errx(EXIT_FAILURE, "Unable to get CSSM reference key (err = %d)", status); return status; } if (extractable) { ok(cssmPrivKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE, "%s: check private key marked as extractable (as requested)", testName); if (!(cssmPrivKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) { //errx(EXIT_FAILURE, "Oops! the private key was not marked as extractable!"); return 1; } } else { ok(!(cssmPrivKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE), "%s: check private key marked as non-extractable (as requested)", testName); if (cssmPrivKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE) { //errx(EXIT_FAILURE, "Oops! the private key was marked as extractable!"); return 1; } } SecKeyImportExportParameters keyParams; memset(&keyParams, 0, sizeof(keyParams)); keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; keyParams.passphrase = CFSTR("borken"); CFDataRef exportedData = NULL; status = SecKeychainItemExport(privateKeyRef, kSecFormatWrappedPKCS8, 0, &keyParams, &exportedData); if(extractable) { ok_status(status, "%s: SecKeychainItemExport (PKCS8) (and we expected it to succeed)", testName); } else { is(status, errSecDataNotAvailable, "%s: SecKeychainItemExport (PKCS8) (and we expected this to fail with errSecDataNotAvailable)", testName); } status = SecKeychainItemExport(privateKeyRef, kSecFormatPKCS12, 0, &keyParams, &exportedData); if(extractable) { ok_status(status, "%s: SecKeychainItemExport(and we expected it to succeed)", testName); } else { is(status, errSecDataNotAvailable, "%s: SecKeychainItemExport (PKCS12) (and we expected this to fail with errSecDataNotAvailable)", testName); } if (status != noErr) { if (extractable) { //errx(EXIT_FAILURE, "Unable to export extractable key! (err = %d)", status); return 1; } else { status = 0; // wasn't extractable, so this is the expected result } } else if (status == noErr && !extractable) { //errx(EXIT_FAILURE, "Was able to export non-extractable key! (err = %d)", status); return 1; } status = SecKeychainItemDelete((SecKeychainItemRef)publicKeyRef); ok_status(status, "%s: SecKeychainItemDelete", testName); if (status != noErr) { warnx("Unable to delete created public key from keychain (err = %d)", (int)status); } status = SecKeychainItemDelete((SecKeychainItemRef)privateKeyRef); ok_status(status, "%s: SecKeychainItemDelete", testName); if (status != noErr) { warnx("Unable to delete created private key from keychain (err = %d)", (int)status); } CFRelease(publicKeyRef); CFRelease(privateKeyRef); return 0; }
static int do_keychain_export( SecKeychainRef kcRef, SecExternalFormat externFormat, ItemSpec itemSpec, const char *passphrase, int doPem, const char *fileName) { int result = 0; CFIndex numItems; unsigned numPrivKeys = 0; unsigned numPubKeys = 0; unsigned numCerts = 0; unsigned numIdents = 0; OSStatus ortn; uint32 expFlags = 0; // SecItemImportExportFlags SecKeyImportExportParameters keyParams; CFStringRef passStr = NULL; CFDataRef outData = NULL; unsigned len; /* gather items */ CFMutableArrayRef exportItems = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); switch(itemSpec) { case IS_Certs: ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts); if(ortn) { result = 1; goto loser; } break; case IS_PrivKeys: ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems, &numPrivKeys); if(ortn) { result = 1; goto loser; } break; case IS_PubKeys: ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems, &numPubKeys); if(ortn) { result = 1; goto loser; } break; case IS_AllKeys: ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems, &numPrivKeys); if(ortn) { result = 1; goto loser; } ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems, &numPubKeys); if(ortn) { result = 1; goto loser; } break; case IS_All: /* No public keys here - PKCS12 doesn't support them */ ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts); if(ortn) { result = 1; goto loser; } ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems, &numPrivKeys); if(ortn) { result = 1; goto loser; } break; case IS_Identities: ortn = addIdentities(kcRef, exportItems, &numIdents); if(ortn) { result = 1; goto loser; } if(numIdents) { numPrivKeys += numIdents; numCerts += numIdents; } break; default: sec_error("Internal error parsing item_spec"); result = 1; goto loser; } numItems = CFArrayGetCount(exportItems); if(externFormat == kSecFormatUnknown) { /* Use default export format per set of items */ if(numItems > 1) { externFormat = kSecFormatPEMSequence; } else if(numCerts) { externFormat = kSecFormatX509Cert; } else { externFormat = kSecFormatOpenSSL; } } if(doPem) { expFlags |= kSecItemPemArmour; } /* * Key related arguments, ignored if we're not exporting keys. * Always specify some kind of passphrase - default is secure passkey. */ memset(&keyParams, 0, sizeof(keyParams)); keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; if(passphrase != NULL) { passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII); keyParams.passphrase = passStr; } else { keyParams.flags = kSecKeySecurePassphrase; } /* Go */ ortn = SecKeychainItemExport(exportItems, externFormat, expFlags, &keyParams, &outData); if(ortn) { sec_perror("SecKeychainItemExport", ortn); result = 1; goto loser; } len = CFDataGetLength(outData); if(fileName) { int rtn = writeFile(fileName, CFDataGetBytePtr(outData), len); if(rtn == 0) { if(!do_quiet) { fprintf(stderr, "...%u bytes written to %s\n", len, fileName); } } else { sec_error("Error writing to %s: %s", fileName, strerror(errno)); result = 1; } } else { int irtn = write(STDOUT_FILENO, CFDataGetBytePtr(outData), len); if(irtn != (int)len) { perror("write"); } } loser: if(exportItems) { CFRelease(exportItems); } if(passStr) { CFRelease(passStr); } if(outData) { CFRelease(outData); } return result; }