1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2000
19 * the Initial Developer. All Rights Reserved.
22 * Ian McGreer <mcgreer@netscape.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
45 #include "base/lazy_instance.h"
46 #include "base/logging.h"
47 #include "base/strings/string_util.h"
48 #include "crypto/nss_util_internal.h"
49 #include "net/base/net_errors.h"
50 #include "net/cert/x509_certificate.h"
52 namespace mozilla_security_manager
{
58 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
59 // a buffer of octets. Must handle byte order correctly.
60 // TODO: Is there a Mozilla way to do this? In the string lib?
61 void unicodeToItem(const PRUnichar
*uni
, SECItem
*item
)
64 while (uni
[len
++] != 0);
65 SECITEM_AllocItem(NULL
, item
, sizeof(PRUnichar
) * len
);
66 #ifdef IS_LITTLE_ENDIAN
68 for (i
=0; i
<len
; i
++) {
69 item
->data
[2*i
] = (unsigned char )(uni
[i
] << 8);
70 item
->data
[2*i
+1] = (unsigned char )(uni
[i
]);
73 memcpy(item
->data
, uni
, item
->len
);
78 // write bytes to the exported PKCS#12 data buffer
79 void write_export_data(void* arg
, const char* buf
, unsigned long len
) {
80 std::string
* dest
= reinterpret_cast<std::string
*>(arg
);
81 dest
->append(buf
, len
);
85 // what to do when the nickname collides with one already in the db.
86 // Based on P12U_NicknameCollisionCallback from nss/cmd/pk12util/pk12util.c
88 nickname_collision(SECItem
*old_nick
, PRBool
*cancel
, void *wincx
)
91 SECItem
*ret_nick
= NULL
;
92 CERTCertificate
* cert
= (CERTCertificate
*)wincx
;
94 if (!cancel
|| !cert
) {
95 // pk12util calls this error user cancelled?
100 VLOG(1) << "no nickname for cert in PKCS12 file.";
102 nick
= CERT_MakeCANickname(cert
);
107 if(old_nick
&& old_nick
->data
&& old_nick
->len
&&
108 PORT_Strlen(nick
) == old_nick
->len
&&
109 !PORT_Strncmp((char *)old_nick
->data
, nick
, old_nick
->len
)) {
111 PORT_SetError(SEC_ERROR_IO
);
115 VLOG(1) << "using nickname " << nick
;
116 ret_nick
= PORT_ZNew(SECItem
);
117 if(ret_nick
== NULL
) {
122 ret_nick
->data
= (unsigned char *)nick
;
123 ret_nick
->len
= PORT_Strlen(nick
);
128 // pip_ucs2_ascii_conversion_fn
129 // required to be set by NSS (to do PKCS#12), but since we've already got
130 // unicode make this a no-op.
132 pip_ucs2_ascii_conversion_fn(PRBool toUnicode
,
133 unsigned char *inBuf
,
134 unsigned int inBufLen
,
135 unsigned char *outBuf
,
136 unsigned int maxOutBufLen
,
137 unsigned int *outBufLen
,
140 CHECK_GE(maxOutBufLen
, inBufLen
);
141 // do a no-op, since I've already got Unicode. Hah!
142 *outBufLen
= inBufLen
;
143 memcpy(outBuf
, inBuf
, inBufLen
);
147 // Based on nsPKCS12Blob::ImportFromFileHelper.
149 nsPKCS12Blob_ImportHelper(const char* pkcs12_data
,
151 const base::string16
& password
,
153 bool try_zero_length_secitem
,
155 net::CertificateList
* imported_certs
)
159 int import_result
= net::ERR_PKCS12_IMPORT_FAILED
;
160 SECStatus srv
= SECSuccess
;
161 SEC_PKCS12DecoderContext
*dcx
= NULL
;
163 SECItem attribute_value
;
164 CK_BBOOL attribute_data
= CK_FALSE
;
165 const SEC_PKCS12DecoderItem
* decoder_item
= NULL
;
167 unicodePw
.type
= siBuffer
;
169 unicodePw
.data
= NULL
;
170 if (!try_zero_length_secitem
) {
171 unicodeToItem(password
.c_str(), &unicodePw
);
174 // Initialize the decoder
175 dcx
= SEC_PKCS12DecoderStart(&unicodePw
, slot
,
178 // dOpen, dClose, dRead, dWrite, dArg: NULL
179 // specifies default impl using memory buffer.
180 NULL
, NULL
, NULL
, NULL
, NULL
);
185 // feed input to the decoder
186 srv
= SEC_PKCS12DecoderUpdate(dcx
,
187 (unsigned char*)pkcs12_data
,
189 if (srv
) goto finish
;
191 srv
= SEC_PKCS12DecoderVerify(dcx
);
192 if (srv
) goto finish
;
194 srv
= SEC_PKCS12DecoderValidateBags(dcx
, nickname_collision
);
195 if (srv
) goto finish
;
196 // import certificate and key
197 srv
= SEC_PKCS12DecoderImportBags(dcx
);
198 if (srv
) goto finish
;
200 attribute_value
.data
= &attribute_data
;
201 attribute_value
.len
= sizeof(attribute_data
);
203 srv
= SEC_PKCS12DecoderIterateInit(dcx
);
204 if (srv
) goto finish
;
207 imported_certs
->clear();
209 // Collect the list of decoded certificates, and mark private keys
210 // non-extractable if needed.
211 while (SEC_PKCS12DecoderIterateNext(dcx
, &decoder_item
) == SECSuccess
) {
212 if (decoder_item
->type
!= SEC_OID_PKCS12_V1_CERT_BAG_ID
)
215 CERTCertificate
* cert
= PK11_FindCertFromDERCertItem(
216 slot
, decoder_item
->der
,
219 LOG(ERROR
) << "Could not grab a handle to the certificate in the slot "
220 << "from the corresponding PKCS#12 DER certificate.";
224 // Add the cert to the list
225 if (imported_certs
) {
226 // Empty list of intermediates.
227 net::X509Certificate::OSCertHandles intermediates
;
228 imported_certs
->push_back(
229 net::X509Certificate::CreateFromHandle(cert
, intermediates
));
232 // Once we have determined that the imported certificate has an
233 // associated private key too, only then can we mark the key as
235 if (!decoder_item
->hasKey
) {
236 CERT_DestroyCertificate(cert
);
240 // Iterate through all the imported PKCS12 items and mark any accompanying
241 // private keys as non-extractable.
242 if (!is_extractable
) {
243 SECKEYPrivateKey
* privKey
= PK11_FindPrivateKeyFromCert(slot
, cert
,
246 // Mark the private key as non-extractable.
247 srv
= PK11_WriteRawAttribute(PK11_TypePrivKey
, privKey
, CKA_EXTRACTABLE
,
249 SECKEY_DestroyPrivateKey(privKey
);
251 LOG(ERROR
) << "Could not set CKA_EXTRACTABLE attribute on private "
253 CERT_DestroyCertificate(cert
);
258 CERT_DestroyCertificate(cert
);
259 if (srv
) goto finish
;
261 import_result
= net::OK
;
263 // If srv != SECSuccess, NSS probably set a specific error code.
264 // We should use that error code instead of inventing a new one
265 // for every error possible.
266 if (srv
!= SECSuccess
) {
267 int error
= PORT_GetError();
268 LOG(ERROR
) << "PKCS#12 import failed with error " << error
;
270 case SEC_ERROR_BAD_PASSWORD
:
271 case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT
:
272 import_result
= net::ERR_PKCS12_IMPORT_BAD_PASSWORD
;
274 case SEC_ERROR_PKCS12_INVALID_MAC
:
275 import_result
= net::ERR_PKCS12_IMPORT_INVALID_MAC
;
277 case SEC_ERROR_BAD_DER
:
278 case SEC_ERROR_PKCS12_DECODING_PFX
:
279 case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE
:
280 import_result
= net::ERR_PKCS12_IMPORT_INVALID_FILE
;
282 case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM
:
283 case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE
:
284 case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM
:
285 case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION
:
286 import_result
= net::ERR_PKCS12_IMPORT_UNSUPPORTED
;
289 import_result
= net::ERR_PKCS12_IMPORT_FAILED
;
293 // Finish the decoder
295 SEC_PKCS12DecoderFinish(dcx
);
296 SECITEM_ZfreeItem(&unicodePw
, PR_FALSE
);
297 return import_result
;
301 // Attempt to read the CKA_EXTRACTABLE attribute on a private key inside
302 // a token. On success, store the attribute in |extractable| and return
305 isExtractable(SECKEYPrivateKey
*privKey
, PRBool
*extractable
)
310 rv
=PK11_ReadRawAttribute(PK11_TypePrivKey
, privKey
, CKA_EXTRACTABLE
, &value
);
311 if (rv
!= SECSuccess
)
314 if ((value
.len
== 1) && (value
.data
!= NULL
))
315 *extractable
= !!(*(CK_BBOOL
*)value
.data
);
318 SECITEM_FreeItem(&value
, PR_FALSE
);
322 class PKCS12InitSingleton
{
324 // From the PKCS#12 section of nsNSSComponent::InitializeNSS in
325 // nsNSSComponent.cpp.
326 PKCS12InitSingleton() {
327 // Enable ciphers for PKCS#12
328 SEC_PKCS12EnableCipher(PKCS12_RC4_40
, 1);
329 SEC_PKCS12EnableCipher(PKCS12_RC4_128
, 1);
330 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40
, 1);
331 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128
, 1);
332 SEC_PKCS12EnableCipher(PKCS12_DES_56
, 1);
333 SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168
, 1);
334 SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168
, 1);
336 // Set no-op ascii-ucs2 conversion function to work around weird NSS
337 // interface. Thankfully, PKCS12 appears to be the only thing in NSS that
338 // uses PORT_UCS2_ASCIIConversion, so this doesn't break anything else.
339 PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn
);
343 // Leaky so it can be initialized on worker threads and because there is no
344 // cleanup necessary.
345 static base::LazyInstance
<PKCS12InitSingleton
>::Leaky g_pkcs12_init_singleton
=
346 LAZY_INSTANCE_INITIALIZER
;
350 void EnsurePKCS12Init() {
351 g_pkcs12_init_singleton
.Get();
354 // Based on nsPKCS12Blob::ImportFromFile.
355 int nsPKCS12Blob_Import(PK11SlotInfo
* slot
,
356 const char* pkcs12_data
,
358 const base::string16
& password
,
360 net::CertificateList
* imported_certs
) {
362 int rv
= nsPKCS12Blob_ImportHelper(pkcs12_data
, pkcs12_len
, password
,
363 is_extractable
, false, slot
,
366 // When the user entered a zero length password:
367 // An empty password should be represented as an empty
368 // string (a SECItem that contains a single terminating
369 // NULL UTF16 character), but some applications use a
370 // zero length SECItem.
371 // We try both variations, zero length item and empty string,
372 // without giving a user prompt when trying the different empty password
374 if (rv
== net::ERR_PKCS12_IMPORT_BAD_PASSWORD
&& password
.empty()) {
375 rv
= nsPKCS12Blob_ImportHelper(pkcs12_data
, pkcs12_len
, password
,
376 is_extractable
, true, slot
, imported_certs
);
381 // Based on nsPKCS12Blob::ExportToFile
383 // Having already loaded the certs, form them into a blob (loading the keys
384 // also), encode the blob, and stuff it into the file.
386 // TODO: handle slots correctly
387 // mirror "slotToUse" behavior from PSM 1.x
388 // verify the cert array to start off with?
389 // set appropriate error codes
391 nsPKCS12Blob_Export(std::string
* output
,
392 const net::CertificateList
& certs
,
393 const base::string16
& password
)
395 int return_count
= 0;
396 SECStatus srv
= SECSuccess
;
397 SEC_PKCS12ExportContext
*ecx
= NULL
;
398 SEC_PKCS12SafeInfo
*certSafe
= NULL
, *keySafe
= NULL
;
400 unicodePw
.type
= siBuffer
;
402 unicodePw
.data
= NULL
;
404 int numCertsExported
= 0;
406 // get file password (unicode)
407 unicodeToItem(password
.c_str(), &unicodePw
);
409 // what about slotToUse in psm 1.x ???
410 // create export context
411 ecx
= SEC_PKCS12CreateExportContext(NULL
, NULL
, NULL
/*slot*/, NULL
);
416 // add password integrity
417 srv
= SEC_PKCS12AddPasswordIntegrity(ecx
, &unicodePw
, SEC_OID_SHA1
);
418 if (srv
) goto finish
;
420 for (size_t i
=0; i
<certs
.size(); i
++) {
421 DCHECK(certs
[i
].get());
422 CERTCertificate
* nssCert
= certs
[i
]->os_cert_handle();
425 // We only allow certificate and private key extraction if the corresponding
426 // CKA_EXTRACTABLE private key attribute is set to CK_TRUE. Most hardware
427 // tokens including smartcards enforce this behavior. An internal (soft)
428 // token may ignore this attribute (and hence still be able to export) but
429 // we still refuse to attempt an export.
430 // In addition, some tokens may not support this attribute, in which case
431 // we still attempt the export and let the token implementation dictate
432 // the export behavior.
434 SECKEYPrivateKey
*privKey
=PK11_FindKeyByDERCert(nssCert
->slot
,
438 PRBool privKeyIsExtractable
= PR_FALSE
;
439 SECStatus rv
= isExtractable(privKey
, &privKeyIsExtractable
);
440 SECKEY_DestroyPrivateKey(privKey
);
442 if (rv
== SECSuccess
&& !privKeyIsExtractable
) {
443 LOG(ERROR
) << "Private key is not extractable";
449 // XXX this is why, to verify the slot is the same
450 // PK11_FindObjectForCert(nssCert, NULL, slot);
451 // create the cert and key safes
452 keySafe
= SEC_PKCS12CreateUnencryptedSafe(ecx
);
453 if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
456 certSafe
= SEC_PKCS12CreatePasswordPrivSafe(ecx
, &unicodePw
,
457 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
);
459 if (!certSafe
|| !keySafe
) {
460 LOG(ERROR
) << "!certSafe || !keySafe " << certSafe
<< " " << keySafe
;
464 // add the cert and key to the blob
465 srv
= SEC_PKCS12AddCertAndKey(ecx
, certSafe
, NULL
, nssCert
,
466 CERT_GetDefaultCertDB(),
467 keySafe
, NULL
, PR_TRUE
, &unicodePw
,
468 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC
);
469 if (srv
) goto finish
;
473 if (!numCertsExported
) goto finish
;
476 srv
= SEC_PKCS12Encode(ecx
, write_export_data
, output
);
477 if (srv
) goto finish
;
478 return_count
= numCertsExported
;
481 LOG(ERROR
) << "PKCS#12 export failed with error " << PORT_GetError();
483 SEC_PKCS12DestroyExportContext(ecx
);
484 SECITEM_ZfreeItem(&unicodePw
, PR_FALSE
);
488 } // namespace mozilla_security_manager