2 * Copyright (c) 2000-2001,2011,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 * Session_Crypto.cpp: CL session functions: sign, verify, CSSM_KEY extraction.
22 * Copyright (c) 2000,2011,2014 Apple Inc.
25 #include "AppleX509CLSession.h"
26 #include "DecodedCert.h"
27 #include "cldebugging.h"
28 #include "CSPAttacher.h"
29 #include "clNssUtils.h"
30 #include <Security/keyTemplates.h>
31 #include <security_asn1/nssUtils.h>
32 #include <Security/oidscert.h>
33 #include <Security/cssmapple.h>
36 * Given a DER-encoded cert, obtain a fully usable CSSM_KEY representing
37 * the cert's public key.
40 AppleX509CLSession::CertGetKeyInfo(
44 DecodedCert
decodedCert(*this, Cert
);
45 Key
= decodedCert
.extractCSSMKey(*this);
49 * Given a DER-encoded cert and a fully specified crypto context, verify
50 * cert's TBS and signature.
53 AppleX509CLSession::CertVerifyWithKey(
54 CSSM_CC_HANDLE CCHandle
,
55 const CssmData
&CertToBeVerified
)
57 CssmAutoData
tbs(*this);
58 CssmAutoData
algId(*this);
59 CssmAutoData
sig(*this);
60 CL_certCrlDecodeComponents(CertToBeVerified
, tbs
, algId
, sig
);
61 verifyData(CCHandle
, tbs
, sig
);
65 * Verify a DER-encoded cert, obtaining crypto context from either
66 * caller-specified context or by inference from SignerCert.
69 AppleX509CLSession::CertVerify(
70 CSSM_CC_HANDLE CCHandle
,
71 const CssmData
&CertToBeVerified
,
72 const CssmData
*SignerCert
,
73 const CSSM_FIELD
*VerifyScope
,
76 if((VerifyScope
!= NULL
) || (ScopeSize
!= 0)) {
77 CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED
);
79 if((CCHandle
== CSSM_INVALID_HANDLE
) && (SignerCert
== NULL
)) {
80 /* need one or the other */
81 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
84 /* get top-level components */
85 CssmAutoData
tbs(*this); // in DER format
86 CssmAutoData
algId(*this); // in DER format
87 CssmAutoData
sig(*this); // in DER format
88 CL_certCrlDecodeComponents(CertToBeVerified
, tbs
, algId
, sig
);
90 /* these must be explicitly freed upon exit */
91 CSSM_KEY_PTR signerPubKey
= NULL
;
92 CSSM_CONTEXT_PTR context
= NULL
;
93 CSSM_CSP_HANDLE cspHand
= CSSM_INVALID_HANDLE
;
94 CSSM_CC_HANDLE ourCcHand
= CSSM_INVALID_HANDLE
;
96 /* SignerCert optional; if present, obtain its subject key */
97 if(SignerCert
!= NULL
) {
98 CertGetKeyInfo(*SignerCert
, signerPubKey
);
101 /* signerPubKey must be explicitly freed in any case */
103 if(CCHandle
!= CSSM_INVALID_HANDLE
) {
105 * We'll use this CCHandle for the sig verify, but
106 * make sure it matches possible incoming SignerCert parameters
108 if(SignerCert
!= NULL
) {
111 /* extract signer's public key as a CSSM_KEY from context */
112 crtn
= CSSM_GetContext(CCHandle
, &context
);
114 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
116 CSSM_CONTEXT_ATTRIBUTE_PTR attr
;
117 crtn
= CSSM_GetContextAttribute(context
,
121 clErrorLog("CertVerify: valid CCHandle but no key!\n");
122 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
125 assert(signerPubKey
!= NULL
);
126 CSSM_KEY_PTR contextPubKey
= attr
->Attribute
.Key
;
127 if(contextPubKey
->KeyHeader
.AlgorithmId
!=
128 signerPubKey
->KeyHeader
.AlgorithmId
) {
129 clErrorLog("CertVerify: AlgorithmId mismatch!\n");
130 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
133 /* TBD - check key size, when we have a CSP which can report it */
134 /* TBD - anything else? */
135 } /* verifying multiple contexts */
136 /* OK to use CCHandle as is for verify context */
137 } /* valid CCHandle */
140 * All we have is signer cert. We already have its public key;
141 * get signature alg from CertToBeVerified's Cert.algID, which
142 * we currently have in DER form. Decode it into temp memory.
144 assert(SignerCert
!= NULL
);
145 assert(signerPubKey
!= NULL
);
147 CSSM_X509_ALGORITHM_IDENTIFIER cssmAlgId
;
151 CssmData
&algIdData
= algId
.get();
152 memset(&cssmAlgId
, 0, sizeof(cssmAlgId
));
153 prtn
= coder
.decode(algIdData
.data(), algIdData
.length(),
154 kSecAsn1AlgorithmIDTemplate
, &cssmAlgId
);
156 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT
);
159 CSSM_ALGORITHMS vfyAlg
= CL_oidToAlg(cssmAlgId
.algorithm
);
162 * Handle CSSMOID_ECDSA_WithSpecified, which requires additional
163 * decode to get the digest algorithm.
165 if(vfyAlg
== CSSM_ALGID_ECDSA_SPECIFIED
) {
166 vfyAlg
= CL_nssDecodeECDSASigAlgParams(cssmAlgId
.parameters
, coder
);
169 /* attach to CSP, cook up a context */
170 cspHand
= getGlobalCspHand(true);
172 crtn
= CSSM_CSP_CreateSignatureContext(cspHand
,
174 NULL
, // Access Creds
177 CCHandle
= ourCcHand
;
178 } /* inferring sig verify context from SignerCert */
179 verifyData(CCHandle
, tbs
, sig
);
182 /* FIXME - isn't there a better way to do this? Save the
183 * exception as a CSSM_RETURN and throw it if nonzero later?
185 if(context
!= NULL
) {
186 CSSM_FreeContext(context
);
188 CL_freeCSSMKey(signerPubKey
, *this);
189 if(ourCcHand
!= CSSM_INVALID_HANDLE
) {
190 CSSM_DeleteContext(ourCcHand
);
194 if(context
!= NULL
) {
195 CSSM_FreeContext(context
);
197 CL_freeCSSMKey(signerPubKey
, *this);
198 if(ourCcHand
!= CSSM_INVALID_HANDLE
) {
199 CSSM_DeleteContext(ourCcHand
);
204 * Given a DER-encoded TBSCert and a fully specified crypto context,
205 * sign the TBSCert and return the resulting DER-encoded Cert.
208 AppleX509CLSession::CertSign(
209 CSSM_CC_HANDLE CCHandle
,
210 const CssmData
&CertTemplate
,
211 const CSSM_FIELD
*SignScope
,
213 CssmData
&SignedCert
)
215 if((SignScope
!= NULL
) || (ScopeSize
!= 0)) {
216 CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED
);
218 if(CCHandle
== CSSM_INVALID_HANDLE
) {
219 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
222 /* cook up algId from context->(signing key, sig algorithm) */
223 CSSM_CONTEXT_PTR context
= NULL
; // must be freed
225 crtn
= CSSM_GetContext(CCHandle
, &context
);
227 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
229 CSSM_CONTEXT_ATTRIBUTE_PTR attr
; // not freed
230 crtn
= CSSM_GetContextAttribute(context
,
234 clErrorLog("CertSign: valid CCHandle but no signing key!\n");
235 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
237 CSSM_KEY_PTR signingKey
= attr
->Attribute
.Key
;
238 if(signingKey
== NULL
) {
239 clErrorLog("CertSign: valid CCHandle, NULL signing key!\n");
240 CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE
);
243 CssmAutoData
encAlgId(*this);
244 CssmAutoData
rawSig(*this);
245 CssmAutoData
fullCert(*this);
248 * FIXME: we really should break up the template and ensure that its
249 * signature algId matches the one we're signing with, or just use
250 * that algId here....for now, this is up to the app to make sure.
253 /* temp allocs/encode into here */
256 /* CSSM alg --> CSSM_X509_ALGORITHM_IDENTIFIER */
258 *** Note: some ECDSA implementations use CSSMOID_ECDSA_WithSpecified for
259 *** the algorithm followed by an encoded digest algorithm. We'll handle
260 *** that on *decode* but we're going to do it the sensible way - with
261 *** one unique OID to specify the whole thing (e.g. CSSMOID_ECDSA_WithSHA512
262 *** which we get from cssmAlgToOid()) unless we're forced to do
263 *** otherwise by cranky servers.
265 CSSM_X509_ALGORITHM_IDENTIFIER algId
;
266 memset(&algId
, 0, sizeof(algId
));
267 const CSSM_OID
*oid
= cssmAlgToOid(context
->AlgorithmType
);
270 clErrorLog("CertSIgn: unknown alg (%u)\n",
271 (unsigned)context
->AlgorithmType
);
272 CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT
);
274 algId
.algorithm
= *oid
;
276 /* NULL params - skip for ECDSA */
277 switch(context
->AlgorithmType
) {
278 case CSSM_ALGID_SHA1WithECDSA
:
279 case CSSM_ALGID_SHA224WithECDSA
:
280 case CSSM_ALGID_SHA256WithECDSA
:
281 case CSSM_ALGID_SHA384WithECDSA
:
282 case CSSM_ALGID_SHA512WithECDSA
:
283 case CSSM_ALGID_ECDSA_SPECIFIED
:
286 CL_nullAlgParams(algId
);
289 /* DER-encode the algID */
291 prtn
= SecNssEncodeItemOdata(&algId
, kSecAsn1AlgorithmIDTemplate
,
294 CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR
);
297 /* sign TBS --> rawSig */
298 signData(CCHandle
, CertTemplate
, rawSig
);
299 /* put it all together */
300 CL_certEncodeComponents(CertTemplate
, encAlgId
, rawSig
, fullCert
);
303 CSSM_FreeContext(context
);
306 CSSM_FreeContext(context
);
307 SignedCert
= fullCert
.release();
310 /*** Private functions ***/
313 * Sign a CssmData with the specified signing context. Used for
314 * signing both certs and CRLs; this routine doesn't know anything
318 AppleX509CLSession::signData(
319 CSSM_CC_HANDLE ccHand
,
321 CssmOwnedData
&sig
) // mallocd and returned
326 crtn
= CSSM_SignData(
330 CSSM_ALGID_NONE
, // DigestAlgorithm,
333 clErrorLog("AppleX509CLSession::CSSM_SignData: %ld\n", (long)crtn
);
334 CssmError::throwMe(crtn
);
340 * Verify a block of data given a crypto context and a signature.
341 * Used for verifying certs and CRLs. Returns a CSSM_RETURN (callers
342 * always need to clean up after calling us).
344 void AppleX509CLSession::verifyData(
345 CSSM_CC_HANDLE ccHand
,
351 crtn
= CSSM_VerifyData(ccHand
,
354 CSSM_ALGID_NONE
, // Digest alg
357 if(crtn
== CSSMERR_CSP_VERIFY_FAILED
) {
358 /* CSP and CL report this differently */
359 CssmError::throwMe(CSSMERR_CL_VERIFICATION_FAILURE
);
362 CssmError::throwMe(crtn
);