2 * Copyright (c) 2003-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 * pkcs12Encode.h - P12Coder encoding engine.
27 * Unlike the decoding side of P12Coder, which can parse PFXs with
28 * more or less arbitrary structures, this encoding engine has
29 * a specific layout for what bags go where in the jungle of
30 * SafeContents and ContentInfos. It would be impractical to allow
31 * (or to expect) the app to specify this structure.
33 * The knowledge of how a PFX is built out of various components
34 * is encapsulated in the authSafeBuild() member function. The rest
35 * of the functions in this file are pretty much "PFX-structure-
36 * agnostic", so if one wanted to change the overall PFX structure,
37 * one would only have to focus on the authSafeBuild() function.
40 #include "pkcs12Coder.h"
41 #include "pkcs12Debug.h"
42 #include "pkcs12Crypto.h"
43 #include "pkcs12Templates.h"
44 #include "pkcs12Utils.h"
45 #include <Security/cssmerr.h>
46 #include <Security/oidsattr.h>
47 #include <Security/SecBase.h>
49 void P12Coder::encode(
50 CFDataRef
*cpfx
) // RETURNED
52 p12EncodeLog("encode top");
54 NSS_P12_DecodedPFX pfx
;
56 memset(&pfx
, 0, sizeof(pfx
));
57 p12IntToData(3, pfx
.version
, localCdr
);
58 authSafeBuild(pfx
.authSafe
, localCdr
);
59 macSignPfx(pfx
, localCdr
);
60 CSSM_DATA derPfx
= {0, NULL
};
61 if(localCdr
.encodeItem(&pfx
, NSS_P12_DecodedPFXTemplate
, derPfx
)) {
62 p12ErrorLog("Error encoding top-level pfx\n");
65 CFDataRef cp
= CFDataCreate(NULL
, derPfx
.Data
, derPfx
.Length
);
69 void P12Coder::macSignPfx(
70 NSS_P12_DecodedPFX
&pfx
,
71 SecNssCoder
&localCdr
)
73 p12EncodeLog("macSignPfx");
74 NSS_P12_MacData
*macData
= localCdr
.mallocn
<NSS_P12_MacData
>();
75 pfx
.macData
= macData
;
76 p12GenSalt(macData
->macSalt
, localCdr
);
77 p12IntToData(mMacIterCount
, macData
->iterations
, localCdr
);
78 NSS_P7_DigestInfo
&digInfo
= macData
->mac
;
80 /* this is not negotiable; it's the only one P12 allows */
81 localCdr
.allocCopyItem(CSSMOID_SHA1
, digInfo
.digestAlgorithm
.algorithm
);
82 /* null algorithm parameters */
83 p12NullAlgParams(digInfo
.digestAlgorithm
);
85 const CSSM_DATA
*macPhrase
= getMacPassPhrase();
86 const CSSM_KEY
*macPassKey
= getMacPassKey();
87 if((macPhrase
== NULL
) && (macPassKey
== NULL
)) {
88 p12ErrorLog("no passphrase set\n");
89 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE
);
92 CSSM_RETURN crtn
= p12GenMac(mCspHand
,
93 *pfx
.authSafe
.content
.data
,
94 CSSM_ALGID_SHA1
, mMacIterCount
, macData
->macSalt
,
95 macPhrase
, macPassKey
, localCdr
, digInfo
.digest
);
97 p12ErrorLog("Error generating PFX MAC\n");
98 CssmError::throwMe(crtn
);
103 * This is the heart of the encoding engine. All knowledge of
104 * "what bags go where" is here. The PFX structure implemented here
105 * is derived from empirical observation of PFXs obtained from
106 * Mozilla 1.2b and from the DoD test vectors for "Conformance
107 * Testing of Relying Party Client Certificate Path Processing
108 * Logic", written by Cygnacom, Septemtber 28, 2001.
110 * The PFX structure is as follows:
112 * -- One AuthenticatedSafe element (a PKCS7 ContentInfo) containing
113 * all certificates and CRLs.
115 * ContentInfo.type = CT_EncryptedData
116 * Encryption algorithm is our "weak" encryption Alg, default
117 * of CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4.
119 * -- One AuthenticatedSafe element containing all private keys in
120 * the form of ShroudedKeyBags.
122 * ContentInfo.type = CT_Data
123 * Encryption algorithm for shrouded key bags is our "strong"
124 * encryption, default CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC
126 * -- Everything else goes in another AuthenticatedSafe element.
128 * ContentInfo.type = CT_EncryptedData
129 * Encryption algorithm is our "strong" encryption Alg
131 void P12Coder::authSafeBuild(
132 NSS_P7_DecodedContentInfo
&authSafe
,
133 SecNssCoder
&localCdr
)
135 p12EncodeLog("authSafeBuild top");
137 /* how many contentInfos are we going to build? */
138 unsigned numContents
= 0;
139 if(mCerts
.size() || mCrls
.size()) {
145 if(mOpaques
.size()) {
149 if(numContents
== 0) {
150 p12ErrorLog("authSafeBuild: no contents\n");
151 MacOSError::throwMe(errSecParam
);
154 NSS_P7_DecodedContentInfo
**contents
=
155 (NSS_P7_DecodedContentInfo
**)p12NssNullArray(numContents
,
157 unsigned contentDex
= 0;
159 NSS_P12_SafeBag
**safeBags
;
162 unsigned numBags
= (unsigned)(mCerts
.size() + mCrls
.size());
163 p12EncodeLog("authSafeBuild : %u certs + CRLS", numBags
);
165 safeBags
= (NSS_P12_SafeBag
**)p12NssNullArray(numBags
, localCdr
);
167 for(unsigned dex
=0; dex
<mCerts
.size(); dex
++) {
168 safeBags
[bagDex
++] = certBagBuild(mCerts
[dex
], localCdr
);
170 for(unsigned dex
=0; dex
<mCrls
.size(); dex
++) {
171 safeBags
[bagDex
++] = crlBagBuild(mCrls
[dex
], localCdr
);
173 contents
[contentDex
++] = safeContentsBuild(safeBags
,
174 CT_EncryptedData
, &mWeakEncrAlg
, mWeakEncrIterCount
, localCdr
);
177 /* shrouded keys - encrypted at bag level */
178 numBags
= (unsigned)mKeys
.size();
180 p12EncodeLog("authSafeBuild : %u keys", numBags
);
181 safeBags
= (NSS_P12_SafeBag
**)p12NssNullArray(numBags
, localCdr
);
183 for(unsigned dex
=0; dex
<numBags
; dex
++) {
184 safeBags
[bagDex
++] = keyBagBuild(mKeys
[dex
], localCdr
);
186 contents
[contentDex
++] = safeContentsBuild(safeBags
,
187 CT_Data
, NULL
, 0, localCdr
);
191 numBags
= (unsigned)mOpaques
.size();
193 p12EncodeLog("authSafeBuild : %u opaques", numBags
);
194 safeBags
= (NSS_P12_SafeBag
**)p12NssNullArray(numBags
, localCdr
);
196 for(unsigned dex
=0; dex
<numBags
; dex
++) {
197 safeBags
[bagDex
++] = opaqueBagBuild(mOpaques
[dex
], localCdr
);
199 contents
[contentDex
++] = safeContentsBuild(safeBags
,
200 CT_EncryptedData
, &mStrongEncrAlg
, mStrongEncrIterCount
, localCdr
);
204 * Encode the whole elements array into authSafe.content.data
206 NSS_P12_AuthenticatedSafe safe
;
207 safe
.info
= contents
;
208 CSSM_DATA
*adata
= localCdr
.mallocn
<CSSM_DATA
>();
209 authSafe
.content
.data
= adata
;
212 if(localCdr
.encodeItem(&safe
, NSS_P12_AuthenticatedSafeTemplate
,
214 p12ErrorLog("authSafeBuild: error encoding auth safe\n");
217 authSafe
.type
= CT_Data
;
218 authSafe
.contentType
= CSSMOID_PKCS7_Data
;
222 * Build a AuthSafe element of specified type out of the
223 * specified array of bags.
225 NSS_P7_DecodedContentInfo
*P12Coder::safeContentsBuild(
226 NSS_P12_SafeBag
**bags
,
227 NSS_P7_CI_Type type
, // CT_Data, CT_EncryptedData
228 CSSM_OID
*encrOid
, // only for CT_EncryptedData
229 unsigned iterCount
, // ditto
230 SecNssCoder
&localCdr
)
232 p12EncodeLog("safeContentsBuild type %u", (unsigned)type
);
235 * First, encode the bag array as a SafeContents
237 CSSM_DATA encSafeContents
= {0, NULL
};
238 NSS_P12_SafeContents safeContents
= {bags
};
239 if(localCdr
.encodeItem(&safeContents
,
240 NSS_P12_SafeContentsTemplate
, encSafeContents
)) {
241 p12ErrorLog("error encoding SafeContents\n");
245 NSS_P7_DecodedContentInfo
*dci
=
246 localCdr
.mallocn
<NSS_P7_DecodedContentInfo
>();
248 if(type
== CT_Data
) {
249 /* plaintext gets encoded as an octet string */
250 localCdr
.allocCopyItem(CSSMOID_PKCS7_Data
, dci
->contentType
);
251 dci
->content
.data
= localCdr
.mallocn
<CSSM_DATA
>();
252 localCdr
.allocCopyItem(encSafeContents
, *dci
->content
.data
);
254 else if(type
== CT_EncryptedData
) {
255 /* encrypt the encoded SafeContents */
256 localCdr
.allocCopyItem(CSSMOID_PKCS7_EncryptedData
,
258 dci
->content
.encryptData
= localCdr
.mallocn
<NSS_P7_EncryptedData
>();
259 NSS_P7_EncryptedData
*ed
= dci
->content
.encryptData
;
260 assert(encrOid
!= NULL
);
261 encryptData(encSafeContents
, *encrOid
, iterCount
, *ed
, localCdr
);
264 p12ErrorLog("bad type in safeContentsBuild\n");
265 CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR
);
271 * Encrypt the specified plaintext with specified algorithm.
272 * Drop result and other interesting info into an NSS_P7_EncryptedData.
274 void P12Coder::encryptData(
275 const CSSM_DATA
&ptext
,
278 NSS_P7_EncryptedData
&ed
,
279 SecNssCoder
&localCdr
)
281 p12EncodeLog("encryptData");
283 /* do the raw encrypt first to make sure we can do it... */
284 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
285 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
286 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
287 uint32 keySizeInBits
;
288 uint32 blockSizeInBytes
; // for IV, optional
289 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
290 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
293 bool found
= pkcsOidToParams(&encrOid
,
294 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
295 padding
, mode
, pkcs
);
296 if(!found
|| (pkcs
!= PW_PKCS12
)) {
297 p12ErrorLog("encryptData encrAlg not understood\n");
298 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
301 /* Salt: we generate random bytes */
303 p12GenSalt(salt
, localCdr
);
305 const CSSM_DATA
*pwd
= getEncrPassPhrase();
306 const CSSM_KEY
*passKey
= getEncrPassKey();
307 if((pwd
== NULL
) && (passKey
== NULL
)) {
308 p12ErrorLog("no passphrase set\n");
309 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE
);
311 CSSM_DATA ctext
= {0, NULL
};
313 CSSM_RETURN crtn
= p12Encrypt(mCspHand
, ptext
,
314 keyAlg
, encrAlg
, pbeHashAlg
,
315 keySizeInBits
, blockSizeInBytes
,
318 pwd
, passKey
, localCdr
,
321 CssmError::throwMe(crtn
);
324 /* Now fill in the NSS_P7_EncryptedData */
325 p12IntToData(0, ed
.version
, localCdr
);
326 NSS_P7_EncrContentInfo
&eci
= ed
.contentInfo
;
327 localCdr
.allocCopyItem(CSSMOID_PKCS7_Data
, eci
.contentType
);
328 algIdBuild(eci
.encrAlg
, encrOid
, salt
, iterCount
, localCdr
);
329 eci
.encrContent
= ctext
;
333 * Fill in an CSSM_X509_ALGORITHM_IDENTIFIER with parameters in
334 * the form of an encoded NSS_P12_PBE_Params
336 void P12Coder::algIdBuild(
337 CSSM_X509_ALGORITHM_IDENTIFIER
&algId
,
338 const CSSM_OID
&algOid
,
339 const CSSM_DATA
&salt
,
341 SecNssCoder
&localCdr
)
343 p12EncodeLog("algIdBuild");
344 localCdr
.allocCopyItem(algOid
, algId
.algorithm
);
345 NSS_P12_PBE_Params pbeParams
;
346 pbeParams
.salt
= salt
;
347 p12IntToData(iterCount
, pbeParams
.iterations
, localCdr
);
348 if(localCdr
.encodeItem(&pbeParams
, NSS_P12_PBE_ParamsTemplate
,
350 p12ErrorLog("error encoding NSS_P12_PBE_Params\n");
355 #pragma mark --- Individual Bag Builders ---
357 NSS_P12_SafeBag
*P12Coder::certBagBuild(
359 SecNssCoder
&localCdr
)
361 p12EncodeLog("certBagBuild");
363 NSS_P12_SafeBag
*safeBag
= localCdr
.mallocn
<NSS_P12_SafeBag
>();
364 safeBag
->bagId
= CSSMOID_PKCS12_certBag
;
365 safeBag
->type
= BT_CertBag
;
367 NSS_P12_CertBag
*certBag
= localCdr
.mallocn
<NSS_P12_CertBag
>();
368 safeBag
->bagValue
.certBag
= certBag
;
369 const CSSM_OID
*certTypeOid
= NULL
;
370 switch(cert
->certType()) {
372 certTypeOid
= &CSSMOID_PKCS9_X509Certificate
;
375 certTypeOid
= &CSSMOID_PKCS9_SdsiCertificate
;
378 p12ErrorLog("unknown certType on encode\n");
379 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
383 /* copies not needed, same scope as P12CertBag */
384 certBag
->bagType
= *certTypeOid
;
385 certBag
->type
= cert
->certType();
386 certBag
->certValue
= cert
->certData();
387 safeBag
->bagAttrs
= cert
->getAllAttrs();
391 NSS_P12_SafeBag
*P12Coder::crlBagBuild(
393 SecNssCoder
&localCdr
)
395 p12EncodeLog("crlBagBuild");
397 NSS_P12_SafeBag
*safeBag
= localCdr
.mallocn
<NSS_P12_SafeBag
>();
398 safeBag
->bagId
= CSSMOID_PKCS12_crlBag
;
399 safeBag
->type
= BT_CrlBag
;
401 NSS_P12_CrlBag
*crlBag
= localCdr
.mallocn
<NSS_P12_CrlBag
>();
402 safeBag
->bagValue
.crlBag
= crlBag
;
403 const CSSM_OID
*crlTypeOid
= NULL
;
404 switch(crl
->crlType()) {
406 crlTypeOid
= &CSSMOID_PKCS9_X509Crl
;
409 p12ErrorLog("unknown crlType on encode\n");
410 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
414 /* copies not needed, same scope as P12CrlBag */
415 crlBag
->bagType
= *crlTypeOid
;
416 crlBag
->type
= crl
->crlType();
417 crlBag
->crlValue
= crl
->crlData();
418 safeBag
->bagAttrs
= crl
->getAllAttrs();
422 NSS_P12_SafeBag
*P12Coder::keyBagBuild(
424 SecNssCoder
&localCdr
)
426 p12EncodeLog("keyBagBuild");
428 NSS_P12_SafeBag
*safeBag
= localCdr
.mallocn
<NSS_P12_SafeBag
>();
429 safeBag
->bagId
= CSSMOID_PKCS12_shroudedKeyBag
;
430 safeBag
->type
= BT_ShroudedKeyBag
;
432 NSS_EncryptedPrivateKeyInfo
*keyInfo
= localCdr
.
433 mallocn
<NSS_EncryptedPrivateKeyInfo
>();
434 safeBag
->bagValue
.shroudedKeyBag
= keyInfo
;
435 safeBag
->bagAttrs
= key
->getAllAttrs();
437 /* Prepare for key wrap */
439 p12GenSalt(salt
, localCdr
);
440 algIdBuild(keyInfo
->algorithm
, mStrongEncrAlg
, salt
,
441 mStrongEncrIterCount
, localCdr
);
443 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
444 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
445 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
446 uint32 keySizeInBits
;
447 uint32 blockSizeInBytes
; // for IV, optional
448 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
449 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
452 bool found
= pkcsOidToParams(&mStrongEncrAlg
,
453 keyAlg
, encrAlg
, pbeHashAlg
, keySizeInBits
, blockSizeInBytes
,
454 padding
, mode
, pkcs
);
455 if(!found
|| (pkcs
!= PW_PKCS12
)) {
456 /* app config error - they gave us bogus algorithm */
457 p12ErrorLog("keyBagBuild encrAlg not understood\n");
458 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
460 const CSSM_DATA
*encrPhrase
= getEncrPassPhrase();
461 const CSSM_KEY
*passKey
= getEncrPassKey();
462 if((encrPhrase
== NULL
) && (passKey
== NULL
)) {
463 p12ErrorLog("no passphrase set\n");
464 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE
);
466 CSSM_DATA shroudedBits
= {0, NULL
};
468 CSSM_RETURN crtn
= p12WrapKey(mCspHand
,
469 key
->key(), key
->privKeyCreds(),
470 keyAlg
, encrAlg
, pbeHashAlg
,
471 keySizeInBits
, blockSizeInBytes
,
473 mStrongEncrIterCount
, salt
,
479 p12ErrorLog("Error wrapping private key\n");
480 CssmError::throwMe(crtn
);
483 keyInfo
->encryptedData
= shroudedBits
;
487 NSS_P12_SafeBag
*P12Coder::opaqueBagBuild(
488 P12OpaqueBag
*opaque
,
489 SecNssCoder
&localCdr
)
491 p12EncodeLog("opaqueBagBuild");
492 NSS_P12_SafeBag
*safeBag
= localCdr
.mallocn
<NSS_P12_SafeBag
>();
493 safeBag
->bagId
= opaque
->oid();
494 safeBag
->bagValue
.secretBag
= &opaque
->blob();
495 safeBag
->bagAttrs
= opaque
->getAllAttrs();