static int32_t ProcessCertificateTypeReturn(CFArrayRef items, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut) { assert(pCertOut != nullptr && *pCertOut == nullptr); assert(pIdentityOut != nullptr && *pIdentityOut == nullptr); if (items == nullptr) { return kErrOutItemsNull; } CFIndex itemCount = CFArrayGetCount(items); if (itemCount == 0) { return kErrOutItemsEmpty; } CFTypeRef bestItem = nullptr; for (CFIndex i = 0; i < itemCount; i++) { CFTypeRef current = CFArrayGetValueAtIndex(items, i); auto currentItemType = CFGetTypeID(current); if (currentItemType == SecIdentityGetTypeID()) { bestItem = current; break; } else if (bestItem == nullptr && currentItemType == SecCertificateGetTypeID()) { bestItem = current; } } if (bestItem == nullptr) { return -13; } if (CFGetTypeID(bestItem) == SecCertificateGetTypeID()) { CFRetain(bestItem); *pCertOut = reinterpret_cast<SecCertificateRef>(const_cast<void*>(bestItem)); return 1; } if (CFGetTypeID(bestItem) == SecIdentityGetTypeID()) { CFRetain(bestItem); *pIdentityOut = reinterpret_cast<SecIdentityRef>(const_cast<void*>(bestItem)); return 1; } return -19; }
/* * 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; }
/* * Assume incoming identity contains a root (e.g., created by * certtool) and add that cert to ST's trusted anchors. This * enables ST's verify of the incoming chain to succeed without * a kludgy "AllowAnyRoot" specification. */ OSStatus addIdentityAsTrustedRoot( SSLContextRef ctx, CFArrayRef identArray) { CFIndex numItems = CFArrayGetCount(identArray); if(numItems == 0) { printf("***addIdentityAsTrustedRoot: empty identArray\n"); return errSecParam; } /* Root should be the last item - could be identity, could be cert */ CFTypeRef theItem = CFArrayGetValueAtIndex(identArray, numItems - 1); if(CFGetTypeID(theItem) == SecIdentityGetTypeID()) { /* identity */ SecCertificateRef certRef; OSStatus ortn = SecIdentityCopyCertificate( (SecIdentityRef)theItem, &certRef); if(ortn) { cssmPerror("SecIdentityCopyCertificate", ortn); printf("***Error gettting cert from identity\n"); return ortn; } ortn = addTrustedSecCert(ctx, certRef, false); CFRelease(certRef); return ortn; } else if(CFGetTypeID(theItem) == SecCertificateGetTypeID()) { /* certificate */ return addTrustedSecCert(ctx, (SecCertificateRef)theItem, false); } else { printf("***Bogus item in identity array\n"); return errSecParam; } }
extern "C" int32_t AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut) { if (pCertOut != nullptr) *pCertOut = nullptr; if (pIdentityOut != nullptr) *pIdentityOut = nullptr; if (handle == nullptr || pCertOut == nullptr || pIdentityOut == nullptr) return kErrorBadInput; auto objectType = CFGetTypeID(handle); void* nonConstHandle = const_cast<void*>(handle); if (objectType == SecIdentityGetTypeID()) { *pIdentityOut = reinterpret_cast<SecIdentityRef>(nonConstHandle); } else if (objectType == SecCertificateGetTypeID()) { *pCertOut = reinterpret_cast<SecCertificateRef>(nonConstHandle); } else { return 0; } CFRetain(handle); return 1; }
static CFDictionaryRef SecItemCopyAttributeDictionary(CFTypeRef ref) { CFDictionaryRef refDictionary = NULL; CFTypeID typeID = CFGetTypeID(ref); if (typeID == SecKeyGetTypeID()) { refDictionary = SecKeyCopyAttributeDictionary((SecKeyRef)ref); } else if (typeID == SecCertificateGetTypeID()) { refDictionary = SecCertificateCopyAttributeDictionary((SecCertificateRef)ref); } else if (typeID == SecIdentityGetTypeID()) { assert(false); SecIdentityRef identity = (SecIdentityRef)ref; SecCertificateRef cert = NULL; SecKeyRef key = NULL; if (!SecIdentityCopyCertificate(identity, &cert) && !SecIdentityCopyPrivateKey(identity, &key)) { CFDataRef data = SecCertificateCopyData(cert); CFDictionaryRef key_dict = SecKeyCopyAttributeDictionary(key); if (key_dict && data) { refDictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, key_dict); CFDictionarySetValue((CFMutableDictionaryRef)refDictionary, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL), data); } CFReleaseNull(key_dict); CFReleaseNull(data); } CFReleaseNull(cert); CFReleaseNull(key); } else { refDictionary = NULL; } return refDictionary; }
/* * Specify signers of the CMS message; implies that the message will be signed. */ OSStatus CMSEncoderAddSigners( CMSEncoderRef cmsEncoder, CFTypeRef signerOrArray) { if(cmsEncoder == NULL) { return errSecParam; } if(cmsEncoder->encState != ES_Init) { return errSecParam; } return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID()); }
static int do_keychain_import( SecKeychainRef kcRef, CFDataRef inData, SecExternalFormat externFormat, SecExternalItemType itemType, SecAccessRef access, Boolean nonExtractable, const char *passphrase, const char *fileName, char **attrNames, char **attrValues, unsigned numExtendedAttributes) { SecKeyImportExportParameters keyParams; OSStatus ortn; CFStringRef fileStr; CFArrayRef outArray = NULL; int result = 0; int numCerts = 0; int numKeys = 0; int numIdentities = 0; int tryCount = 0; CFIndex dex; CFIndex numItems = 0; CFStringRef passStr = NULL; CFStringRef promptStr = NULL; CFStringRef retryStr = NULL; /* * Specify some kind of passphrase in case caller doesn't know this * is a wrapped object */ memset(&keyParams, 0, sizeof(SecKeyImportExportParameters)); keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; if(passphrase != NULL) { passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII); keyParams.passphrase = passStr; } else { keyParams.flags = kSecKeySecurePassphrase; } if(nonExtractable) { // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit keyParams.keyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE; } keyParams.accessRef = access; fileStr = CFStringCreateWithCString(NULL, fileName, kCFStringEncodingUTF8); if (fileStr) { CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, fileStr, kCFURLPOSIXPathStyle, FALSE); if (fileURL) { CFStringRef nameStr = CFURLCopyLastPathComponent(fileURL); if (nameStr) { safe_CFRelease(&fileStr); fileStr = nameStr; } safe_CFRelease(&fileURL); } } promptStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_MESSAGE, fileStr); retryStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE, fileStr); while (TRUE) { keyParams.alertPrompt = (tryCount == 0) ? promptStr : retryStr; ortn = SecKeychainItemImport(inData, fileStr, &externFormat, &itemType, 0, /* flags not used (yet) */ &keyParams, kcRef, &outArray); if(ortn) { if (ortn == errSecPkcs12VerifyFailure && ++tryCount < 3) { continue; } sec_perror("SecKeychainItemImport", ortn); result = 1; goto cleanup; } break; } /* * Parse returned items & report to user */ if(outArray == NULL) { sec_error("No keychain items found"); result = 1; goto cleanup; } numItems = CFArrayGetCount(outArray); for(dex=0; dex<numItems; dex++) { CFTypeRef item = CFArrayGetValueAtIndex(outArray, dex); CFTypeID itemType = CFGetTypeID(item); if(itemType == SecIdentityGetTypeID()) { numIdentities++; } else if(itemType == SecCertificateGetTypeID()) { numCerts++; } else if(itemType == SecKeyGetTypeID()) { numKeys++; } else { sec_error("Unexpected item type returned from SecKeychainItemImport"); result = 1; goto cleanup; } } if(numIdentities) { char *str; if(numIdentities > 1) { str = "identities"; } else { str = "identity"; } fprintf(stdout, "%d %s imported.\n", numIdentities, str); } if(numKeys) { char *str; if(numKeys > 1) { str = "keys"; } else { str = "key"; } fprintf(stdout, "%d %s imported.\n", numKeys, str); } if(numCerts) { char *str; if(numCerts > 1) { str = "certificates"; } else { str = "certificate"; } fprintf(stdout, "%d %s imported.\n", numCerts, str); } /* optionally apply extended attributes */ if(numExtendedAttributes) { unsigned attrDex; for(attrDex=0; attrDex<numExtendedAttributes; attrDex++) { CFStringRef attrNameStr = CFStringCreateWithCString(NULL, attrNames[attrDex], kCFStringEncodingASCII); CFDataRef attrValueData = CFDataCreate(NULL, (const UInt8 *)attrValues[attrDex], strlen(attrValues[attrDex])); for(dex=0; dex<numItems; dex++) { SecKeychainItemRef itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(outArray, dex); ortn = SecKeychainItemSetExtendedAttribute(itemRef, attrNameStr, attrValueData); if(ortn) { cssmPerror("SecKeychainItemSetExtendedAttribute", ortn); result = 1; break; } } /* for each imported item */ CFRelease(attrNameStr); CFRelease(attrValueData); if(result) { break; } } /* for each extended attribute */ } cleanup: safe_CFRelease(&fileStr); safe_CFRelease(&outArray); safe_CFRelease(&passStr); safe_CFRelease(&promptStr); safe_CFRelease(&retryStr); return result; }
static CFArrayRef /* O - Array of certificates */ copy_cdsa_certificate( cupsd_client_t *con) /* I - Client connection */ { OSStatus err; /* Error info */ SecKeychainRef keychain = NULL;/* Keychain reference */ SecIdentitySearchRef search = NULL; /* Search reference */ SecIdentityRef identity = NULL;/* Identity */ CFArrayRef certificates = NULL; /* Certificate array */ SecPolicyRef policy = NULL; /* Policy ref */ CFStringRef servername = NULL; /* Server name */ CFMutableDictionaryRef query = NULL; /* Query qualifiers */ CFArrayRef list = NULL; /* Keychain list */ # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) char localname[1024];/* Local hostname */ # endif /* HAVE_DNSSD || HAVE_AVAHI */ cupsdLogMessage(CUPSD_LOG_DEBUG, "copy_cdsa_certificate: Looking for certs for \"%s\".", con->servername); if ((err = SecKeychainOpen(ServerCertificate, &keychain))) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)", ServerCertificate, cssmErrorString(err), (int)err); goto cleanup; } servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername, kCFStringEncodingUTF8); policy = SecPolicyCreateSSL(1, servername); if (servername) CFRelease(servername); if (!policy) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference"); goto cleanup; } if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks))) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary"); goto cleanup; } list = CFArrayCreate(kCFAllocatorDefault, (const void **)&keychain, 1, &kCFTypeArrayCallBacks); CFDictionaryAddValue(query, kSecClass, kSecClassIdentity); CFDictionaryAddValue(query, kSecMatchPolicy, policy); CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); CFDictionaryAddValue(query, kSecMatchSearchList, list); CFRelease(list); err = SecItemCopyMatching(query, (CFTypeRef *)&identity); # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (err && DNSSDHostName) { /* * Search for the connection server name failed; try the DNS-SD .local * hostname instead... */ snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName); cupsdLogMessage(CUPSD_LOG_DEBUG, "copy_cdsa_certificate: Looking for certs for \"%s\".", localname); servername = CFStringCreateWithCString(kCFAllocatorDefault, localname, kCFStringEncodingUTF8); CFRelease(policy); policy = SecPolicyCreateSSL(1, servername); if (servername) CFRelease(servername); if (!policy) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference"); goto cleanup; } CFDictionarySetValue(query, kSecMatchPolicy, policy); err = SecItemCopyMatching(query, (CFTypeRef *)&identity); } # endif /* HAVE_DNSSD || HAVE_AVAHI */ if (err) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Cannot find signing key in keychain \"%s\": %s (%d)", ServerCertificate, cssmErrorString(err), (int)err); goto cleanup; } if (CFGetTypeID(identity) != SecIdentityGetTypeID()) { cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure."); goto cleanup; } if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks)) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array"); goto cleanup; } cleanup : if (keychain) CFRelease(keychain); if (search) CFRelease(search); if (identity) CFRelease(identity); if (policy) CFRelease(policy); if (query) CFRelease(query); return (certificates); }
OSStatus parseIncomingCerts( SSLContext *ctx, CFArrayRef certs, CFArrayRef *destCertChain, /* &ctx->{localCertChain,encryptCertChain} */ SSLPubKey **sslPubKey, /* &ctx->signingPubKey, etc. */ SSLPrivKey **sslPrivKey, /* &ctx->signingPrivKeyRef, etc. */ CFIndex *signerAlg) /* optional */ { OSStatus ortn; CFIndex ix, numCerts; SecIdentityRef identity; CFMutableArrayRef certChain = NULL; /* Retained */ SecCertificateRef leafCert = NULL; /* Retained */ SecKeyRef pubKey = NULL; /* Retained */ SecKeyRef privKey = NULL; /* Retained */ SecTrustRef trust = NULL; /* Retained */ SecTrustResultType trustResult; assert(ctx != NULL); assert(destCertChain != NULL); /* though its referent may be NULL */ assert(sslPubKey != NULL); assert(sslPrivKey != NULL); if (certs == NULL) { sslErrorLog("parseIncomingCerts: NULL incoming cert array\n"); ortn = errSSLBadCert; goto errOut; } numCerts = CFArrayGetCount(certs); if (numCerts == 0) { sslErrorLog("parseIncomingCerts: empty incoming cert array\n"); ortn = errSSLBadCert; goto errOut; } /* * Certs[0] is an SecIdentityRef from which we extract subject cert, * privKey, pubKey. * * 1. ensure the first element is a SecIdentityRef. */ identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0); if (identity == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (1)\n"); ortn = paramErr; goto errOut; } if (CFGetTypeID(identity) != SecIdentityGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (2)\n"); ortn = paramErr; goto errOut; } /* * 2. Extract cert, keys and convert to local format. */ ortn = SecIdentityCopyCertificate(identity, &leafCert); if (ortn) { sslErrorLog("parseIncomingCerts: bad cert array (3)\n"); goto errOut; } /* Fetch private key from identity */ ortn = SecIdentityCopyPrivateKey(identity, &privKey); if (ortn) { sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n", (int)ortn); goto errOut; } /* Convert the input array of SecIdentityRef at the start to an array of all certificates. */ certChain = CFArrayCreateMutable(kCFAllocatorDefault, numCerts, &kCFTypeArrayCallBacks); if (!certChain) { ortn = memFullErr; goto errOut; } CFArrayAppendValue(certChain, leafCert); for (ix = 1; ix < numCerts; ++ix) { SecCertificateRef intermediate = (SecCertificateRef)CFArrayGetValueAtIndex(certs, ix); if (intermediate == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (5)\n"); ortn = paramErr; goto errOut; } if (CFGetTypeID(intermediate) != SecCertificateGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (6)\n"); ortn = paramErr; goto errOut; } CFArrayAppendValue(certChain, intermediate); } /* Obtain public key from cert */ #if TARGET_OS_IOS ortn = SecTrustCreateWithCertificates(certChain, NULL, &trust); #else { SecPolicyRef policy = SecPolicyCreateBasicX509(); ortn = SecTrustCreateWithCertificates(certChain, policy, &trust); CFReleaseSafe(policy); if (!ortn) { /* We are only interested in getting the public key from the leaf * cert here, so for best performance, don't try to build a chain * or search any keychains. */ CFArrayRef emptyArray = CFArrayCreate(NULL, NULL, 0, NULL); (void)SecTrustSetAnchorCertificates(trust, emptyArray); (void)SecTrustSetKeychains(trust, emptyArray); CFReleaseSafe(emptyArray); } } #endif if (ortn) { sslErrorLog("parseIncomingCerts: SecTrustCreateWithCertificates err %d\n", (int)ortn); goto errOut; } ortn = SecTrustEvaluate(trust, &trustResult); if (ortn) { sslErrorLog("parseIncomingCerts: SecTrustEvaluate err %d\n", (int)ortn); goto errOut; } pubKey = SecTrustCopyPublicKey(trust); if (pubKey == NULL) { sslErrorLog("parseIncomingCerts: SecTrustCopyPublicKey failed\n"); ortn = -67712; // errSecInvalidKeyRef goto errOut; } /* SUCCESS */ errOut: CFReleaseSafe(trust); CFReleaseSafe(leafCert); CFReleaseSafe(*destCertChain); sslFreePubKey(sslPubKey); sslFreePrivKey(sslPrivKey); if (ortn) { CFReleaseSafe(certChain); CFReleaseSafe(pubKey); CFReleaseSafe(privKey); *destCertChain = NULL; } else { *destCertChain = certChain; *sslPubKey = (SSLPubKey*)pubKey; *sslPrivKey = (SSLPrivKey*)privKey; } return ortn; }
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 findFirstEncryptionPublicKeyOnToken(SecKeyRef *publicKey, SecKeychainRef *keychainRef, CFDataRef *label) { if (!publicKey || !keychainRef) return paramErr; OSStatus status = noErr; CFArrayRef identityArray = NULL; SecKeyRef tmpKeyRef = NULL; SecCertificateRef certificate = NULL; SecKeychainRef tmpKeychainRef = NULL; try { status = findEncryptionIdentities((CFTypeRef *)&identityArray); if (status) MacOSError::throwMe(status); if (!identityArray || (CFGetTypeID(identityArray)!=CFArrayGetTypeID()) || (CFArrayGetCount(identityArray)==0)) MacOSError::throwMe(paramErr); CFTypeRef tmpref = CFArrayGetValueAtIndex(identityArray, 0); if (CFGetTypeID(tmpref)!=SecIdentityGetTypeID()) MacOSError::throwMe(paramErr); status = SecIdentityCopyCertificate(SecIdentityRef(tmpref), &certificate); if (status) MacOSError::throwMe(status); if (!certificate) MacOSError::throwMe(errKCItemNotFound); status = findCertificatePublicKeyHash(certificate, label); if (status) MacOSError::throwMe(status); status = SecKeychainItemCopyKeychain(SecKeychainItemRef(certificate), &tmpKeychainRef); if (status) MacOSError::throwMe(status); status = SecCertificateCopyPublicKey(certificate, &tmpKeyRef); if (status) MacOSError::throwMe(status); // Found an encryption key *publicKey = tmpKeyRef; *keychainRef = tmpKeychainRef; } catch (const MacOSError &err) { status = err.osStatus(); cssmPerror("findFirstEncryptionPublicKeyOnToken", status); } catch (...) { fprintf(stderr, "findFirstEncryptionPublicKeyOnToken: unknown exception\n"); status = errKCItemNotFound; } if (status) { if (identityArray) CFRelease(identityArray); if (certificate) CFRelease(certificate); } if (identityArray) CFRelease(identityArray); if (certificate) CFRelease(certificate); return status; }
/* * Given an open keychain, find a SecIdentityRef and munge it into * a CFArrayRef required by SSLSetCertificate(). */ CFArrayRef sslKcRefToCertArray( SecKeychainRef kcRef, bool encryptOnly, bool completeCertChain, const char *trustedAnchorFile) { /* quick check to make sure the keychain exists */ SecKeychainStatus kcStat; OSStatus ortn = SecKeychainGetStatus(kcRef, &kcStat); if(ortn) { printSslErrStr("SecKeychainGetStatus", ortn); printf("Can not open keychain. Aborting.\n"); return nil; } /* * Search for "any" identity matching specified key use; * in this app, we expect there to be exactly one. */ SecIdentitySearchRef srchRef = nil; ortn = SecIdentitySearchCreate(kcRef, encryptOnly ? CSSM_KEYUSE_DECRYPT : CSSM_KEYUSE_SIGN, &srchRef); if(ortn) { printf("SecIdentitySearchCreate returned %d.\n", (int)ortn); printf("Cannot find signing key in keychain. Aborting.\n"); return nil; } SecIdentityRef identity = nil; ortn = SecIdentitySearchCopyNext(srchRef, &identity); if(ortn) { printf("SecIdentitySearchCopyNext returned %d.\n", (int)ortn); printf("Cannot find signing key in keychain. Aborting.\n"); return nil; } if(CFGetTypeID(identity) != SecIdentityGetTypeID()) { printf("SecIdentitySearchCopyNext CFTypeID failure!\n"); return nil; } /* * Found one. */ if(completeCertChain) { /* * Place it and the other certs needed to verify it - * up to but not including the root - in a CFArray. */ SecCertificateRef anchorCert = NULL; if(trustedAnchorFile) { ortn = sslReadAnchor(trustedAnchorFile, &anchorCert); if(ortn) { printf("***Error reading anchor file\n"); } } CFArrayRef ca; ortn = sslCompleteCertChain(identity, anchorCert, false, &ca); if(anchorCert) { CFRelease(anchorCert); } return ca; } else { /* simple case, just this one identity */ CFArrayRef ca = CFArrayCreate(NULL, (const void **)&identity, 1, NULL); if(ca == nil) { printf("CFArrayCreate error\n"); } return ca; } }
extern "C" int32_t AppleCryptoNative_X509CopyWithPrivateKey(SecCertificateRef cert, SecKeyRef privateKey, SecKeychainRef targetKeychain, SecIdentityRef* pIdentityOut, int32_t* pOSStatus) { if (pIdentityOut != nullptr) *pIdentityOut = nullptr; if (pOSStatus != nullptr) *pOSStatus = noErr; if (cert == nullptr || privateKey == nullptr || targetKeychain == nullptr || pIdentityOut == nullptr || pOSStatus == nullptr) { return -1; } SecKeychainRef keyKeychain = nullptr; OSStatus status = SecKeychainItemCopyKeychain(reinterpret_cast<SecKeychainItemRef>(privateKey), &keyKeychain); SecKeychainItemRef itemCopy = nullptr; // This only happens with an ephemeral key, so the keychain we're adding it to is temporary. if (status == errSecNoSuchKeychain) { status = AddKeyToKeychain(privateKey, targetKeychain); } if (itemCopy != nullptr) { CFRelease(itemCopy); } CFMutableDictionaryRef query = nullptr; if (status == noErr) { query = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (query == nullptr) { status = errSecAllocate; } } CFArrayRef searchList = nullptr; if (status == noErr) { searchList = CFArrayCreate( nullptr, const_cast<const void**>(reinterpret_cast<void**>(&targetKeychain)), 1, &kCFTypeArrayCallBacks); if (searchList == nullptr) { status = errSecAllocate; } } CFArrayRef itemMatch = nullptr; if (status == noErr) { itemMatch = CFArrayCreate( nullptr, const_cast<const void**>(reinterpret_cast<void**>(&cert)), 1, &kCFTypeArrayCallBacks); if (itemMatch == nullptr) { status = errSecAllocate; } } CFTypeRef result = nullptr; if (status == noErr) { CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue); CFDictionarySetValue(query, kSecMatchSearchList, searchList); CFDictionarySetValue(query, kSecMatchItemList, itemMatch); CFDictionarySetValue(query, kSecClass, kSecClassIdentity); status = SecItemCopyMatching(query, &result); if (status != noErr && result != nullptr) { CFRelease(result); result = nullptr; } bool added = false; if (status == errSecItemNotFound) { status = SecCertificateAddToKeychain(cert, targetKeychain); added = (status == noErr); } if (result == nullptr && status == noErr) { status = SecItemCopyMatching(query, &result); } if (result != nullptr && status == noErr) { if (CFGetTypeID(result) != SecIdentityGetTypeID()) { status = errSecItemNotFound; } else { SecIdentityRef identity = reinterpret_cast<SecIdentityRef>(const_cast<void*>(result)); CFRetain(identity); *pIdentityOut = identity; } } if (added) { // The same query that was used to find the identity can be used // to find/delete the certificate, as long as we fix the class to just the cert. CFDictionarySetValue(query, kSecClass, kSecClassCertificate); // Ignore the output status, there's no point in telling the user // that the cleanup failed, since that just makes them have a dirty keychain // AND their program didn't work. SecItemDelete(query); } } if (result != nullptr) CFRelease(result); if (itemMatch != nullptr) CFRelease(itemMatch); if (searchList != nullptr) CFRelease(searchList); if (query != nullptr) CFRelease(query); if (keyKeychain != nullptr) CFRelease(keyKeychain); *pOSStatus = status; return status == noErr; }
static void tests(void) { CFDataRef message = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _user_one_p12, sizeof(_user_one_p12), kCFAllocatorNull); CFArrayRef items = NULL; SecCertificateRef cert = NULL; SecKeyRef pkey = NULL; is_status(SecPKCS12Import(message, NULL, NULL), errSecAuthFailed, "try null password on a known good p12"); CFStringRef password = CFSTR("user-one"); CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&kSecImportExportPassphrase, (const void **)&password, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ok_status(SecPKCS12Import(message, options, &items), "import user one"); is(CFArrayGetCount(items), 1, "one identity"); CFDictionaryRef item = CFArrayGetValueAtIndex(items, 0); SecIdentityRef identity = NULL; ok(identity = (SecIdentityRef)CFDictionaryGetValue(item, kSecImportItemIdentity), "pull identity from imported data"); ok(CFGetTypeID(identity)==SecIdentityGetTypeID(),"this is a SecIdentityRef"); ok_status(SecIdentityCopyPrivateKey(identity, &pkey),"get private key"); ok_status(SecIdentityCopyCertificate(identity, &cert), "get certificate"); CFReleaseNull(items); CFReleaseNull(message); CFReleaseNull(options); CFReleaseNull(password); CFReleaseNull(cert); CFReleaseNull(pkey); message = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, _user_two_p12, sizeof(_user_two_p12), kCFAllocatorNull); items = NULL; password = CFSTR("user-two"); options = CFDictionaryCreate(NULL, (const void **)&kSecImportExportPassphrase, (const void **)&password, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ok_status(SecPKCS12Import(message, options, &items), "import user two"); is(CFArrayGetCount(items), 1, "one identity"); item = CFArrayGetValueAtIndex(items, 0); ok(identity = (SecIdentityRef)CFDictionaryGetValue(item, kSecImportItemIdentity), "pull identity from imported data"); ok(CFGetTypeID(identity)==SecIdentityGetTypeID(),"this is a SecIdentityRef"); ok_status(SecIdentityCopyPrivateKey(identity, &pkey),"get private key"); ok_status(SecIdentityCopyCertificate(identity, &cert), "get certificate"); CFReleaseNull(items); CFReleaseNull(message); CFReleaseNull(options); CFReleaseNull(password); CFReleaseNull(cert); CFReleaseNull(pkey); message = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, ECDSA_fails_import_p12, ECDSA_fails_import_p12_len, kCFAllocatorNull); items = NULL; password = CFSTR("test"); options = CFDictionaryCreate(NULL, (const void **)&kSecImportExportPassphrase, (const void **)&password, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ok_status(SecPKCS12Import(message, options, &items), "import ECDSA_fails_import_p12"); is(CFArrayGetCount(items), 1, "one identity"); item = CFArrayGetValueAtIndex(items, 0); ok(identity = (SecIdentityRef)CFDictionaryGetValue(item, kSecImportItemIdentity), "pull identity from imported data"); ok(CFGetTypeID(identity)==SecIdentityGetTypeID(),"this is a SecIdentityRef"); ok_status(SecIdentityCopyPrivateKey(identity, &pkey),"get private key"); ok_status(SecIdentityCopyCertificate(identity, &cert), "get certificate"); CFDataRef pubdata = NULL; SecKeyRef pubkey = NULL; ok_status(SecKeyCopyPublicBytes(pkey, &pubdata), "pub key from priv key"); ok(pubkey = SecKeyCreateECPublicKey(kCFAllocatorDefault, CFDataGetBytePtr(pubdata), CFDataGetLength(pubdata), kSecKeyEncodingBytes), "recreate seckey"); /* Sign something. */ uint8_t something[20] = {0x80, 0xbe, 0xef, 0xba, 0xd0, }; size_t sigLen = SecKeyGetSize(pkey, kSecKeySignatureSize); uint8_t sig[sigLen]; ok_status(SecKeyRawSign(pkey, kSecPaddingPKCS1, something, sizeof(something), sig, &sigLen), "sign something"); ok_status(SecKeyRawVerify(pubkey, kSecPaddingPKCS1, something, sizeof(something), sig, sigLen), "verify sig on something"); CFReleaseNull(pubdata); CFReleaseNull(pubkey); CFReleaseNull(pkey); ok(pkey = SecKeyCreateECPrivateKey(kCFAllocatorDefault, ECDSA_fails_import_priv_only, ECDSA_fails_import_priv_only_len, kSecKeyEncodingPkcs1), "import privkey without pub"); ok_status(SecKeyCopyPublicBytes(pkey, &pubdata), "pub key from priv key"); ok(pubkey = SecKeyCreateECPublicKey(kCFAllocatorDefault, CFDataGetBytePtr(pubdata), CFDataGetLength(pubdata), kSecKeyEncodingBytes), "recreate seckey"); ok_status(SecKeyRawVerify(pubkey, kSecPaddingPKCS1, something, sizeof(something), sig, sigLen), "verify sig on something"); CFReleaseNull(pubdata); CFReleaseNull(pubkey); CFReleaseNull(pkey); CFReleaseNull(items); CFReleaseNull(message); CFReleaseNull(options); CFReleaseNull(password); CFReleaseNull(cert); }
static OSStatus parseIncomingCerts(CFArrayRef certs, SSLCertificate **destCertChain, /* &ctx->{localCertChain,encryptCertChain} */ tls_private_key_t *sslPrivKey) /* &ctx->signingPrivKeyRef, etc. */ { OSStatus ortn; CFIndex ix, numCerts; SecIdentityRef identity; SSLCertificate *certChain = NULL; /* Retained */ SecCertificateRef leafCert = NULL; /* Retained */ SecKeyRef privKey = NULL; /* Retained */ assert(destCertChain != NULL); /* though its referent may be NULL */ assert(sslPrivKey != NULL); if (certs == NULL) { sslErrorLog("parseIncomingCerts: NULL incoming cert array\n"); ortn = errSSLBadCert; goto errOut; } numCerts = CFArrayGetCount(certs); if (numCerts == 0) { sslErrorLog("parseIncomingCerts: empty incoming cert array\n"); ortn = errSSLBadCert; goto errOut; } certChain=sslMalloc(numCerts*sizeof(SSLCertificate)); if (!certChain) { ortn = errSecAllocate; goto errOut; } /* * Certs[0] is an SecIdentityRef from which we extract subject cert, * privKey, pubKey. * * 1. ensure the first element is a SecIdentityRef. */ identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0); if (identity == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (1)\n"); ortn = errSecParam; goto errOut; } if (CFGetTypeID(identity) != SecIdentityGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (2)\n"); ortn = errSecParam; goto errOut; } /* * 2. Extract cert, keys and convert to local format. */ ortn = SecIdentityCopyCertificate(identity, &leafCert); if (ortn) { sslErrorLog("parseIncomingCerts: bad cert array (3)\n"); goto errOut; } /* Fetch private key from identity */ ortn = SecIdentityCopyPrivateKey(identity, &privKey); if (ortn) { sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n", (int)ortn); goto errOut; } /* Convert the input array of SecIdentityRef at the start to an array of all certificates. */ SSLCopyBufferFromData(SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert), &certChain[0].derCert); certChain[0].next = NULL; for (ix = 1; ix < numCerts; ++ix) { SecCertificateRef intermediate = (SecCertificateRef)CFArrayGetValueAtIndex(certs, ix); if (intermediate == NULL) { sslErrorLog("parseIncomingCerts: bad cert array (5)\n"); ortn = errSecParam; goto errOut; } if (CFGetTypeID(intermediate) != SecCertificateGetTypeID()) { sslErrorLog("parseIncomingCerts: bad cert array (6)\n"); ortn = errSecParam; goto errOut; } SSLCopyBufferFromData(SecCertificateGetBytePtr(intermediate), SecCertificateGetLength(intermediate), &certChain[ix].derCert); certChain[ix].next = NULL; certChain[ix-1].next = &certChain[ix]; } size_t size = SecKeyGetBlockSize(privKey); tls_private_key_desc_t desc; if(SecKeyGetAlgorithmId(privKey) == kSecRSAAlgorithmID) { desc.type = tls_private_key_type_rsa; desc.rsa.sign = mySSLPrivKeyRSA_sign; desc.rsa.decrypt = mySSLPrivKeyRSA_decrypt; desc.rsa.size = SecKeyGetBlockSize(privKey); } else if (SecKeyGetAlgorithmId(privKey) == kSecECDSAAlgorithmID) { desc.type = tls_private_key_type_ecdsa; desc.ecdsa.sign = mySSLPrivKeyECDSA_sign; desc.ecdsa.curve = SecECKeyGetNamedCurve(privKey); #if TARGET_OS_IPHONE /* Compute signature size from key size */ desc.ecdsa.size = 8+2*size; #else desc.ecdsa.size = size; #endif } else { ortn = errSecParam; goto errOut; } *sslPrivKey = tls_private_key_create(&desc, privKey, (tls_private_key_ctx_release)&CFRelease); if(*sslPrivKey) ortn = errSecSuccess; else ortn = errSecAllocate; /* SUCCESS */ errOut: CFReleaseSafe(leafCert); if (ortn) { free(certChain); CFReleaseSafe(privKey); *destCertChain = NULL; } else { *destCertChain = certChain; } return ortn; }