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 <Security/SecInternal.h>
45 #include "sslKeychain.h"
51 #ifdef USE_SSLCERTIFICATE
54 * Given an array of certs (as SecIdentityRefs, specified by caller
55 * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
56 * destination SSLCertificate:
58 * -- free destCerts if we have any
59 * -- Get raw cert data, convert to array of SSLCertificates in *destCert
60 * -- validate cert chain
61 * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
64 /* Convert a SecCertificateRef to an SSLCertificate * */
65 static OSStatus
secCertToSslCert(
67 SecCertificateRef certRef
,
68 SSLCertificate
**sslCert
)
70 CSSM_DATA certData
; // struct is transient, referent owned by
73 SSLCertificate
*thisSslCert
= NULL
;
75 ortn
= SecCertificateGetData(certRef
, &certData
);
77 sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn
);
81 thisSslCert
= (SSLCertificate
*)sslMalloc(sizeof(SSLCertificate
));
82 if(thisSslCert
== NULL
) {
85 if(SSLAllocBuffer(&thisSslCert
->derCert
, certData
.Length
,
89 memcpy(thisSslCert
->derCert
.data
, certData
.Data
, certData
.Length
);
90 thisSslCert
->derCert
.length
= certData
.Length
;
91 *sslCert
= thisSslCert
;
96 * Determine the basic signing algorithm, without the digest, component, of
97 * a cert. The returned algorithm will be RSA, DSA, or ECDSA.
99 static OSStatus
sslCertSignerAlg(
100 SecCertificateRef certRef
,
101 CSSM_ALGORITHMS
*signerAlg
)
104 CSSM_DATA_PTR fieldPtr
;
105 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
;
106 CSSM_ALGORITHMS sigAlg
;
109 * Extract the full signature algorithm OID
111 *signerAlg
= CSSM_ALGID_NONE
;
112 ortn
= SecCertificateCopyFirstFieldValue(certRef
,
113 &CSSMOID_X509V1SignatureAlgorithm
,
118 if(fieldPtr
->Length
!= sizeof(CSSM_X509_ALGORITHM_IDENTIFIER
)) {
119 sslErrorLog("sslCertSignerAlg() length error\n");
123 algId
= (CSSM_X509_ALGORITHM_IDENTIFIER
*)fieldPtr
->Data
;
124 if(!cssmOidToAlg(&algId
->algorithm
, &sigAlg
)) {
125 /* Only way this could happen is if we're given a bad cert */
126 sslErrorLog("sslCertSignerAlg() bad sigAlg OID\n");
132 * OK we have the full signature algorithm as a CSSM_ALGORITHMS.
133 * Extract the core signature alg.
137 case CSSM_ALGID_MD2WithRSA
:
138 case CSSM_ALGID_MD5WithRSA
:
139 case CSSM_ALGID_SHA1WithRSA
:
140 case CSSM_ALGID_SHA224WithRSA
:
141 case CSSM_ALGID_SHA256WithRSA
:
142 case CSSM_ALGID_SHA384WithRSA
:
143 case CSSM_ALGID_SHA512WithRSA
:
144 *signerAlg
= CSSM_ALGID_RSA
;
146 case CSSM_ALGID_SHA1WithECDSA
:
147 case CSSM_ALGID_SHA224WithECDSA
:
148 case CSSM_ALGID_SHA256WithECDSA
:
149 case CSSM_ALGID_SHA384WithECDSA
:
150 case CSSM_ALGID_SHA512WithECDSA
:
151 case CSSM_ALGID_ECDSA
:
152 case CSSM_ALGID_ECDSA_SPECIFIED
:
153 *signerAlg
= CSSM_ALGID_ECDSA
;
156 case CSSM_ALGID_SHA1WithDSA
:
157 *signerAlg
= CSSM_ALGID_DSA
;
160 sslErrorLog("sslCertSignerAlg() unknown sigAlg\n");
165 SecCertificateReleaseFirstFieldValue(certRef
,
166 &CSSMOID_X509V1SignatureAlgorithm
, fieldPtr
);
174 SSLCertificate
**destCert
, /* &ctx->{localCert,encryptCert} */
175 CSSM_KEY_PTR
*pubKey
, /* &ctx->signingPubKey, etc. */
176 SecKeyRef
*privKeyRef
, /* &ctx->signingPrivKeyRef, etc. */
177 CSSM_ALGORITHMS
*signerAlg
) /* optional */
181 SSLCertificate
*certChain
= NULL
;
182 SSLCertificate
*thisSslCert
;
184 SecIdentityRef identity
;
185 SecCertificateRef certRef
;
188 CSSM_CL_HANDLE clHand
; // carefully derive from a SecCertificateRef
190 CSSM_KEY_PTR
*pubKey
;
191 SecKeyRef
*privKeyRef
;
194 assert(destCert
!= NULL
); /* though its referent may be NULL */
195 assert(sslPubKey
!= NULL
);
196 assert(sslPrivKeyRef
!= NULL
);
198 pubKey
= &sslPubKey
->key
;
199 privKeyRef
= &sslPrivKey
->key
;
201 sslDeleteCertificateChain(*destCert
, ctx
);
207 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
208 return errSSLBadCert
;
210 numCerts
= CFArrayGetCount(certs
);
212 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
213 return errSSLBadCert
;
217 * Certs[0] is an SecIdentityRef from which we extract subject cert,
218 * privKeyRef, pubKey.
220 * 1. ensure the first element is a SecIdentityRef.
222 identity
= (SecIdentityRef
)CFArrayGetValueAtIndex(certs
, 0);
223 if(identity
== NULL
) {
224 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
227 if(CFGetTypeID(identity
) != SecIdentityGetTypeID()) {
228 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
233 * 2. Extract cert, keys and convert to local format.
235 ortn
= SecIdentityCopyCertificate(identity
, &certRef
);
237 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
240 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
242 sslErrorLog("parseIncomingCerts: bad cert array (4)\n");
245 /* enqueue onto head of cert chain */
246 thisSslCert
->next
= certChain
;
247 certChain
= thisSslCert
;
249 if(signerAlg
!= NULL
) {
250 ortn
= sslCertSignerAlg(certRef
, signerAlg
);
256 /* fetch private key from identity */
257 ortn
= SecIdentityCopyPrivateKey(identity
, &keyRef
);
259 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
263 *privKeyRef
= keyRef
;
265 /* obtain public key from cert */
266 ortn
= SecCertificateGetCLHandle(certRef
, &clHand
);
268 sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
272 certData
.Data
= thisSslCert
->derCert
.data
;
273 certData
.Length
= thisSslCert
->derCert
.length
;
274 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, pubKey
);
276 sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
277 return (OSStatus
)crtn
;
280 /* OK, that's the subject cert. Fetch optional remaining certs. */
282 * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
283 * Incoming certs have root last; SSLCertificate chain has root
286 for(cert
=1; cert
<numCerts
; cert
++) {
287 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, cert
);
288 if(certRef
== NULL
) {
289 sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
292 if(CFGetTypeID(certRef
) != SecCertificateGetTypeID()) {
293 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
297 /* Extract cert, convert to local format.
299 ortn
= secCertToSslCert(ctx
, certRef
, &thisSslCert
);
301 sslErrorLog("parseIncomingCerts: bad cert array (7)\n");
304 /* enqueue onto head of cert chain */
305 thisSslCert
->next
= certChain
;
306 certChain
= thisSslCert
;
310 *destCert
= certChain
;
313 /* free certChain, everything in it, other vars, return ortn */
314 sslDeleteCertificateChain(certChain
, ctx
);
315 /* FIXME - anything else? */
319 #else /* !USE_SSLCERTIFICATE */
325 CFArrayRef
*destCertChain
, /* &ctx->{localCertChain,encryptCertChain} */
326 SSLPubKey
**sslPubKey
, /* &ctx->signingPubKey, etc. */
327 SSLPrivKey
**sslPrivKey
, /* &ctx->signingPrivKeyRef, etc. */
328 CFIndex
*signerAlg
) /* optional */
331 CFIndex ix
, numCerts
;
332 SecIdentityRef identity
;
333 CFMutableArrayRef certChain
= NULL
; /* Retained */
334 SecCertificateRef leafCert
= NULL
; /* Retained */
335 SecKeyRef pubKey
= NULL
; /* Retained */
336 SecKeyRef privKey
= NULL
; /* Retained */
337 SecTrustRef trust
= NULL
; /* Retained */
338 SecTrustResultType trustResult
;
341 assert(destCertChain
!= NULL
); /* though its referent may be NULL */
342 assert(sslPubKey
!= NULL
);
343 assert(sslPrivKey
!= NULL
);
346 sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
347 ortn
= errSSLBadCert
;
350 numCerts
= CFArrayGetCount(certs
);
352 sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
353 ortn
= errSSLBadCert
;
358 * Certs[0] is an SecIdentityRef from which we extract subject cert,
361 * 1. ensure the first element is a SecIdentityRef.
363 identity
= (SecIdentityRef
)CFArrayGetValueAtIndex(certs
, 0);
364 if (identity
== NULL
) {
365 sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
369 if (CFGetTypeID(identity
) != SecIdentityGetTypeID()) {
370 sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
376 * 2. Extract cert, keys and convert to local format.
378 ortn
= SecIdentityCopyCertificate(identity
, &leafCert
);
380 sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
384 /* Fetch private key from identity */
385 ortn
= SecIdentityCopyPrivateKey(identity
, &privKey
);
387 sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
392 /* Convert the input array of SecIdentityRef at the start to an array of
394 certChain
= CFArrayCreateMutable(kCFAllocatorDefault
, numCerts
,
395 &kCFTypeArrayCallBacks
);
400 CFArrayAppendValue(certChain
, leafCert
);
401 for (ix
= 1; ix
< numCerts
; ++ix
) {
402 SecCertificateRef intermediate
=
403 (SecCertificateRef
)CFArrayGetValueAtIndex(certs
, ix
);
404 if (intermediate
== NULL
) {
405 sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
409 if (CFGetTypeID(intermediate
) != SecCertificateGetTypeID()) {
410 sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
415 CFArrayAppendValue(certChain
, intermediate
);
418 /* Obtain public key from cert */
420 ortn
= SecTrustCreateWithCertificates(certChain
, NULL
, &trust
);
423 SecPolicyRef policy
= SecPolicyCreateBasicX509();
424 ortn
= SecTrustCreateWithCertificates(certChain
, policy
, &trust
);
425 CFReleaseSafe(policy
);
427 /* We are only interested in getting the public key from the leaf
428 * cert here, so for best performance, don't try to build a chain
429 * or search any keychains.
431 CFArrayRef emptyArray
= CFArrayCreate(NULL
, NULL
, 0, NULL
);
432 (void)SecTrustSetAnchorCertificates(trust
, emptyArray
);
433 (void)SecTrustSetKeychains(trust
, emptyArray
);
434 CFReleaseSafe(emptyArray
);
439 sslErrorLog("parseIncomingCerts: SecTrustCreateWithCertificates err %d\n",
443 ortn
= SecTrustEvaluate(trust
, &trustResult
);
445 sslErrorLog("parseIncomingCerts: SecTrustEvaluate err %d\n",
449 pubKey
= SecTrustCopyPublicKey(trust
);
450 if (pubKey
== NULL
) {
451 sslErrorLog("parseIncomingCerts: SecTrustCopyPublicKey failed\n");
452 ortn
= -67712; // errSecInvalidKeyRef
458 CFReleaseSafe(trust
);
459 CFReleaseSafe(leafCert
);
460 CFReleaseSafe(*destCertChain
);
461 sslFreePubKey(sslPubKey
);
462 sslFreePrivKey(sslPrivKey
);
465 CFReleaseSafe(certChain
);
466 CFReleaseSafe(pubKey
);
467 CFReleaseSafe(privKey
);
469 *destCertChain
= NULL
;
471 *destCertChain
= certChain
;
472 *sslPubKey
= (SSLPubKey
*)pubKey
;
473 *sslPrivKey
= (SSLPrivKey
*)privKey
;
478 #endif /* !USE_SSLCERTIFICATE */