2 * Copyright (c) 2000-2004 Apple Computer, 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 * SecImportExportPkcs8.cpp - support for generating and parsing/decoding
26 * private keys in PKCS8 format.
28 * The current version (as of March 12 2004) can parse and decode every
29 * PKCS8 blob generated by openssl with the exception of those using
30 * double DES encryption. This has been verified by actually generating
31 * those blobs with openssl and decoding them here.
33 * PLEASE: don't even *think* about changing a single line of code here
34 * without verifying the results against the full import/export regression
35 * test in SecurityTests/clxutils/importExport.
39 #include <Security/SecImportExport.h>
40 #include "SecImportExportPkcs8.h"
41 #include "SecPkcs8Templates.h"
42 #include "SecImportExportUtils.h"
43 #include "SecImportExportCrypto.h"
44 #include <security_pkcs12/pkcs12Utils.h>
45 #include <security_pkcs12/pkcs12Crypto.h>
46 #include <security_asn1/SecNssCoder.h>
47 #include <Security/keyTemplates.h>
48 #include <Security/SecAsn1Templates.h>
49 #include <Security/secasn1t.h>
50 #include <security_asn1/nssUtils.h>
51 #include <security_utilities/debugging.h>
52 #include <security_utilities/devrandom.h>
53 #include <Security/oidsalg.h>
54 #include <Security/SecKeyPriv.h>
55 #include <security_cdsa_utils/cuCdsaUtils.h>
56 #include <openssl/pem.h>
58 #include <Security/SecBase.h>
60 #define SecPkcs8Dbg(args...) secdebug("SecPkcs8", ## args)
62 #pragma mark --- PKCS5 v1.5 Key Derivation ---
65 * PKCS5 v1.5. Caller has gleaned everything except salt,
66 * iterationCount, and IV from the AlgId.algorithm OID.
68 * We get salt and iteration count from the incoming alg params.
69 * IV is derived along with the unwrapping key from the passphrase.
71 static CSSM_RETURN
pkcs5_v15_genKey(
72 CSSM_CSP_HANDLE cspHand
,
74 const SecKeyImportExportParameters
*keyParams
,
75 const CSSM_DATA
¶mData
,
76 CSSM_ALGORITHMS keyAlg
,
77 CSSM_ALGORITHMS pbeHashAlg
,
79 uint32 blockSizeInBytes
,
80 impExpKeyUnwrapParams
*unwrapParams
)
82 CSSM_KEY
*passKey
= NULL
;
83 CFDataRef cfPhrase
= NULL
;
86 CSSM_CRYPTO_DATA seed
;
87 CSSM_CC_HANDLE ccHand
= 0;
88 CSSM_ACCESS_CREDENTIALS creds
;
91 /* passphrase or passkey? */
92 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_Data
, VP_Import
,
93 (CFTypeRef
*)&cfPhrase
, &passKey
);
97 /* subsequent errors to errOut: */
99 memset(&seed
, 0, sizeof(seed
));
100 if(cfPhrase
!= NULL
) {
101 size_t len
= CFDataGetLength(cfPhrase
);
102 coder
.allocItem(seed
.Param
, len
);
103 memmove(seed
.Param
.Data
, CFDataGetBytePtr(cfPhrase
), len
);
107 /* hash algorithm --> PBE alg for CSP */
108 CSSM_ALGORITHMS pbeAlg
;
111 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_MD2
;
114 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_MD5
;
116 case CSSM_ALGID_SHA1
:
117 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_SHA1
;
120 /* really shouldn't happen - pbeHashAlg was inferred by
121 * pkcsOidToParams() */
122 SecPkcs8Dbg("PKCS8: PKCS5 v1/5 bogus hash alg");
123 crtn
= CSSMERR_CSP_INTERNAL_ERROR
;
127 /* Salt and iteration count from alg parameters */
128 impExpPKCS5_PBE_Parameters pbeParams
;
129 memset(&pbeParams
, 0, sizeof(pbeParams
));
130 if(coder
.decodeItem(paramData
, impExpPKCS5_PBE_ParametersTemplate
, &pbeParams
)) {
131 SecPkcs8Dbg("PKCS8: PKCS5 v1.5 pbeParams decode error");
132 crtn
= errSecUnknownFormat
;
136 if(!p12DataToInt(pbeParams
.iterations
, iterCount
)) {
137 SecPkcs8Dbg("PKCS8: bad PKCS5 v1.5 iteration count");
138 crtn
= errSecUnknownFormat
;
142 /* ask for hard coded 8 bytes of IV */
143 coder
.allocItem(unwrapParams
->iv
, 8);
145 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
146 crtn
= CSSM_CSP_CreateDeriveKeyContext(cspHand
,
157 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
161 memset(unwrapParams
->unwrappingKey
, 0, sizeof(CSSM_KEY
));
163 CSSM_DATA dummyLabel
;
164 dummyLabel
.Data
= (uint8
*)"temp unwrap key";
165 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
167 crtn
= CSSM_DeriveKey(ccHand
,
168 &unwrapParams
->iv
, // IV returned in in/out Param
170 /* not extractable even for the short time this key lives */
171 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
,
173 NULL
, // cred and acl
174 unwrapParams
->unwrappingKey
);
176 SecPkcs8Dbg("PKCS8: PKCS5 v1.5 CSSM_DeriveKey failure");
180 CSSM_DeleteContext(ccHand
);
182 if(passKey
!= NULL
) {
183 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
189 #pragma mark --- PKCS5 v2.0 Key Derivation ---
192 * PKCS5 v2.0 has different means of encoding algorithm parameters,
193 * depending on the encryption algorithm.
196 * Obtain encryption parameters for PKCS5 v2.0, DES and DES3 variants.
198 static OSStatus
pkcs5_DES_params(
199 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
201 impExpKeyUnwrapParams
*unwrapParams
,
202 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
203 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
206 /* Params is iv as OCTET STRING */
207 if(coder
.decodeItem(paramData
, kSecAsn1OctetStringTemplate
, &unwrapParams
->iv
)) {
208 SecPkcs8Dbg("PKCS8: PKCS5 v2 DES init vector decode error");
209 return errSecUnknownFormat
;
211 if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_DES_EDE3_CBC
)) {
212 *keyAlg
= CSSM_ALGID_3DES_3KEY
;
213 unwrapParams
->encrAlg
= CSSM_ALGID_3DES_3KEY_EDE
;
214 if(*keySizeInBits
== 0) {
215 *keySizeInBits
= 3 * 64;
219 *keyAlg
= CSSM_ALGID_DES
;
220 unwrapParams
->encrAlg
= CSSM_ALGID_DES
;
221 if(*keySizeInBits
== 0) {
225 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
226 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
227 return errSecSuccess
;
231 * Obtain encryption parameters for PKCS5 v2.0, RC2 variant.
233 static OSStatus
pkcs5_RC2_params(
234 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
235 impExpKeyUnwrapParams
*unwrapParams
,
236 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
237 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
240 /* Params is impExpPKCS5_RC2Params */
241 impExpPKCS5_RC2Params rc2Params
;
242 memset(&rc2Params
, 0, sizeof(rc2Params
));
243 if(coder
.decodeItem(paramData
, impExpPKCS5_RC2ParamsTemplate
, &rc2Params
)) {
244 SecPkcs8Dbg("PKCS8: PKCS5 v2 RC2 params decode error");
245 return errSecUnknownFormat
;
248 *keyAlg
= CSSM_ALGID_RC2
;
249 unwrapParams
->encrAlg
= CSSM_ALGID_RC2
;
250 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
251 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
253 /* the version actually maps to effective key size like this */
254 /* I swear all of this is in the PKCS5 v2.0 spec */
255 unwrapParams
->effectiveKeySizeInBits
= 32; // default
256 if(rc2Params
.version
.Data
) {
258 if(!p12DataToInt(rc2Params
.version
, v
)) {
259 SecPkcs8Dbg("PKCS8: bad PKCS5 rc2Params.version");
260 return errSecUnknownFormat
;
264 unwrapParams
->effectiveKeySizeInBits
= 40;
267 unwrapParams
->effectiveKeySizeInBits
= 64;
270 unwrapParams
->effectiveKeySizeInBits
= 128;
274 unwrapParams
->effectiveKeySizeInBits
= v
;
277 /* not in the spec, use as zero */
282 unwrapParams
->iv
= rc2Params
.iv
;
284 /* the PKCS5 spec does not give a default for the RC2 key size */
285 if(*keySizeInBits
== 0) {
286 SecPkcs8Dbg("PKCS8: NO RC2 DEFAULT KEYSIZE!");
287 return errSecUnknownFormat
;
289 return errSecSuccess
;
293 * Infer encryption parameters for PKCS5 v2.0, RC5 variant.
294 * All info contained in encryptionScheme.parameters.
296 static OSStatus
pkcs5_RC5_params(
297 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
298 impExpKeyUnwrapParams
*unwrapParams
,
299 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
300 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
303 /* Params is a impExpPKCS5_RC5Params */
304 impExpPKCS5_RC5Params rc5Params
;
305 memset(&rc5Params
, 0, sizeof(rc5Params
));
306 if(coder
.decodeItem(paramData
, impExpPKCS5_RC5ParamsTemplate
, &rc5Params
)) {
307 SecPkcs8Dbg("PKCS8: PKCS5 v2 RC5 params decode error");
308 return errSecUnknownFormat
;
311 *keyAlg
= CSSM_ALGID_RC5
;
312 unwrapParams
->encrAlg
= CSSM_ALGID_RC5
;
313 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
314 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
316 if(rc5Params
.rounds
.Data
) {
317 if(!p12DataToInt(rc5Params
.rounds
, unwrapParams
->rounds
)) {
318 SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.rounds");
319 return errSecUnknownFormat
;
322 if(rc5Params
.blockSizeInBits
.Data
) {
323 if(!p12DataToInt(rc5Params
.blockSizeInBits
, unwrapParams
->blockSizeInBits
)) {
324 SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.blockSizeInBits");
325 return errSecUnknownFormat
;
329 /* Spec says default iv is zeroes */
330 unwrapParams
->iv
= rc5Params
.iv
;
331 if(unwrapParams
->iv
.Length
== 0) {
332 uint32 len
= unwrapParams
->blockSizeInBits
/ 8;
333 coder
.allocItem(unwrapParams
->iv
, len
);
334 memset(unwrapParams
->iv
.Data
, 0, len
);
338 * Spec does not give a default for key RC5 size, and openssl doesn't
339 * support RC5 for PKCS8.
341 if(*keySizeInBits
== 0) {
342 SecPkcs8Dbg("PKCS8: NO RC5 DEFAULT KEYSIZE!");
343 return errSecUnknownFormat
;
345 return errSecSuccess
;
349 * Common code to derive a wrap/unwrap key using PBKDF2 (i.e., using PKCS5 v2.0
350 * key derivation). Caller must CSSM_FreeKey when done.
352 static CSSM_RETURN
pbkdf2DeriveKey(
353 CSSM_CSP_HANDLE cspHand
,
355 CSSM_ALGORITHMS keyAlg
,
356 uint32 keySizeInBits
,
357 uint32 iterationCount
,
358 const CSSM_DATA
&salt
,
359 const SecKeyImportExportParameters
*keyParams
, // required
360 impExpVerifyPhrase verifyPhrase
, // for secure passphrase
361 CSSM_KEY_PTR symKey
) // RETURNED
363 CSSM_KEY
*passKey
= NULL
;
364 CFDataRef cfPhrase
= NULL
;
365 CSSM_PKCS5_PBKDF2_PARAMS pbeParams
;
368 CSSM_DATA dummyLabel
;
371 CSSM_CC_HANDLE ccHand
= 0;
372 CSSM_ACCESS_CREDENTIALS creds
;
374 memset(&pbeParams
, 0, sizeof(pbeParams
));
376 /* passphrase or passkey? */
377 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_Data
, verifyPhrase
,
378 (CFTypeRef
*)&cfPhrase
, &passKey
);
382 /* subsequent errors to errOut: */
384 if(cfPhrase
!= NULL
) {
385 size_t len
= CFDataGetLength(cfPhrase
);
386 coder
.allocItem(pbeParams
.Passphrase
, len
);
387 memmove(pbeParams
.Passphrase
.Data
,
388 CFDataGetBytePtr(cfPhrase
), len
);
392 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
393 crtn
= CSSM_CSP_CreateDeriveKeyContext(cspHand
,
394 CSSM_ALGID_PKCS5_PBKDF2
,
404 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
408 memset(symKey
, 0, sizeof(CSSM_KEY
));
410 /* not extractable even for the short time this key lives */
411 keyAttr
= CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
;
412 dummyLabel
.Data
= (uint8
*)"temp unwrap key";
413 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
415 pbeParams
.PseudoRandomFunction
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
416 pbeData
.Data
= (uint8
*)&pbeParams
;
417 pbeData
.Length
= sizeof(pbeParams
);
418 crtn
= CSSM_DeriveKey(ccHand
,
423 NULL
, // cred and acl
426 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_DeriveKey failure");
430 CSSM_DeleteContext(ccHand
);
432 if(passKey
!= NULL
) {
433 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
440 * Obtain PKCS5, v.2.0 key derivation and encryption parameters and
441 * derive the key. This one obtains all of the crypt parameters
442 * from the top-level AlgId.Params. What a mess.
444 static CSSM_RETURN
pkcs5_v2_genKey(
445 CSSM_CSP_HANDLE cspHand
,
447 const CSSM_DATA
¶mData
,
448 const SecKeyImportExportParameters
*keyParams
,
449 impExpKeyUnwrapParams
*unwrapParams
)
451 SecPkcs8Dbg("PKCS8: generating PKCS5 v2.0 key");
453 CSSM_ALGORITHMS keyAlg
= CSSM_ALGID_NONE
;
454 uint32 prf
= 0; // CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1...
456 /* caller should check */
457 assert(keyParams
!= NULL
);
459 /* AlgId.Params --> impExpPKCS5_PBES2_Params */
460 if(paramData
.Length
== 0) {
461 SecPkcs8Dbg("PKCS8: empty PKCS5 v2 pbes2Params");
462 return errSecUnknownFormat
;
464 impExpPKCS5_PBES2_Params pbes2Params
;
465 memset(&pbes2Params
, 0, sizeof(pbes2Params
));
466 if(coder
.decodeItem(paramData
, impExpPKCS5_PBES2_ParamsTemplate
, &pbes2Params
)) {
467 SecPkcs8Dbg("PKCS8: PKCS5 v2 pbes2Params decode error");
468 return errSecUnknownFormat
;
472 * As far as I know the keyDerivationFunc OID must be id-PBKDF2
474 if(!nssCompareCssmData(&pbes2Params
.keyDerivationFunc
.algorithm
,
475 &CSSMOID_PKCS5_PBKDF2
)) {
476 SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected keyDerivationFunc alg");
477 return errSecUnknownFormat
;
481 * The params of the keyDerivationFunc algId are an encoded
482 * impExpPKCS5_PBKDF2_Params.
484 impExpPKCS5_PBKDF2_Params pbkdf2Params
;
485 memset(&pbkdf2Params
, 0, sizeof(pbkdf2Params
));
486 if(coder
.decodeItem(pbes2Params
.keyDerivationFunc
.parameters
,
487 impExpPKCS5_PBKDF2_ParamsTemplate
, &pbkdf2Params
)) {
488 SecPkcs8Dbg("PKCS8: PKCS5 v2 pbkdf2Params decode error");
489 return errSecUnknownFormat
;
493 * Salt and iteration count from the impExpPKCS5_PBKDF2_Params (ignoring the
494 * possible CHOICE for salt source).
496 CSSM_DATA salt
= pbkdf2Params
.salt
;
498 if(!p12DataToInt(pbkdf2Params
.iterationCount
, iterCount
)) {
499 SecPkcs8Dbg("PKCS8: bad PKCS5 v2 iteration count");
500 return errSecUnknownFormat
;
504 * Key size optional, use defaults per alg (later) if it's not there
506 uint32 keySizeInBits
= 0;
507 if(pbkdf2Params
.keyLengthInBytes
.Data
) {
508 uint32 keyLengthInBytes
;
509 if(!p12DataToInt(pbkdf2Params
.keyLengthInBytes
, keyLengthInBytes
)) {
510 SecPkcs8Dbg("PKCS8: bad PKCS5 v2 key size");
511 return errSecUnknownFormat
;
513 keySizeInBits
= keyLengthInBytes
* 8;
515 /* else we'll infer key size from the encryption algorithm */
517 /* prf optional, but if it's there it better be CSSMOID_PKCS5_HMAC_SHA1 */
518 if(pbkdf2Params
.prf
.Data
) {
519 if(!nssCompareCssmData(&pbkdf2Params
.prf
, &CSSMOID_PKCS5_HMAC_SHA1
)) {
520 SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected prf OID");
521 return errSecUnknownFormat
;
524 prf
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
527 * Now process the encryptionScheme, which is even messier - the algParams
528 * varies per encryption algorithm.
530 CSSM_X509_ALGORITHM_IDENTIFIER
&encrScheme
= pbes2Params
.encryptionScheme
;
531 CSSM_OID
*encrOid
= &encrScheme
.algorithm
;
533 CSSM_DATA
&encrParam
= encrScheme
.parameters
;
535 if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_DES_EDE3_CBC
) ||
536 nssCompareCssmData(encrOid
, &CSSMOID_DES_CBC
)) {
537 ortn
= pkcs5_DES_params(encrParam
, encrOid
, unwrapParams
, &keyAlg
,
538 &keySizeInBits
, coder
);
543 else if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_RC2_CBC
)) {
544 ortn
= pkcs5_RC2_params(encrParam
, unwrapParams
, &keyAlg
,
545 &keySizeInBits
, coder
);
550 else if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_RC5_CBC
)) {
551 ortn
= pkcs5_RC5_params(encrParam
, unwrapParams
, &keyAlg
,
552 &keySizeInBits
, coder
);
558 SecPkcs8Dbg("PKCS8: PKCS5 v2 unknown encrScheme.algorithm");
559 return errSecUnknownFormat
;
562 /* We should be ready to go */
563 assert(keyAlg
!= CSSM_ALGID_NONE
);
564 assert(unwrapParams
->encrAlg
!= CSSM_ALGID_NONE
);
566 /* use all the stuff we just figured out to derive a symmetric decryption key */
567 return pbkdf2DeriveKey(cspHand
, coder
,
568 keyAlg
, keySizeInBits
,
572 unwrapParams
->unwrappingKey
);
575 #pragma mark --- PKCS12 Key Derivation ---
578 * PKCS12 style key derivation. Caller has gleaned everything except
579 * salt, iterationCount, and IV from the AlgId.algorithm OID.
581 * We get salt and iteration count from the incoming alg params.
582 * IV is derived along with the unwrapping key from the passphrase.
584 static CSSM_RETURN
pkcs12_genKey(
585 CSSM_CSP_HANDLE cspHand
,
587 const SecKeyImportExportParameters
*keyParams
,
588 const CSSM_DATA
¶mData
, // from algID
589 CSSM_ALGORITHMS keyAlg
, // valid on entry
590 CSSM_ALGORITHMS pbeHashAlg
, // valid on entry
591 uint32 keySizeInBits
, // valid on entry
592 uint32 blockSizeInBytes
, // for IV
593 impExpKeyUnwrapParams
*unwrapParams
)
595 SecPkcs8Dbg("PKCS8: generating PKCS12 key");
597 assert(keyAlg
!= CSSM_ALGID_NONE
);
598 assert(pbeHashAlg
!= CSSM_ALGID_NONE
);
599 assert(keySizeInBits
!= 0);
601 /* get iteration count, salt from alg params */
602 NSS_P12_PBE_Params pbeParams
;
604 if(paramData
.Length
== 0) {
605 SecPkcs8Dbg("PKCS8: empty P12 pbeParams");
606 return errSecUnknownFormat
;
608 memset(&pbeParams
, 0, sizeof(pbeParams
));
609 if(coder
.decodeItem(paramData
, NSS_P12_PBE_ParamsTemplate
, &pbeParams
)) {
610 SecPkcs8Dbg("PKCS8: P12 pbeParams decode error");
611 return errSecUnknownFormat
;
614 uint32 iterCount
= 0;
615 if(!p12DataToInt(pbeParams
.iterations
, iterCount
)) {
616 SecPkcs8Dbg("PKCS8: bad P12 iteration count");
617 return errSecUnknownFormat
;
620 /* passphrase or passkey? */
621 CSSM_KEY
*passKey
= NULL
;
622 CFStringRef phraseStr
= NULL
;
623 CSSM_DATA phraseData
= {0, NULL
};
624 CSSM_DATA
*phraseDataP
= NULL
;
628 assert(keyParams
!= NULL
);
630 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_String
, VP_Import
,
631 (CFTypeRef
*)&phraseStr
, &passKey
);
635 /* subsequent errors to errOut: */
637 if(phraseStr
!= NULL
) {
638 /* convert to CSSM_DATA for use with p12KeyGen() */
640 p12ImportPassPhrase(phraseStr
, coder
, phraseData
);
643 SecPkcs8Dbg("PKCS8: p12ImportPassPhrase threw");
644 crtn
= errSecAllocate
;
647 CFRelease(phraseStr
);
648 phraseDataP
= &phraseData
;
651 /* use p12 module to cook up the key and IV */
652 if(blockSizeInBytes
) {
653 coder
.allocItem(unwrapParams
->iv
, blockSizeInBytes
);
655 crtn
= p12KeyGen(cspHand
,
656 *unwrapParams
->unwrappingKey
,
667 SecPkcs8Dbg("PKCS8: p12KeyGen failed");
670 if(passKey
!= NULL
) {
671 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
677 #pragma mark --- Public PKCS8 import function ---
680 * Called out from SecImportRep::importWrappedKey().
681 * If cspHand is provided instead of importKeychain, the CSP
682 * handle MUST be for the CSPDL, not for the raw CSP.
684 OSStatus
impExpPkcs8Import(
686 SecKeychainRef importKeychain
, // optional
687 CSSM_CSP_HANDLE cspHand
, // required
688 SecItemImportExportFlags flags
,
689 const SecKeyImportExportParameters
*keyParams
, // REQUIRED for unwrap
690 CFMutableArrayRef outArray
) // optional, append here
693 CSSM_KEYHEADER
&hdr
= wrappedKey
.KeyHeader
;
694 CSSM_RETURN crtn
= CSSM_OK
;
696 /* key derivation and encryption parameters gleaned from alg ID */
697 impExpKeyUnwrapParams unwrapParams
;
698 memset(&unwrapParams
, 0, sizeof(unwrapParams
));
699 CSSM_ALGORITHMS keyAlg
= CSSM_ALGID_NONE
;
700 CSSM_ALGORITHMS pbeHashAlg
= CSSM_ALGID_NONE
; // SHA1 or MD5
701 uint32 keySizeInBits
;
702 uint32 blockSizeInBytes
;
703 PKCS_Which pkcs
= PW_None
;
705 if( (keyParams
== NULL
) ||
706 ( (keyParams
->passphrase
== NULL
) &&
707 !(keyParams
->flags
& kSecKeySecurePassphrase
) ) ) {
708 /* passphrase mandatory */
709 return errSecPassphraseRequired
;
711 assert(cspHand
!= 0);
717 NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo
;
719 memset(&encrPrivKeyInfo
, 0, sizeof(encrPrivKeyInfo
));
720 if(coder
.decode(CFDataGetBytePtr(inData
),
721 CFDataGetLength(inData
),
722 kSecAsn1EncryptedPrivateKeyInfoTemplate
, &encrPrivKeyInfo
)) {
723 SecImpExpDbg("impExpPkcs8Import: error decoding top-level encrPrivKeyInfo");
724 return errSecUnknownFormat
;
728 * The algorithm OID of that top-level struct is the key piece of info
732 found
= pkcsOidToParams(&encrPrivKeyInfo
.algorithm
.algorithm
,
733 keyAlg
, unwrapParams
.encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
734 unwrapParams
.encrPad
, unwrapParams
.encrMode
, pkcs
);
736 SecImpExpDbg("impExpPkcs8Import: unknown OID in top-level encrPrivKeyInfo");
737 return errSecUnknownFormat
;
741 * Each PBE method has its own way of filling in the remaining gaps
742 * in impExpKeyUnwrapParams and generating a key.
744 CSSM_KEY unwrappingKey
;
745 memset(&unwrappingKey
, 0, sizeof(unwrappingKey
));
746 unwrapParams
.unwrappingKey
= &unwrappingKey
;
747 CSSM_DATA
¶mData
= encrPrivKeyInfo
.algorithm
.parameters
;
751 /* we have everything except iv, iterations, salt */
752 crtn
= pkcs5_v15_genKey(cspHand
, coder
, keyParams
, paramData
,
753 keyAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
758 /* obtain everything, including iv, from alg params */
759 crtn
= pkcs5_v2_genKey(cspHand
, coder
, paramData
, keyParams
, &unwrapParams
);
762 /* we have everything except iv, iterations, salt */
763 crtn
= pkcs12_genKey(cspHand
, coder
, keyParams
, paramData
,
764 keyAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
768 /* satisfy compiler */
770 return errSecUnknownFormat
;
773 SecPkcs8Dbg("PKCS8: key derivation failed");
777 /* we should be ready to rock'n'roll no matter how we got here */
778 assert(unwrapParams
.encrAlg
!= CSSM_ALGID_NONE
);
779 assert(unwrappingKey
.KeyData
.Data
!= NULL
);
780 assert(unwrappingKey
.KeyHeader
.AlgorithmId
!= CSSM_ALGID_NONE
);
782 /* set up key to unwrap */
783 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
784 hdr
.HeaderVersion
= CSSM_KEYHEADER_VERSION
;
785 /* CspId : don't care */
786 hdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
787 hdr
.Format
= CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
;
788 /* AlgorithmId : inferred by CSP */
789 hdr
.AlgorithmId
= CSSM_ALGID_NONE
;
790 hdr
.KeyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
791 /* LogicalKeySizeInBits : calculated by CSP during unwrap */
792 hdr
.KeyAttr
= CSSM_KEYATTR_EXTRACTABLE
;
793 hdr
.KeyUsage
= CSSM_KEYUSE_ANY
;
795 wrappedKey
.KeyData
= encrPrivKeyInfo
.encryptedData
;
797 crtn
= impExpImportKeyCommon(
804 NULL
, // default label
806 CSSM_FreeKey(cspHand
, NULL
, &unwrappingKey
, CSSM_FALSE
);
810 #pragma mark --- Public PKCS8 export function ---
812 #define PKCS5_V2_SALT_LEN 8
813 #define PKCS5_V2_ITERATIONS 2048
814 #define PKCS5_V2_DES_IV_SIZE 8
817 * Unlike impExpPkcs8Import(), which can handle every PBE algorithm in the spec
818 * and implemented by openssl, this one has a fixed PBE and encryption scheme.
819 * We do not provide a means at the API for the client to specify these.
821 * We generate blobs with triple DES encryption, with PKCS5 v2.0 key
824 OSStatus
impExpPkcs8Export(
826 SecItemImportExportFlags flags
,
827 const SecKeyImportExportParameters
*keyParams
, // optional
828 CFMutableDataRef outData
, // output appended here
829 const char **pemHeader
)
831 DevRandomGenerator rng
;
833 impExpPKCS5_PBES2_Params pbes2Params
;
834 CSSM_X509_ALGORITHM_IDENTIFIER
&keyDeriveAlgId
= pbes2Params
.keyDerivationFunc
;
835 CSSM_ATTRIBUTE_TYPE formatAttrType
= CSSM_ATTRIBUTE_NONE
;
836 CSSM_KEYBLOB_FORMAT blobForm
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
837 const CSSM_KEY
*cssmKey
;
839 if(keyParams
== NULL
) {
842 assert(secKey
!= NULL
);
843 assert(outData
!= NULL
);
845 memset(&pbes2Params
, 0, sizeof(pbes2Params
));
849 * parameters is an encoded impExpPKCS5_PBKDF2_Params
850 * We generate random salt
852 keyDeriveAlgId
.algorithm
= CSSMOID_PKCS5_PBKDF2
;
853 impExpPKCS5_PBKDF2_Params pbkdf2Params
;
854 memset(&pbkdf2Params
, 0, sizeof(pbkdf2Params
));
855 coder
.allocItem(pbkdf2Params
.salt
, PKCS5_V2_SALT_LEN
);
856 rng
.random(pbkdf2Params
.salt
.Data
, PKCS5_V2_SALT_LEN
);
857 p12IntToData(PKCS5_V2_ITERATIONS
, pbkdf2Params
.iterationCount
, coder
);
858 /* leave pbkdf2Params.keyLengthInBytes NULL for default */
859 /* openssl can't handle this, which is the default value:
860 pbkdf2Params.prf = CSSMOID_PKCS5_HMAC_SHA1;
863 coder
.encodeItem(&pbkdf2Params
, impExpPKCS5_PBKDF2_ParamsTemplate
,
864 keyDeriveAlgId
.parameters
);
868 * parameters is an encoded OCTET STRING containing the (random) IV
870 CSSM_X509_ALGORITHM_IDENTIFIER
&encrScheme
= pbes2Params
.encryptionScheme
;
871 encrScheme
.algorithm
= CSSMOID_PKCS5_DES_EDE3_CBC
;
872 CSSM_DATA rawIv
= {0, NULL
};
873 coder
.allocItem(rawIv
, PKCS5_V2_DES_IV_SIZE
);
874 rng
.random(rawIv
.Data
, PKCS5_V2_DES_IV_SIZE
);
876 coder
.encodeItem(&rawIv
, kSecAsn1OctetStringTemplate
,
877 encrScheme
.parameters
);
880 * Top level NSS_EncryptedPrivateKeyInfo, whose parameters is the encoded
881 * impExpPKCS5_PBES2_Params.
883 NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo
;
884 memset(&encrPrivKeyInfo
, 0, sizeof(encrPrivKeyInfo
));
885 CSSM_X509_ALGORITHM_IDENTIFIER
&topAlgId
= encrPrivKeyInfo
.algorithm
;
886 topAlgId
.algorithm
= CSSMOID_PKCS5_PBES2
;
887 coder
.encodeItem(&pbes2Params
, impExpPKCS5_PBES2_ParamsTemplate
,
888 topAlgId
.parameters
);
891 * Now all we have to do is generate the encrypted key data itself.
892 * When doing a WrapKey op in PKCS8 form, the CSP gives us the
893 * NSS_EncryptedPrivateKeyInfo.encryptedData values.
896 /* we need a CSPDL handle - try to get it from the key */
897 CSSM_CSP_HANDLE cspdlHand
= 0;
899 bool releaseCspHand
= false;
900 CSSM_DATA encodedKeyInfo
= {0, NULL
};
902 ortn
= SecKeyGetCSPHandle(secKey
, &cspdlHand
);
904 cspdlHand
= cuCspStartup(CSSM_FALSE
);
906 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
908 releaseCspHand
= true;
910 /* subsequent errors to errOut: */
912 /* get wrapping key from parameters we just set up */
913 CSSM_KEY wrappingKey
;
914 memset(&wrappingKey
, 0, sizeof(CSSM_KEY
));
915 CSSM_RETURN crtn
= pbkdf2DeriveKey(cspdlHand
, coder
,
916 CSSM_ALGID_3DES_3KEY
, 3 * 64,
917 PKCS5_V2_ITERATIONS
, pbkdf2Params
.salt
,
926 * Special case for DSA, ECDSA: specify that the raw blob, pre-encrypt, is in
927 * the PKCS8 PrivateKeyInfo format that openssl understands. The
930 crtn
= SecKeyGetCSSMKey(secKey
, &cssmKey
);
932 SecImpExpDbg("impExpPkcs8Export SecKeyGetCSSMKey error");
935 switch(cssmKey
->KeyHeader
.AlgorithmId
) {
937 case CSSM_ALGID_ECDSA
:
938 formatAttrType
= CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT
;
939 blobForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
;
947 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
949 crtn
= impExpExportKeyCommon(cspdlHand
, secKey
, &wrappingKey
, &wrappedKey
,
950 CSSM_ALGID_3DES_3KEY_EDE
, CSSM_ALGMODE_CBCPadIV8
, CSSM_PADDING_PKCS7
,
951 CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
, formatAttrType
, blobForm
, NULL
, &rawIv
);
957 * OK... *that* wrapped key's data goes into the top-level
958 * NSS_EncryptedPrivateKeyInfo, which we then encode; the caller
959 * gets the result of that encoding.
961 encrPrivKeyInfo
.encryptedData
= wrappedKey
.KeyData
;
962 coder
.encodeItem(&encrPrivKeyInfo
, kSecAsn1EncryptedPrivateKeyInfoTemplate
,
965 CFDataAppendBytes(outData
, encodedKeyInfo
.Data
, encodedKeyInfo
.Length
);
966 CSSM_FreeKey(cspdlHand
, NULL
, &wrappedKey
, CSSM_FALSE
);
968 *pemHeader
= PEM_STRING_PKCS8
;
971 if(wrappingKey
.KeyData
.Data
) {
972 CSSM_FreeKey(cspdlHand
, NULL
, &wrappingKey
, CSSM_FALSE
);
975 cuCspDetachUnload(cspdlHand
, CSSM_FALSE
);