/* * Given a SecCertificateRef cert, obtain its public key as a SSLPubKey. * Caller must sslFreePubKey and free the SSLPubKey itself. */ extern OSStatus sslCopyPeerPubKey( SSLContext *ctx, SSLPubKey **pubKey) { OSStatus status = noErr; check(pubKey); check(ctx->peerSecTrust); if (!ctx->enableCertVerify) { SecTrustResultType result; require_noerr(status = SecTrustEvaluate(ctx->peerSecTrust, &result), errOut); } SecKeyRef key = SecTrustCopyPublicKey(ctx->peerSecTrust); if (!key) { sslErrorLog("sslCopyPeerPubKey: %s, ctx->peerSecTrust=%p\n", "SecTrustCopyPublicKey failed", (uintptr_t)ctx->peerSecTrust); return errSSLBadCert; } *pubKey = (SSLPubKey*)key; errOut: if (status) { sslErrorLog("sslCopyPeerPubKey: error %d\n", status); } return status; }
/* Extract public SecKeyRef from Certificate Chain */ static int sslCopyPeerPubKey(const SSLCertificate *certchain, SecKeyRef *pubKey) { int err; check(pubKey); SecTrustRef trust = NULL; const SSLCertificate *cert; CFMutableArrayRef certArray = NULL; CFDataRef certData = NULL; SecCertificateRef cfCert = NULL; err = errSSLInternal; certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); cert = certchain; while(cert) { require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out); require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out); CFArrayAppendValue(certArray, cfCert); CFReleaseNull(cfCert); CFReleaseNull(certData); cert=cert->next; } require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out); SecKeyRef key = SecTrustCopyPublicKey(trust); require_action(key, out, err=-9808); // errSSLBadCert *pubKey = key; err = errSecSuccess; out: CFReleaseSafe(certData); CFReleaseSafe(cfCert); CFReleaseSafe(trust); CFReleaseSafe(certArray); return err; }
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; }
/* Extract the pubkey from a cert chain, and send it to the tls_handshake context */ int tls_set_encrypt_pubkey(tls_handshake_t hdsk, const SSLCertificate *certchain) { int err; CFIndex algId; SecTrustRef trustRef = NULL; SecKeyRef pubkey = NULL; CFDataRef modulus = NULL; CFDataRef exponent = NULL; #if 0 { /* dump certs */ int i=0; int j; const SSLCertificate *tmp = certchain; while(tmp) { printf("cert%d[] = {", i); for(j=0; j<tmp->derCert.length; j++) { if((j&0xf)==0) printf("\n"); printf("0x%02x, ", tmp->derCert.data[j]); } printf("}\n"); tmp=tmp->next; i++; } } #endif require_noerr((err=tls_create_trust_from_certs(certchain, &trustRef)), errOut); require_action((pubkey = SecTrustCopyPublicKey(trustRef)), errOut, err=-9808); // errSSLBadCert #if TARGET_OS_IPHONE algId = SecKeyGetAlgorithmID(pubkey); #else algId = SecKeyGetAlgorithmId(pubkey); #endif err = -9809; //errSSLCrypto; switch(algId) { case kSecRSAAlgorithmID: { require((modulus = SecKeyCopyModulus(pubkey)), errOut); require((exponent = SecKeyCopyExponent(pubkey)), errOut); tls_buffer mod; tls_buffer exp; mod.data = (uint8_t *)CFDataGetBytePtr(modulus); mod.length = CFDataGetLength(modulus); exp.data = (uint8_t *)CFDataGetBytePtr(exponent); exp.length = CFDataGetLength(exponent); err = tls_handshake_set_encrypt_rsa_public_key(hdsk, &mod, &exp); break; } default: break; } errOut: CFReleaseSafe(trustRef); CFReleaseSafe(pubkey); CFReleaseSafe(modulus); CFReleaseSafe(exponent); return err; }
/* Test basic add delete update copy matching stuff. */ static void tests(void) { SecTrustRef trust; SecCertificateRef cert0, cert1; isnt(cert0 = SecCertificateCreateWithBytes(NULL, _c0, sizeof(_c0)), NULL, "create cert0"); isnt(cert1 = SecCertificateCreateWithBytes(NULL, _c1, sizeof(_c1)), NULL, "create cert1"); const void *v_certs[] = { cert0, cert1 }; SecPolicyRef policy = SecPolicyCreateSSL(false, NULL); CFArrayRef certs = CFArrayCreate(NULL, v_certs, array_size(v_certs), NULL); /* SecTrustCreateWithCertificates using single cert. */ ok_status(SecTrustCreateWithCertificates(cert0, policy, &trust), "create trust with single cert0"); is(SecTrustGetCertificateCount(trust), 1, "cert count is 1"); is(SecTrustGetCertificateAtIndex(trust, 0), cert0, "cert 0 is leaf"); CFReleaseNull(trust); /* SecTrustCreateWithCertificates failures. */ is_status(SecTrustCreateWithCertificates(kCFBooleanTrue, policy, &trust), errSecParam, "create trust with boolean instead of cert"); is_status(SecTrustCreateWithCertificates(cert0, kCFBooleanTrue, &trust), errSecParam, "create trust with boolean instead of policy"); /* SecTrustCreateWithCertificates using array of certs. */ ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust"); /* NOTE: prior to <rdar://11810677 SecTrustGetCertificateCount would return 1 at this point. * Now, however, we do an implicit SecTrustEvaluate to build the chain if it has not yet been * evaluated, so we now expect the full chain length. */ is(SecTrustGetCertificateCount(trust), 3, "cert count is 3"); is(SecTrustGetCertificateAtIndex(trust, 0), cert0, "cert 0 is leaf"); /* Jan 1st 2006. */ CFDateRef date = CFDateCreate(NULL, 157680000.0); ok_status(SecTrustSetVerifyDate(trust, date), "set date"); is(SecTrustGetVerifyTime(trust), 157680000.0, "get date"); SecTrustResultType trustResult; SKIP: { #ifdef NO_SERVER skip("Can't fail to connect to securityd in NO_SERVER mode", 4, false); #endif // Test Restore OS environment SecServerSetMachServiceName("com.apple.security.doesn't-exist"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust without securityd running"); is_status(trustResult, kSecTrustResultInvalid, "trustResult is kSecTrustResultInvalid"); is(SecTrustGetCertificateCount(trust), 1, "cert count is 1 without securityd running"); SecKeyRef pubKey = NULL; ok(pubKey = SecTrustCopyPublicKey(trust), "copy public key without securityd running"); CFReleaseNull(pubKey); SecServerSetMachServiceName(NULL); // End of Restore OS environment tests } ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); is_status(trustResult, kSecTrustResultUnspecified, "trustResult is kSecTrustResultUnspecified"); is(SecTrustGetCertificateCount(trust), 3, "cert count is 3"); CFDataRef c0_serial = CFDataCreate(NULL, _c0_serial, sizeof(_c0_serial)); CFDataRef serial; ok(serial = SecCertificateCopySerialNumber(cert0), "copy cert0 serial"); ok(CFEqual(c0_serial, serial), "serial matches"); CFArrayRef anchors = CFArrayCreate(NULL, (const void **)&cert1, 1, &kCFTypeArrayCallBacks); ok_status(SecTrustSetAnchorCertificates(trust, anchors), "set anchors"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); is(SecTrustGetCertificateCount(trust), 2, "cert count is 2"); CFReleaseSafe(anchors); anchors = CFArrayCreate(NULL, NULL, 0, NULL); ok_status(SecTrustSetAnchorCertificates(trust, anchors), "set empty anchors list"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure"); ok_status(SecTrustSetAnchorCertificatesOnly(trust, false), "trust passed in anchors and system anchors"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); ok_status(SecTrustSetAnchorCertificatesOnly(trust, true), "only trust passed in anchors (default)"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure"); /* Test cert_1 intermididate from the keychain. */ CFReleaseSafe(trust); ok_status(SecTrustCreateWithCertificates(cert0, policy, &trust), "create trust with single cert0"); ok_status(SecTrustSetVerifyDate(trust, date), "set date"); // Add cert1 CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSecClass, kSecClassCertificate, kSecValueRef, cert1, NULL); ok_status(SecItemAdd(query, NULL), "add cert1 to keychain"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust"); // Cleanup added cert1. ok_status(SecItemDelete(query), "remove cert1 from keychain"); CFReleaseSafe(query); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); is(SecTrustGetCertificateCount(trust), 3, "cert count is 3"); /* Set certs to be the xedge2 leaf. */ CFReleaseSafe(certs); const void *cert_xedge2; isnt(cert_xedge2 = SecCertificateCreateWithBytes(NULL, xedge2_certificate, sizeof(xedge2_certificate)), NULL, "create cert_xedge2"); certs = CFArrayCreate(NULL, &cert_xedge2, 1, NULL); CFReleaseSafe(trust); CFReleaseSafe(policy); CFReleaseSafe(date); bool server = true; policy = SecPolicyCreateSSL(server, CFSTR("xedge2.apple.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ssl server xedge2.apple.com"); /* Jan 1st 2009. */ date = CFDateCreate(NULL, 252288000.0); ok_status(SecTrustSetVerifyDate(trust, date), "set xedge2 trust date to Jan 1st 2009"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate xedge2 trust"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); CFReleaseSafe(trust); CFReleaseSafe(policy); server = false; policy = SecPolicyCreateSSL(server, CFSTR("xedge2.apple.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ssl client xedge2.apple.com"); ok_status(SecTrustSetVerifyDate(trust, date), "set xedge2 trust date to Jan 1st 2009"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate xedge2 trust"); is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure"); CFReleaseSafe(trust); CFReleaseSafe(policy); server = true; policy = SecPolicyCreateIPSec(server, CFSTR("xedge2.apple.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ip server xedge2.apple.com"); ok_status(SecTrustSetVerifyDate(trust, date), "set xedge2 trust date to Jan 1st 2009"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate xedge2 trust"); #if 0 /* Although this shouldn't be a valid ipsec cert, since we no longer check for ekus in the ipsec policy it is. */ is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure"); #else is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); #endif CFReleaseSafe(trust); CFReleaseSafe(policy); server = true; policy = SecPolicyCreateSSL(server, CFSTR("nowhere.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ssl server nowhere.com"); SecPolicyRef replacementPolicy = SecPolicyCreateSSL(server, CFSTR("xedge2.apple.com")); SecTrustSetPolicies(trust, replacementPolicy); CFReleaseSafe(replacementPolicy); ok_status(SecTrustSetVerifyDate(trust, date), "set xedge2 trust date to Jan 1st 2009"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate xedge2 trust"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); CFReleaseSafe(trust); CFReleaseSafe(policy); server = true; policy = SecPolicyCreateSSL(server, CFSTR("nowhere.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ssl server nowhere.com"); SecPolicyRef replacementPolicy2 = SecPolicyCreateSSL(server, CFSTR("xedge2.apple.com")); CFArrayRef replacementPolicies = CFArrayCreate(kCFAllocatorDefault, (CFTypeRef*)&replacementPolicy2, 1, &kCFTypeArrayCallBacks); SecTrustSetPolicies(trust, replacementPolicies); CFReleaseSafe(replacementPolicy2); CFReleaseSafe(replacementPolicies); ok_status(SecTrustSetVerifyDate(trust, date), "set xedge2 trust date to Jan 1st 2009"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate xedge2 trust"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); /* Test self signed ssl cert with cert itself set as anchor. */ CFReleaseSafe(trust); CFReleaseSafe(policy); CFReleaseSafe(certs); CFReleaseSafe(date); const void *garthc2; server = true; isnt(garthc2 = SecCertificateCreateWithBytes(NULL, garthc2_certificate, sizeof(garthc2_certificate)), NULL, "create garthc2"); certs = CFArrayCreate(NULL, &garthc2, 1, NULL); policy = SecPolicyCreateSSL(server, CFSTR("garthc2.apple.com")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ip server garthc2.apple.com"); date = CFDateCreate(NULL, 269568000.0); ok_status(SecTrustSetVerifyDate(trust, date), "set garthc2 trust date to Aug 2009"); ok_status(SecTrustSetAnchorCertificates(trust, certs), "set garthc2 as anchor"); ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate self signed cert with cert as anchor"); is_status(trustResult, kSecTrustResultUnspecified, "trust is kSecTrustResultUnspecified"); CFReleaseSafe(garthc2); CFReleaseSafe(cert_xedge2); CFReleaseSafe(anchors); CFReleaseSafe(trust); CFReleaseSafe(serial); CFReleaseSafe(c0_serial); CFReleaseSafe(policy); CFReleaseSafe(certs); CFReleaseSafe(cert0); CFReleaseSafe(cert1); CFReleaseSafe(date); /* Test prt_forest_fi */ const void *prt_forest_fi; isnt(prt_forest_fi = SecCertificateCreateWithBytes(NULL, prt_forest_fi_certificate, sizeof(prt_forest_fi_certificate)), NULL, "create prt_forest_fi"); isnt(certs = CFArrayCreate(NULL, &prt_forest_fi, 1, NULL), NULL, "failed to create cert array"); policy = SecPolicyCreateSSL(false, CFSTR("owa.prt-forest.fi")); ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust for ip client owa.prt-forest.fi"); date = CFDateCreate(NULL, 391578321.0); ok_status(SecTrustSetVerifyDate(trust, date), "set owa.prt-forest.fi trust date to May 2013"); SecKeyRef pubkey = SecTrustCopyPublicKey(trust); is(pubkey, NULL, "pubkey returned"); CFReleaseSafe(certs); CFReleaseNull(prt_forest_fi); CFReleaseNull(policy); CFReleaseNull(trust); CFReleaseNull(pubkey); CFReleaseNull(date); }