2 * Copyright (c) 1999-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * sslKeychain.c - Apple Keychain routines
29 #include "sslContext.h"
30 #include "sslMemory.h"
32 #include "sslCrypto.h"
33 #ifdef USE_CDSA_CRYPTO
34 #include <Security/Security.h>
36 #include <Security/SecBase.h>
37 #include <Security/SecCertificate.h>
38 #include <Security/SecIdentity.h>
39 #include <Security/SecPolicy.h>
40 #include <Security/SecTrust.h>
41 #endif /* !USE_CDSA_CRYPTO */
42 #include "utilities/SecCFRelease.h"
45 #include "sslKeychain.h"
51 #include "utilities/SecCFRelease.h"
54 #ifdef USE_SSLCERTIFICATE
57 * Given an array of certs (as SecIdentityRefs, specified by caller
58 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
59 * destination SSLCertificate:
61 * -- free destCerts if we have any
62 * -- Get raw cert data, convert to array of SSLCertificates in *destCert
63 * -- validate cert chain
64 * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
67 /* Convert a SecCertificateRef to an SSLCertificate * */
68 static OSStatus
secCertToSslCert(
70 SecCertificateRef certRef
,
71 SSLCertificate
**sslCert
)
73 CSSM_DATA certData
; // struct is transient, referent owned by
76 SSLCertificate
*thisSslCert
= NULL
;
78 ortn
= SecCertificateGetData(certRef
, &certData
);
80 sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn
);
84 thisSslCert
= (SSLCertificate
*)sslMalloc(sizeof(SSLCertificate
));
85 if(thisSslCert
== NULL
) {
86 return errSecAllocate
;
88 if(SSLAllocBuffer(&thisSslCert
->derCert
, certData
.Length
,
90 return errSecAllocate
;
92 memcpy(thisSslCert
->derCert
.data
, certData
.Data
, certData
.Length
);
93 thisSslCert
->derCert
.length
= certData
.Length
;
94 *sslCert
= thisSslCert
;
99 * Determine the basic signing algorithm, without the digest, component, of
100 * a cert. The returned algorithm will be RSA, DSA, or ECDSA.
102 static OSStatus
sslCertSignerAlg(
103 SecCertificateRef certRef
,
104 CSSM_ALGORITHMS
*signerAlg
)
107 CSSM_DATA_PTR fieldPtr
;
108 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
;
109 CSSM_ALGORITHMS sigAlg
;
112 * Extract the full signature algorithm OID
114 *signerAlg
= CSSM_ALGID_NONE
;
115 ortn
= SecCertificateCopyFirstFieldValue(certRef
,
116 &CSSMOID_X509V1SignatureAlgorithm
,
121 if(fieldPtr
->Length
!= sizeof(CSSM_X509_ALGORITHM_IDENTIFIER
)) {
122 sslErrorLog("sslCertSignerAlg() length error\n");
126 algId
= (CSSM_X509_ALGORITHM_IDENTIFIER
*)fieldPtr
->Data
;
127 if(!cssmOidToAlg(&algId
->algorithm
, &sigAlg
)) {
128 /* Only way this could happen is if we're given a bad cert */
129 sslErrorLog("sslCertSignerAlg() bad sigAlg OID\n");
135 * OK we have the full signature algorithm as a CSSM_ALGORITHMS.
136 * Extract the core signature alg.
140 case CSSM_ALGID_MD2WithRSA
:
141 case CSSM_ALGID_MD5WithRSA
:
142 case CSSM_ALGID_SHA1WithRSA
:
143 case CSSM_ALGID_SHA224WithRSA
:
144 case CSSM_ALGID_SHA256WithRSA
:
145 case CSSM_ALGID_SHA384WithRSA
:
146 case CSSM_ALGID_SHA512WithRSA
:
147 *signerAlg
= CSSM_ALGID_RSA
;
149 case CSSM_ALGID_SHA1WithECDSA
:
150 case CSSM_ALGID_SHA224WithECDSA
:
151 case CSSM_ALGID_SHA256WithECDSA
:
152 case CSSM_ALGID_SHA384WithECDSA
:
153 case CSSM_ALGID_SHA512WithECDSA
:
154 case CSSM_ALGID_ECDSA
:
155 case CSSM_ALGID_ECDSA_SPECIFIED
:
156 *signerAlg
= CSSM_ALGID_ECDSA
;
159 case CSSM_ALGID_SHA1WithDSA
:
160 *signerAlg
= CSSM_ALGID_DSA
;
163 sslErrorLog("sslCertSignerAlg() unknown sigAlg\n");
168 SecCertificateReleaseFirstFieldValue(certRef
,
169 &CSSMOID_X509V1SignatureAlgorithm
, fieldPtr
);
177 SSLCertificate
**destCert
, /* &ctx->{localCert,encryptCert} */
178 CSSM_KEY_PTR
*pubKey
, /* &ctx->signingPubKey, etc. */
179 SecKeyRef
*privKeyRef
, /* &ctx->signingPrivKeyRef, etc. */
180 CSSM_ALGORITHMS
*signerAlg
) /* optional */
184 SSLCertificate
*certChain
= NULL
;
185 SSLCertificate
*thisSslCert
;
187 SecIdentityRef identity
;
188 SecCertificateRef certRef
;
191 CSSM_CL_HANDLE clHand
; // carefully derive from a SecCertificateRef
193 CSSM_KEY_PTR
*pubKey
;
194 SecKeyRef
*privKeyRef
;
197 assert(destCert
!= NULL
); /* though its referent may be NULL */
198 assert(sslPubKey
!= NULL
);
199 assert(sslPrivKeyRef
!= NULL
);
201 pubKey
= &sslPubKey
->key
;
202 privKeyRef
= &sslPrivKey
->key
;
204 sslDeleteCertificateChain(*destCert
, ctx
);
210 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
211 return errSSLBadCert
;
213 numCerts
= CFArrayGetCount(certs
);
215 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
216 return errSSLBadCert
;
220 * Certs[0] is an SecIdentityRef from which we extract subject cert,
221 * privKeyRef, pubKey.
223 * 1. ensure the first element is a SecIdentityRef.
225 identity
= (SecIdentityRef
)CFArrayGetValueAtIndex(certs
, 0);
226 if(identity
== NULL
) {
227 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
230 if(CFGetTypeID(identity
) != SecIdentityGetTypeID()) {
231 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
236 * 2. Extract cert, keys and convert to local format.
238 ortn
= SecIdentityCopyCertificate(identity
, &certRef
);
240 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
243 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
245 sslErrorLog("parseIncomingCerts: bad cert array (4)\n");
248 /* enqueue onto head of cert chain */
249 thisSslCert
->next
= certChain
;
250 certChain
= thisSslCert
;
252 if(signerAlg
!= NULL
) {
253 ortn
= sslCertSignerAlg(certRef
, signerAlg
);
259 /* fetch private key from identity */
260 ortn
= SecIdentityCopyPrivateKey(identity
, &keyRef
);
262 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
266 *privKeyRef
= keyRef
;
268 /* obtain public key from cert */
269 ortn
= SecCertificateGetCLHandle(certRef
, &clHand
);
271 sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
275 certData
.Data
= thisSslCert
->derCert
.data
;
276 certData
.Length
= thisSslCert
->derCert
.length
;
277 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, pubKey
);
279 sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
280 return (OSStatus
)crtn
;
283 /* OK, that's the subject cert. Fetch optional remaining certs. */
285 * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
286 * Incoming certs have root last; SSLCertificate chain has root
289 for(cert
=1; cert
<numCerts
; cert
++) {
290 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, cert
);
291 if(certRef
== NULL
) {
292 sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
295 if(CFGetTypeID(certRef
) != SecCertificateGetTypeID()) {
296 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
300 /* Extract cert, convert to local format.
302 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
304 sslErrorLog("parseIncomingCerts: bad cert array (7)\n");
307 /* enqueue onto head of cert chain */
308 thisSslCert
->next
= certChain
;
309 certChain
= thisSslCert
;
313 *destCert
= certChain
;
314 return errSecSuccess
;
316 /* free certChain, everything in it, other vars, return ortn */
317 sslDeleteCertificateChain(certChain
, ctx
);
318 /* FIXME - anything else? */
322 #else /* !USE_SSLCERTIFICATE */
328 CFArrayRef
*destCertChain
, /* &ctx->{localCertChain,encryptCertChain} */
329 SSLPubKey
**sslPubKey
, /* &ctx->signingPubKey, etc. */
330 SSLPrivKey
**sslPrivKey
, /* &ctx->signingPrivKeyRef, etc. */
331 CFIndex
*signerAlg
) /* optional */
334 CFIndex ix
, numCerts
;
335 SecIdentityRef identity
;
336 CFMutableArrayRef certChain
= NULL
; /* Retained */
337 SecCertificateRef leafCert
= NULL
; /* Retained */
338 SecKeyRef pubKey
= NULL
; /* Retained */
339 SecKeyRef privKey
= NULL
; /* Retained */
340 SecTrustRef trust
= NULL
; /* Retained */
343 assert(destCertChain
!= NULL
); /* though its referent may be NULL */
344 assert(sslPubKey
!= NULL
);
345 assert(sslPrivKey
!= NULL
);
348 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
349 ortn
= errSSLBadCert
;
352 numCerts
= CFArrayGetCount(certs
);
354 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
355 ortn
= errSSLBadCert
;
360 * Certs[0] is an SecIdentityRef from which we extract subject cert,
363 * 1. ensure the first element is a SecIdentityRef.
365 identity
= (SecIdentityRef
)CFArrayGetValueAtIndex(certs
, 0);
366 if (identity
== NULL
) {
367 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
371 if (CFGetTypeID(identity
) != SecIdentityGetTypeID()) {
372 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
378 * 2. Extract cert, keys and convert to local format.
380 ortn
= SecIdentityCopyCertificate(identity
, &leafCert
);
382 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
386 /* Fetch private key from identity */
387 ortn
= SecIdentityCopyPrivateKey(identity
, &privKey
);
389 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
394 /* Convert the input array of SecIdentityRef at the start to an array of
396 certChain
= CFArrayCreateMutable(kCFAllocatorDefault
, numCerts
,
397 &kCFTypeArrayCallBacks
);
399 ortn
= errSecAllocate
;
402 CFArrayAppendValue(certChain
, leafCert
);
403 for (ix
= 1; ix
< numCerts
; ++ix
) {
404 SecCertificateRef intermediate
=
405 (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, ix
);
406 if (intermediate
== NULL
) {
407 sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
411 if (CFGetTypeID(intermediate
) != SecCertificateGetTypeID()) {
412 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
417 CFArrayAppendValue(certChain
, intermediate
);
420 /* Obtain public key from cert */
422 ortn
= SecTrustCreateWithCertificates(certChain
, NULL
, &trust
);
425 SecPolicyRef policy
= SecPolicyCreateBasicX509();
426 ortn
= SecTrustCreateWithCertificates(certChain
, policy
, &trust
);
427 CFReleaseSafe(policy
);
429 /* We are only interested in getting the public key from the leaf
430 * cert here, so for best performance, don't try to build a chain
431 * or search any keychains.
433 CFArrayRef emptyArray
= CFArrayCreate(NULL
, NULL
, 0, NULL
);
434 (void)SecTrustSetAnchorCertificates(trust
, emptyArray
);
435 (void)SecTrustSetKeychains(trust
, emptyArray
);
436 CFReleaseSafe(emptyArray
);
441 sslErrorLog("parseIncomingCerts: SecTrustCreateWithCertificates err %d\n",
447 #if !TARGET_OS_IPHONE
448 /* This is not required on iOS, but still required on osx */
449 SecTrustResultType trustResult
;
450 ortn
= SecTrustEvaluate(trust
, &trustResult
);
452 sslErrorLog("parseIncomingCerts: SecTrustEvaluate err %d\n",
459 pubKey
= SecTrustCopyPublicKey(trust
);
461 /* We parsed the private key succesfully but could not get the public key: return an error */
462 sslErrorLog("parseIncomingCerts: SecTrustCopyPublicKey failed\n");
469 CFReleaseSafe(trust
);
470 CFReleaseSafe(leafCert
);
471 CFReleaseSafe(*destCertChain
);
472 sslFreePubKey(sslPubKey
);
473 sslFreePrivKey(sslPrivKey
);
476 CFReleaseSafe(certChain
);
477 CFReleaseSafe(pubKey
);
478 CFReleaseSafe(privKey
);
480 *destCertChain
= NULL
;
482 *destCertChain
= certChain
;
483 *sslPubKey
= (SSLPubKey
*)pubKey
;
484 *sslPrivKey
= (SSLPrivKey
*)privKey
;
489 #endif /* !USE_SSLCERTIFICATE */