2 * Copyright (c) 2000-2004,2011-2014 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 * 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/oidsalg.h>
53 #include <Security/SecKeyPriv.h>
54 #include <security_cdsa_utils/cuCdsaUtils.h>
55 #include <openssl/pem.h>
57 #include <Security/SecBase.h>
59 #define SecPkcs8Dbg(args...) secinfo("SecPkcs8", ## args)
61 #pragma mark --- PKCS5 v1.5 Key Derivation ---
64 * PKCS5 v1.5. Caller has gleaned everything except salt,
65 * iterationCount, and IV from the AlgId.algorithm OID.
67 * We get salt and iteration count from the incoming alg params.
68 * IV is derived along with the unwrapping key from the passphrase.
70 static CSSM_RETURN
pkcs5_v15_genKey(
71 CSSM_CSP_HANDLE cspHand
,
73 const SecKeyImportExportParameters
*keyParams
,
74 const CSSM_DATA
¶mData
,
75 CSSM_ALGORITHMS keyAlg
,
76 CSSM_ALGORITHMS pbeHashAlg
,
78 uint32 blockSizeInBytes
,
79 impExpKeyUnwrapParams
*unwrapParams
)
81 CSSM_KEY
*passKey
= NULL
;
82 CFDataRef cfPhrase
= NULL
;
85 CSSM_CRYPTO_DATA seed
;
86 CSSM_CC_HANDLE ccHand
= 0;
87 CSSM_ACCESS_CREDENTIALS creds
;
90 /* passphrase or passkey? */
91 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_Data
, VP_Import
,
92 (CFTypeRef
*)&cfPhrase
, &passKey
);
96 /* subsequent errors to errOut: */
98 memset(&seed
, 0, sizeof(seed
));
99 if(cfPhrase
!= NULL
) {
100 size_t len
= CFDataGetLength(cfPhrase
);
101 coder
.allocItem(seed
.Param
, len
);
102 memmove(seed
.Param
.Data
, CFDataGetBytePtr(cfPhrase
), len
);
106 /* hash algorithm --> PBE alg for CSP */
107 CSSM_ALGORITHMS pbeAlg
;
110 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_MD2
;
113 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_MD5
;
115 case CSSM_ALGID_SHA1
:
116 pbeAlg
= CSSM_ALGID_PKCS5_PBKDF1_SHA1
;
119 /* really shouldn't happen - pbeHashAlg was inferred by
120 * pkcsOidToParams() */
121 SecPkcs8Dbg("PKCS8: PKCS5 v1/5 bogus hash alg");
122 crtn
= CSSMERR_CSP_INTERNAL_ERROR
;
126 /* Salt and iteration count from alg parameters */
127 impExpPKCS5_PBE_Parameters pbeParams
;
128 memset(&pbeParams
, 0, sizeof(pbeParams
));
129 if(coder
.decodeItem(paramData
, impExpPKCS5_PBE_ParametersTemplate
, &pbeParams
)) {
130 SecPkcs8Dbg("PKCS8: PKCS5 v1.5 pbeParams decode error");
131 crtn
= errSecUnknownFormat
;
135 if(!p12DataToInt(pbeParams
.iterations
, iterCount
)) {
136 SecPkcs8Dbg("PKCS8: bad PKCS5 v1.5 iteration count");
137 crtn
= errSecUnknownFormat
;
141 /* ask for hard coded 8 bytes of IV */
142 coder
.allocItem(unwrapParams
->iv
, 8);
144 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
145 crtn
= CSSM_CSP_CreateDeriveKeyContext(cspHand
,
156 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
160 memset(unwrapParams
->unwrappingKey
, 0, sizeof(CSSM_KEY
));
162 CSSM_DATA dummyLabel
;
163 dummyLabel
.Data
= (uint8
*)"temp unwrap key";
164 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
166 crtn
= CSSM_DeriveKey(ccHand
,
167 &unwrapParams
->iv
, // IV returned in in/out Param
169 /* not extractable even for the short time this key lives */
170 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
,
172 NULL
, // cred and acl
173 unwrapParams
->unwrappingKey
);
175 SecPkcs8Dbg("PKCS8: PKCS5 v1.5 CSSM_DeriveKey failure");
179 CSSM_DeleteContext(ccHand
);
181 if(passKey
!= NULL
) {
182 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
188 #pragma mark --- PKCS5 v2.0 Key Derivation ---
191 * PKCS5 v2.0 has different means of encoding algorithm parameters,
192 * depending on the encryption algorithm.
195 * Obtain encryption parameters for PKCS5 v2.0, DES and DES3 variants.
197 static OSStatus
pkcs5_DES_params(
198 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
200 impExpKeyUnwrapParams
*unwrapParams
,
201 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
202 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
205 /* Params is iv as OCTET STRING */
206 if(coder
.decodeItem(paramData
, kSecAsn1OctetStringTemplate
, &unwrapParams
->iv
)) {
207 SecPkcs8Dbg("PKCS8: PKCS5 v2 DES init vector decode error");
208 return errSecUnknownFormat
;
210 if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_DES_EDE3_CBC
)) {
211 *keyAlg
= CSSM_ALGID_3DES_3KEY
;
212 unwrapParams
->encrAlg
= CSSM_ALGID_3DES_3KEY_EDE
;
213 if(*keySizeInBits
== 0) {
214 *keySizeInBits
= 3 * 64;
218 *keyAlg
= CSSM_ALGID_DES
;
219 unwrapParams
->encrAlg
= CSSM_ALGID_DES
;
220 if(*keySizeInBits
== 0) {
224 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
225 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
226 return errSecSuccess
;
230 * Obtain encryption parameters for PKCS5 v2.0, RC2 variant.
232 static OSStatus
pkcs5_RC2_params(
233 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
234 impExpKeyUnwrapParams
*unwrapParams
,
235 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
236 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
239 /* Params is impExpPKCS5_RC2Params */
240 impExpPKCS5_RC2Params rc2Params
;
241 memset(&rc2Params
, 0, sizeof(rc2Params
));
242 if(coder
.decodeItem(paramData
, impExpPKCS5_RC2ParamsTemplate
, &rc2Params
)) {
243 SecPkcs8Dbg("PKCS8: PKCS5 v2 RC2 params decode error");
244 return errSecUnknownFormat
;
247 *keyAlg
= CSSM_ALGID_RC2
;
248 unwrapParams
->encrAlg
= CSSM_ALGID_RC2
;
249 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
250 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
252 /* the version actually maps to effective key size like this */
253 /* I swear all of this is in the PKCS5 v2.0 spec */
254 unwrapParams
->effectiveKeySizeInBits
= 32; // default
255 if(rc2Params
.version
.Data
) {
257 if(!p12DataToInt(rc2Params
.version
, v
)) {
258 SecPkcs8Dbg("PKCS8: bad PKCS5 rc2Params.version");
259 return errSecUnknownFormat
;
263 unwrapParams
->effectiveKeySizeInBits
= 40;
266 unwrapParams
->effectiveKeySizeInBits
= 64;
269 unwrapParams
->effectiveKeySizeInBits
= 128;
273 unwrapParams
->effectiveKeySizeInBits
= v
;
276 /* not in the spec, use as zero */
281 unwrapParams
->iv
= rc2Params
.iv
;
283 /* the PKCS5 spec does not give a default for the RC2 key size */
284 if(*keySizeInBits
== 0) {
285 SecPkcs8Dbg("PKCS8: NO RC2 DEFAULT KEYSIZE!");
286 return errSecUnknownFormat
;
288 return errSecSuccess
;
292 * Infer encryption parameters for PKCS5 v2.0, RC5 variant.
293 * All info contained in encryptionScheme.parameters.
295 static OSStatus
pkcs5_RC5_params(
296 const CSSM_DATA
¶mData
, // encryptionScheme.parameters
297 impExpKeyUnwrapParams
*unwrapParams
,
298 CSSM_ALGORITHMS
*keyAlg
, // RETURNED
299 uint32
*keySizeInBits
, // IN/OUT (returned if 0 on entry)
302 /* Params is a impExpPKCS5_RC5Params */
303 impExpPKCS5_RC5Params rc5Params
;
304 memset(&rc5Params
, 0, sizeof(rc5Params
));
305 if(coder
.decodeItem(paramData
, impExpPKCS5_RC5ParamsTemplate
, &rc5Params
)) {
306 SecPkcs8Dbg("PKCS8: PKCS5 v2 RC5 params decode error");
307 return errSecUnknownFormat
;
310 *keyAlg
= CSSM_ALGID_RC5
;
311 unwrapParams
->encrAlg
= CSSM_ALGID_RC5
;
312 unwrapParams
->encrPad
= CSSM_PADDING_PKCS7
;
313 unwrapParams
->encrMode
= CSSM_ALGMODE_CBCPadIV8
;
315 if(rc5Params
.rounds
.Data
) {
316 if(!p12DataToInt(rc5Params
.rounds
, unwrapParams
->rounds
)) {
317 SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.rounds");
318 return errSecUnknownFormat
;
321 if(rc5Params
.blockSizeInBits
.Data
) {
322 if(!p12DataToInt(rc5Params
.blockSizeInBits
, unwrapParams
->blockSizeInBits
)) {
323 SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.blockSizeInBits");
324 return errSecUnknownFormat
;
328 /* Spec says default iv is zeroes */
329 unwrapParams
->iv
= rc5Params
.iv
;
330 if(unwrapParams
->iv
.Length
== 0) {
331 uint32 len
= unwrapParams
->blockSizeInBits
/ 8;
332 coder
.allocItem(unwrapParams
->iv
, len
);
333 memset(unwrapParams
->iv
.Data
, 0, len
);
337 * Spec does not give a default for key RC5 size, and openssl doesn't
338 * support RC5 for PKCS8.
340 if(*keySizeInBits
== 0) {
341 SecPkcs8Dbg("PKCS8: NO RC5 DEFAULT KEYSIZE!");
342 return errSecUnknownFormat
;
344 return errSecSuccess
;
348 * Common code to derive a wrap/unwrap key using PBKDF2 (i.e., using PKCS5 v2.0
349 * key derivation). Caller must CSSM_FreeKey when done.
351 static CSSM_RETURN
pbkdf2DeriveKey(
352 CSSM_CSP_HANDLE cspHand
,
354 CSSM_ALGORITHMS keyAlg
,
355 uint32 keySizeInBits
,
356 uint32 iterationCount
,
357 const CSSM_DATA
&salt
,
358 const SecKeyImportExportParameters
*keyParams
, // required
359 impExpVerifyPhrase verifyPhrase
, // for secure passphrase
360 CSSM_KEY_PTR symKey
) // RETURNED
362 CSSM_KEY
*passKey
= NULL
;
363 CFDataRef cfPhrase
= NULL
;
364 CSSM_PKCS5_PBKDF2_PARAMS pbeParams
;
367 CSSM_DATA dummyLabel
;
370 CSSM_CC_HANDLE ccHand
= 0;
371 CSSM_ACCESS_CREDENTIALS creds
;
373 memset(&pbeParams
, 0, sizeof(pbeParams
));
375 /* passphrase or passkey? */
376 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_Data
, verifyPhrase
,
377 (CFTypeRef
*)&cfPhrase
, &passKey
);
381 /* subsequent errors to errOut: */
383 if(cfPhrase
!= NULL
) {
384 size_t len
= CFDataGetLength(cfPhrase
);
385 coder
.allocItem(pbeParams
.Passphrase
, len
);
386 memmove(pbeParams
.Passphrase
.Data
,
387 CFDataGetBytePtr(cfPhrase
), len
);
391 memset(&creds
, 0, sizeof(CSSM_ACCESS_CREDENTIALS
));
392 crtn
= CSSM_CSP_CreateDeriveKeyContext(cspHand
,
393 CSSM_ALGID_PKCS5_PBKDF2
,
403 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure");
407 memset(symKey
, 0, sizeof(CSSM_KEY
));
409 /* not extractable even for the short time this key lives */
410 keyAttr
= CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
;
411 dummyLabel
.Data
= (uint8
*)"temp unwrap key";
412 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
414 pbeParams
.PseudoRandomFunction
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
415 pbeData
.Data
= (uint8
*)&pbeParams
;
416 pbeData
.Length
= sizeof(pbeParams
);
417 crtn
= CSSM_DeriveKey(ccHand
,
422 NULL
, // cred and acl
425 SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_DeriveKey failure");
429 CSSM_DeleteContext(ccHand
);
431 if(passKey
!= NULL
) {
432 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
439 * Obtain PKCS5, v.2.0 key derivation and encryption parameters and
440 * derive the key. This one obtains all of the crypt parameters
441 * from the top-level AlgId.Params. What a mess.
443 static CSSM_RETURN
pkcs5_v2_genKey(
444 CSSM_CSP_HANDLE cspHand
,
446 const CSSM_DATA
¶mData
,
447 const SecKeyImportExportParameters
*keyParams
,
448 impExpKeyUnwrapParams
*unwrapParams
)
450 SecPkcs8Dbg("PKCS8: generating PKCS5 v2.0 key");
452 CSSM_ALGORITHMS keyAlg
= CSSM_ALGID_NONE
;
453 uint32 prf
= 0; // CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1...
455 /* caller should check */
456 assert(keyParams
!= NULL
);
458 /* AlgId.Params --> impExpPKCS5_PBES2_Params */
459 if(paramData
.Length
== 0) {
460 SecPkcs8Dbg("PKCS8: empty PKCS5 v2 pbes2Params");
461 return errSecUnknownFormat
;
463 impExpPKCS5_PBES2_Params pbes2Params
;
464 memset(&pbes2Params
, 0, sizeof(pbes2Params
));
465 if(coder
.decodeItem(paramData
, impExpPKCS5_PBES2_ParamsTemplate
, &pbes2Params
)) {
466 SecPkcs8Dbg("PKCS8: PKCS5 v2 pbes2Params decode error");
467 return errSecUnknownFormat
;
471 * As far as I know the keyDerivationFunc OID must be id-PBKDF2
473 if(!nssCompareCssmData(&pbes2Params
.keyDerivationFunc
.algorithm
,
474 &CSSMOID_PKCS5_PBKDF2
)) {
475 SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected keyDerivationFunc alg");
476 return errSecUnknownFormat
;
480 * The params of the keyDerivationFunc algId are an encoded
481 * impExpPKCS5_PBKDF2_Params.
483 impExpPKCS5_PBKDF2_Params pbkdf2Params
;
484 memset(&pbkdf2Params
, 0, sizeof(pbkdf2Params
));
485 if(coder
.decodeItem(pbes2Params
.keyDerivationFunc
.parameters
,
486 impExpPKCS5_PBKDF2_ParamsTemplate
, &pbkdf2Params
)) {
487 SecPkcs8Dbg("PKCS8: PKCS5 v2 pbkdf2Params decode error");
488 return errSecUnknownFormat
;
492 * Salt and iteration count from the impExpPKCS5_PBKDF2_Params (ignoring the
493 * possible CHOICE for salt source).
495 CSSM_DATA salt
= pbkdf2Params
.salt
;
497 if(!p12DataToInt(pbkdf2Params
.iterationCount
, iterCount
)) {
498 SecPkcs8Dbg("PKCS8: bad PKCS5 v2 iteration count");
499 return errSecUnknownFormat
;
503 * Key size optional, use defaults per alg (later) if it's not there
505 uint32 keySizeInBits
= 0;
506 if(pbkdf2Params
.keyLengthInBytes
.Data
) {
507 uint32 keyLengthInBytes
;
508 if(!p12DataToInt(pbkdf2Params
.keyLengthInBytes
, keyLengthInBytes
)) {
509 SecPkcs8Dbg("PKCS8: bad PKCS5 v2 key size");
510 return errSecUnknownFormat
;
512 keySizeInBits
= keyLengthInBytes
* 8;
514 /* else we'll infer key size from the encryption algorithm */
516 /* prf optional, but if it's there it better be CSSMOID_PKCS5_HMAC_SHA1 */
517 if(pbkdf2Params
.prf
.Data
) {
518 if(!nssCompareCssmData(&pbkdf2Params
.prf
, &CSSMOID_PKCS5_HMAC_SHA1
)) {
519 SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected prf OID");
520 return errSecUnknownFormat
;
523 prf
= CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
;
526 * Now process the encryptionScheme, which is even messier - the algParams
527 * varies per encryption algorithm.
529 CSSM_X509_ALGORITHM_IDENTIFIER
&encrScheme
= pbes2Params
.encryptionScheme
;
530 CSSM_OID
*encrOid
= &encrScheme
.algorithm
;
532 CSSM_DATA
&encrParam
= encrScheme
.parameters
;
534 if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_DES_EDE3_CBC
) ||
535 nssCompareCssmData(encrOid
, &CSSMOID_DES_CBC
)) {
536 ortn
= pkcs5_DES_params(encrParam
, encrOid
, unwrapParams
, &keyAlg
,
537 &keySizeInBits
, coder
);
542 else if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_RC2_CBC
)) {
543 ortn
= pkcs5_RC2_params(encrParam
, unwrapParams
, &keyAlg
,
544 &keySizeInBits
, coder
);
549 else if(nssCompareCssmData(encrOid
, &CSSMOID_PKCS5_RC5_CBC
)) {
550 ortn
= pkcs5_RC5_params(encrParam
, unwrapParams
, &keyAlg
,
551 &keySizeInBits
, coder
);
557 SecPkcs8Dbg("PKCS8: PKCS5 v2 unknown encrScheme.algorithm");
558 return errSecUnknownFormat
;
561 /* We should be ready to go */
562 assert(keyAlg
!= CSSM_ALGID_NONE
);
563 assert(unwrapParams
->encrAlg
!= CSSM_ALGID_NONE
);
565 /* use all the stuff we just figured out to derive a symmetric decryption key */
566 return pbkdf2DeriveKey(cspHand
, coder
,
567 keyAlg
, keySizeInBits
,
571 unwrapParams
->unwrappingKey
);
574 #pragma mark --- PKCS12 Key Derivation ---
577 * PKCS12 style key derivation. Caller has gleaned everything except
578 * salt, iterationCount, and IV from the AlgId.algorithm OID.
580 * We get salt and iteration count from the incoming alg params.
581 * IV is derived along with the unwrapping key from the passphrase.
583 static CSSM_RETURN
pkcs12_genKey(
584 CSSM_CSP_HANDLE cspHand
,
586 const SecKeyImportExportParameters
*keyParams
,
587 const CSSM_DATA
¶mData
, // from algID
588 CSSM_ALGORITHMS keyAlg
, // valid on entry
589 CSSM_ALGORITHMS pbeHashAlg
, // valid on entry
590 uint32 keySizeInBits
, // valid on entry
591 uint32 blockSizeInBytes
, // for IV
592 impExpKeyUnwrapParams
*unwrapParams
)
594 SecPkcs8Dbg("PKCS8: generating PKCS12 key");
596 assert(keyAlg
!= CSSM_ALGID_NONE
);
597 assert(pbeHashAlg
!= CSSM_ALGID_NONE
);
598 assert(keySizeInBits
!= 0);
600 /* get iteration count, salt from alg params */
601 NSS_P12_PBE_Params pbeParams
;
603 if(paramData
.Length
== 0) {
604 SecPkcs8Dbg("PKCS8: empty P12 pbeParams");
605 return errSecUnknownFormat
;
607 memset(&pbeParams
, 0, sizeof(pbeParams
));
608 if(coder
.decodeItem(paramData
, NSS_P12_PBE_ParamsTemplate
, &pbeParams
)) {
609 SecPkcs8Dbg("PKCS8: P12 pbeParams decode error");
610 return errSecUnknownFormat
;
613 uint32 iterCount
= 0;
614 if(!p12DataToInt(pbeParams
.iterations
, iterCount
)) {
615 SecPkcs8Dbg("PKCS8: bad P12 iteration count");
616 return errSecUnknownFormat
;
619 /* passphrase or passkey? */
620 CSSM_KEY
*passKey
= NULL
;
621 CFStringRef phraseStr
= NULL
;
622 CSSM_DATA phraseData
= {0, NULL
};
623 CSSM_DATA
*phraseDataP
= NULL
;
627 assert(keyParams
!= NULL
);
629 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_String
, VP_Import
,
630 (CFTypeRef
*)&phraseStr
, &passKey
);
634 /* subsequent errors to errOut: */
636 if(phraseStr
!= NULL
) {
637 /* convert to CSSM_DATA for use with p12KeyGen() */
639 p12ImportPassPhrase(phraseStr
, coder
, phraseData
);
642 SecPkcs8Dbg("PKCS8: p12ImportPassPhrase threw");
643 crtn
= errSecAllocate
;
646 CFRelease(phraseStr
);
647 phraseDataP
= &phraseData
;
650 /* use p12 module to cook up the key and IV */
651 if(blockSizeInBytes
) {
652 coder
.allocItem(unwrapParams
->iv
, blockSizeInBytes
);
654 crtn
= p12KeyGen(cspHand
,
655 *unwrapParams
->unwrappingKey
,
666 SecPkcs8Dbg("PKCS8: p12KeyGen failed");
669 if(passKey
!= NULL
) {
670 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
676 #pragma mark --- Public PKCS8 import function ---
679 * Called out from SecImportRep::importWrappedKey().
680 * If cspHand is provided instead of importKeychain, the CSP
681 * handle MUST be for the CSPDL, not for the raw CSP.
683 OSStatus
impExpPkcs8Import(
685 SecKeychainRef importKeychain
, // optional
686 CSSM_CSP_HANDLE cspHand
, // required
687 SecItemImportExportFlags flags
,
688 const SecKeyImportExportParameters
*keyParams
, // REQUIRED for unwrap
689 CFMutableArrayRef outArray
) // optional, append here
692 CSSM_KEYHEADER
&hdr
= wrappedKey
.KeyHeader
;
693 CSSM_RETURN crtn
= CSSM_OK
;
695 /* key derivation and encryption parameters gleaned from alg ID */
696 impExpKeyUnwrapParams unwrapParams
;
697 memset(&unwrapParams
, 0, sizeof(unwrapParams
));
698 CSSM_ALGORITHMS keyAlg
= CSSM_ALGID_NONE
;
699 CSSM_ALGORITHMS pbeHashAlg
= CSSM_ALGID_NONE
; // SHA1 or MD5
700 uint32 keySizeInBits
;
701 uint32 blockSizeInBytes
;
702 PKCS_Which pkcs
= PW_None
;
704 if( (keyParams
== NULL
) ||
705 ( (keyParams
->passphrase
== NULL
) &&
706 !(keyParams
->flags
& kSecKeySecurePassphrase
) ) ) {
707 /* passphrase mandatory */
708 return errSecPassphraseRequired
;
710 assert(cspHand
!= 0);
716 NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo
;
718 memset(&encrPrivKeyInfo
, 0, sizeof(encrPrivKeyInfo
));
719 if(coder
.decode(CFDataGetBytePtr(inData
),
720 CFDataGetLength(inData
),
721 kSecAsn1EncryptedPrivateKeyInfoTemplate
, &encrPrivKeyInfo
)) {
722 SecImpExpDbg("impExpPkcs8Import: error decoding top-level encrPrivKeyInfo");
723 return errSecUnknownFormat
;
727 * The algorithm OID of that top-level struct is the key piece of info
731 found
= pkcsOidToParams(&encrPrivKeyInfo
.algorithm
.algorithm
,
732 keyAlg
, unwrapParams
.encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
733 unwrapParams
.encrPad
, unwrapParams
.encrMode
, pkcs
);
735 SecImpExpDbg("impExpPkcs8Import: unknown OID in top-level encrPrivKeyInfo");
736 return errSecUnknownFormat
;
740 * Each PBE method has its own way of filling in the remaining gaps
741 * in impExpKeyUnwrapParams and generating a key.
743 CSSM_KEY unwrappingKey
;
744 memset(&unwrappingKey
, 0, sizeof(unwrappingKey
));
745 unwrapParams
.unwrappingKey
= &unwrappingKey
;
746 CSSM_DATA
¶mData
= encrPrivKeyInfo
.algorithm
.parameters
;
750 /* we have everything except iv, iterations, salt */
751 crtn
= pkcs5_v15_genKey(cspHand
, coder
, keyParams
, paramData
,
752 keyAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
757 /* obtain everything, including iv, from alg params */
758 crtn
= pkcs5_v2_genKey(cspHand
, coder
, paramData
, keyParams
, &unwrapParams
);
761 /* we have everything except iv, iterations, salt */
762 crtn
= pkcs12_genKey(cspHand
, coder
, keyParams
, paramData
,
763 keyAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
767 /* satisfy compiler */
769 return errSecUnknownFormat
;
772 SecPkcs8Dbg("PKCS8: key derivation failed");
776 /* we should be ready to rock'n'roll no matter how we got here */
777 assert(unwrapParams
.encrAlg
!= CSSM_ALGID_NONE
);
778 assert(unwrappingKey
.KeyData
.Data
!= NULL
);
779 assert(unwrappingKey
.KeyHeader
.AlgorithmId
!= CSSM_ALGID_NONE
);
781 /* set up key to unwrap */
782 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
783 hdr
.HeaderVersion
= CSSM_KEYHEADER_VERSION
;
784 /* CspId : don't care */
785 hdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
786 hdr
.Format
= CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
;
787 /* AlgorithmId : inferred by CSP */
788 hdr
.AlgorithmId
= CSSM_ALGID_NONE
;
789 hdr
.KeyClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
790 /* LogicalKeySizeInBits : calculated by CSP during unwrap */
791 hdr
.KeyAttr
= CSSM_KEYATTR_EXTRACTABLE
;
792 hdr
.KeyUsage
= CSSM_KEYUSE_ANY
;
794 wrappedKey
.KeyData
= encrPrivKeyInfo
.encryptedData
;
796 crtn
= impExpImportKeyCommon(
803 NULL
, // default label
805 CSSM_FreeKey(cspHand
, NULL
, &unwrappingKey
, CSSM_FALSE
);
809 #pragma mark --- Public PKCS8 export function ---
811 #define PKCS5_V2_SALT_LEN 8
812 #define PKCS5_V2_ITERATIONS 2048
813 #define PKCS5_V2_DES_IV_SIZE 8
816 * Unlike impExpPkcs8Import(), which can handle every PBE algorithm in the spec
817 * and implemented by openssl, this one has a fixed PBE and encryption scheme.
818 * We do not provide a means at the API for the client to specify these.
820 * We generate blobs with triple DES encryption, with PKCS5 v2.0 key
823 OSStatus
impExpPkcs8Export(
825 SecItemImportExportFlags flags
,
826 const SecKeyImportExportParameters
*keyParams
, // optional
827 CFMutableDataRef outData
, // output appended here
828 const char **pemHeader
)
831 impExpPKCS5_PBES2_Params pbes2Params
;
832 CSSM_X509_ALGORITHM_IDENTIFIER
&keyDeriveAlgId
= pbes2Params
.keyDerivationFunc
;
833 CSSM_ATTRIBUTE_TYPE formatAttrType
= CSSM_ATTRIBUTE_NONE
;
834 CSSM_KEYBLOB_FORMAT blobForm
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
835 const CSSM_KEY
*cssmKey
;
837 if(keyParams
== NULL
) {
840 assert(secKey
!= NULL
);
841 assert(outData
!= NULL
);
843 memset(&pbes2Params
, 0, sizeof(pbes2Params
));
847 * parameters is an encoded impExpPKCS5_PBKDF2_Params
848 * We generate random salt
850 keyDeriveAlgId
.algorithm
= CSSMOID_PKCS5_PBKDF2
;
851 impExpPKCS5_PBKDF2_Params pbkdf2Params
;
852 memset(&pbkdf2Params
, 0, sizeof(pbkdf2Params
));
853 coder
.allocItem(pbkdf2Params
.salt
, PKCS5_V2_SALT_LEN
);
854 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, PKCS5_V2_SALT_LEN
, pbkdf2Params
.salt
.Data
));
856 p12IntToData(PKCS5_V2_ITERATIONS
, pbkdf2Params
.iterationCount
, coder
);
857 /* leave pbkdf2Params.keyLengthInBytes NULL for default */
858 /* openssl can't handle this, which is the default value:
859 pbkdf2Params.prf = CSSMOID_PKCS5_HMAC_SHA1;
862 coder
.encodeItem(&pbkdf2Params
, impExpPKCS5_PBKDF2_ParamsTemplate
,
863 keyDeriveAlgId
.parameters
);
867 * parameters is an encoded OCTET STRING containing the (random) IV
869 CSSM_X509_ALGORITHM_IDENTIFIER
&encrScheme
= pbes2Params
.encryptionScheme
;
870 encrScheme
.algorithm
= CSSMOID_PKCS5_DES_EDE3_CBC
;
871 CSSM_DATA rawIv
= {0, NULL
};
872 coder
.allocItem(rawIv
, PKCS5_V2_DES_IV_SIZE
);
873 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, PKCS5_V2_DES_IV_SIZE
, rawIv
.Data
));
875 coder
.encodeItem(&rawIv
, kSecAsn1OctetStringTemplate
,
876 encrScheme
.parameters
);
879 * Top level NSS_EncryptedPrivateKeyInfo, whose parameters is the encoded
880 * impExpPKCS5_PBES2_Params.
882 NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo
;
883 memset(&encrPrivKeyInfo
, 0, sizeof(encrPrivKeyInfo
));
884 CSSM_X509_ALGORITHM_IDENTIFIER
&topAlgId
= encrPrivKeyInfo
.algorithm
;
885 topAlgId
.algorithm
= CSSMOID_PKCS5_PBES2
;
886 coder
.encodeItem(&pbes2Params
, impExpPKCS5_PBES2_ParamsTemplate
,
887 topAlgId
.parameters
);
890 * Now all we have to do is generate the encrypted key data itself.
891 * When doing a WrapKey op in PKCS8 form, the CSP gives us the
892 * NSS_EncryptedPrivateKeyInfo.encryptedData values.
895 /* we need a CSPDL handle - try to get it from the key */
896 CSSM_CSP_HANDLE cspdlHand
= 0;
898 bool releaseCspHand
= false;
899 CSSM_DATA encodedKeyInfo
= {0, NULL
};
901 ortn
= SecKeyGetCSPHandle(secKey
, &cspdlHand
);
903 cspdlHand
= cuCspStartup(CSSM_FALSE
);
905 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
907 releaseCspHand
= true;
909 /* subsequent errors to errOut: */
911 /* get wrapping key from parameters we just set up */
912 CSSM_KEY wrappingKey
;
913 memset(&wrappingKey
, 0, sizeof(CSSM_KEY
));
914 CSSM_RETURN crtn
= pbkdf2DeriveKey(cspdlHand
, coder
,
915 CSSM_ALGID_3DES_3KEY
, 3 * 64,
916 PKCS5_V2_ITERATIONS
, pbkdf2Params
.salt
,
925 * Special case for DSA, ECDSA: specify that the raw blob, pre-encrypt, is in
926 * the PKCS8 PrivateKeyInfo format that openssl understands. The
929 crtn
= SecKeyGetCSSMKey(secKey
, &cssmKey
);
931 SecImpExpDbg("impExpPkcs8Export SecKeyGetCSSMKey error");
934 switch(cssmKey
->KeyHeader
.AlgorithmId
) {
936 case CSSM_ALGID_ECDSA
:
937 formatAttrType
= CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT
;
938 blobForm
= CSSM_KEYBLOB_RAW_FORMAT_PKCS8
;
946 memset(&wrappedKey
, 0, sizeof(CSSM_KEY
));
948 crtn
= impExpExportKeyCommon(cspdlHand
, secKey
, &wrappingKey
, &wrappedKey
,
949 CSSM_ALGID_3DES_3KEY_EDE
, CSSM_ALGMODE_CBCPadIV8
, CSSM_PADDING_PKCS7
,
950 CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
, formatAttrType
, blobForm
, NULL
, &rawIv
);
956 * OK... *that* wrapped key's data goes into the top-level
957 * NSS_EncryptedPrivateKeyInfo, which we then encode; the caller
958 * gets the result of that encoding.
960 encrPrivKeyInfo
.encryptedData
= wrappedKey
.KeyData
;
961 coder
.encodeItem(&encrPrivKeyInfo
, kSecAsn1EncryptedPrivateKeyInfoTemplate
,
964 CFDataAppendBytes(outData
, encodedKeyInfo
.Data
, encodedKeyInfo
.Length
);
965 CSSM_FreeKey(cspdlHand
, NULL
, &wrappedKey
, CSSM_FALSE
);
967 *pemHeader
= PEM_STRING_PKCS8
;
970 if(wrappingKey
.KeyData
.Data
) {
971 CSSM_FreeKey(cspdlHand
, NULL
, &wrappingKey
, CSSM_FALSE
);
974 cuCspDetachUnload(cspdlHand
, CSSM_FALSE
);