nsPKCS12Blob.cpp (12872B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "nsPKCS12Blob.h" 6 7 #include "mozilla/Assertions.h" 8 #include "mozilla/Logging.h" 9 #include "mozilla/Preferences.h" 10 #include "mozilla/StaticPrefs_security.h" 11 #include "mozpkix/pkixtypes.h" 12 #include "nsIFile.h" 13 #include "nsIInputStream.h" 14 #include "nsIX509CertDB.h" 15 #include "nsNetUtil.h" 16 #include "nsNSSCertHelper.h" 17 #include "nsNSSCertificate.h" 18 #include "nsNSSHelper.h" 19 #include "nsReadableUtils.h" 20 #include "nsTArray.h" 21 #include "nsThreadUtils.h" 22 #include "p12plcy.h" 23 #include "ScopedNSSTypes.h" 24 #include "secerr.h" 25 26 using namespace mozilla; 27 extern LazyLogModule gPIPNSSLog; 28 29 #define PIP_PKCS12_BUFFER_SIZE 2048 30 #define PIP_PKCS12_NOSMARTCARD_EXPORT 4 31 #define PIP_PKCS12_RESTORE_FAILED 5 32 #define PIP_PKCS12_BACKUP_FAILED 6 33 #define PIP_PKCS12_NSS_ERROR 7 34 35 nsPKCS12Blob::nsPKCS12Blob() : mUIContext(new PipUIContext()) {} 36 37 // Given a file handle, read a PKCS#12 blob from that file, decode it, and 38 // import the results into the internal database. 39 nsresult nsPKCS12Blob::ImportFromFile(nsIFile* aFile, 40 const nsAString& aPassword, 41 uint32_t& aError) { 42 uint32_t passwordBufferLength; 43 UniquePtr<uint8_t[]> passwordBuffer; 44 45 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); 46 if (!slot) { 47 return NS_ERROR_FAILURE; 48 } 49 50 passwordBuffer = stringToBigEndianBytes(aPassword, passwordBufferLength); 51 52 // initialize the decoder 53 SECItem unicodePw = {siBuffer, passwordBuffer.get(), passwordBufferLength}; 54 UniqueSEC_PKCS12DecoderContext dcx( 55 SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, nullptr, nullptr, 56 nullptr, nullptr, nullptr)); 57 if (!dcx) { 58 return NS_ERROR_FAILURE; 59 } 60 // read input aFile and feed it to the decoder 61 PRErrorCode nssError; 62 nsresult rv = inputToDecoder(dcx, aFile, nssError); 63 if (NS_FAILED(rv)) { 64 return rv; 65 } 66 if (nssError != 0) { 67 aError = handlePRErrorCode(nssError); 68 return NS_OK; 69 } 70 // verify the blob 71 SECStatus srv = SEC_PKCS12DecoderVerify(dcx.get()); 72 if (srv != SECSuccess) { 73 aError = handlePRErrorCode(PR_GetError()); 74 return NS_OK; 75 } 76 // validate bags 77 srv = SEC_PKCS12DecoderValidateBags(dcx.get(), nicknameCollision); 78 if (srv != SECSuccess) { 79 aError = handlePRErrorCode(PR_GetError()); 80 return NS_OK; 81 } 82 // import cert and key 83 srv = SEC_PKCS12DecoderImportBags(dcx.get()); 84 if (srv != SECSuccess) { 85 aError = handlePRErrorCode(PR_GetError()); 86 return NS_OK; 87 } 88 aError = nsIX509CertDB::Success; 89 return NS_OK; 90 } 91 92 static bool isExtractable(UniqueSECKEYPrivateKey& privKey) { 93 ScopedAutoSECItem value; 94 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(), 95 CKA_EXTRACTABLE, &value); 96 if (rv != SECSuccess) { 97 return false; 98 } 99 100 bool isExtractable = false; 101 if ((value.len == 1) && value.data) { 102 isExtractable = !!(*(CK_BBOOL*)value.data); 103 } 104 return isExtractable; 105 } 106 107 // Having already loaded the certs, form them into a blob (loading the keys 108 // also), encode the blob, and stuff it into the file. 109 nsresult nsPKCS12Blob::ExportToFile(nsIFile* aFile, 110 const nsTArray<RefPtr<nsIX509Cert>>& aCerts, 111 const nsAString& aPassword, 112 uint32_t& aError) { 113 nsCString passwordUtf8 = NS_ConvertUTF16toUTF8(aPassword); 114 uint32_t passwordBufferLength = passwordUtf8.Length(); 115 aError = nsIX509CertDB::Success; 116 // The conversion to UCS2 is executed by sec_pkcs12_encode_password when 117 // necessary (for some older PKCS12 algorithms). The NSS 3.31 and newer 118 // expects password to be in the utf8 encoding to support modern encoders. 119 UniquePtr<unsigned char[]> passwordBuffer( 120 reinterpret_cast<unsigned char*>(ToNewCString(passwordUtf8))); 121 if (!passwordBuffer.get()) { 122 return NS_OK; 123 } 124 UniqueSEC_PKCS12ExportContext ecx( 125 SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr, nullptr)); 126 if (!ecx) { 127 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 128 return NS_OK; 129 } 130 bool useModernCrypto = 131 StaticPrefs::security_pki_use_modern_crypto_with_pkcs12(); 132 // add password integrity 133 SECItem unicodePw = {siBuffer, passwordBuffer.get(), passwordBufferLength}; 134 SECStatus srv = SEC_PKCS12AddPasswordIntegrity( 135 ecx.get(), &unicodePw, useModernCrypto ? SEC_OID_SHA256 : SEC_OID_SHA1); 136 if (srv != SECSuccess) { 137 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 138 return NS_OK; 139 } 140 for (auto& cert : aCerts) { 141 UniqueCERTCertificate nssCert(cert->GetCert()); 142 if (!nssCert) { 143 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 144 return NS_OK; 145 } 146 // We can probably only successfully export certs that are on the internal 147 // token. Most, if not all, smart card vendors won't let you extract the 148 // private key (in any way shape or form) from the card. So let's punt if 149 // the cert is not in the internal db. 150 if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) { 151 // We aren't the internal token, see if the key is extractable. 152 UniqueSECKEYPrivateKey privKey( 153 PK11_FindKeyByDERCert(nssCert->slot, nssCert.get(), mUIContext)); 154 if (privKey && !isExtractable(privKey)) { 155 // This is informative. If a serious error occurs later it will 156 // override it later and return. 157 aError = nsIX509CertDB::ERROR_PKCS12_NOSMARTCARD_EXPORT; 158 continue; 159 } 160 } 161 162 // certSafe and keySafe are owned by ecx. 163 SEC_PKCS12SafeInfo* certSafe; 164 SEC_PKCS12SafeInfo* keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx.get()); 165 // We use SEC_OID_AES_128_CBC for the password and SEC_OID_AES_256_CBC 166 // for the certificate because it's a default for openssl an pk12util 167 // command. 168 if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) { 169 certSafe = keySafe; 170 } else { 171 SECOidTag privAlg = 172 useModernCrypto ? SEC_OID_AES_128_CBC 173 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; 174 certSafe = 175 SEC_PKCS12CreatePasswordPrivSafe(ecx.get(), &unicodePw, privAlg); 176 } 177 if (!certSafe || !keySafe) { 178 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 179 return NS_OK; 180 } 181 // add the cert and key to the blob 182 SECOidTag algorithm = 183 useModernCrypto 184 ? SEC_OID_AES_256_CBC 185 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC; 186 srv = SEC_PKCS12AddCertAndKey(ecx.get(), certSafe, nullptr, nssCert.get(), 187 CERT_GetDefaultCertDB(), keySafe, nullptr, 188 true, &unicodePw, algorithm); 189 if (srv != SECSuccess) { 190 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 191 return NS_OK; 192 } 193 } 194 195 UniquePRFileDesc prFile; 196 PRFileDesc* rawPRFile; 197 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 198 0664, &rawPRFile); 199 if (NS_FAILED(rv) || !rawPRFile) { 200 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 201 return NS_OK; 202 } 203 prFile.reset(rawPRFile); 204 // encode and write 205 srv = SEC_PKCS12Encode(ecx.get(), writeExportFile, prFile.get()); 206 if (srv != SECSuccess) { 207 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED; 208 } 209 return NS_OK; 210 } 211 212 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to a buffer of 213 // octets. Must handle byte order correctly. 214 UniquePtr<uint8_t[]> nsPKCS12Blob::stringToBigEndianBytes( 215 const nsAString& uni, uint32_t& bytesLength) { 216 if (uni.IsVoid()) { 217 bytesLength = 0; 218 return nullptr; 219 } 220 221 uint32_t wideLength = uni.Length() + 1; // +1 for the null terminator. 222 bytesLength = wideLength * 2; 223 auto buffer = MakeUnique<uint8_t[]>(bytesLength); 224 225 // We have to use a cast here because on Windows, uni.get() returns 226 // char16ptr_t instead of char16_t*. 227 mozilla::NativeEndian::copyAndSwapToBigEndian( 228 buffer.get(), static_cast<const char16_t*>(uni.BeginReading()), 229 wideLength); 230 231 return buffer; 232 } 233 234 // Given a decoder, read bytes from file and input them to the decoder. 235 nsresult nsPKCS12Blob::inputToDecoder(UniqueSEC_PKCS12DecoderContext& dcx, 236 nsIFile* file, PRErrorCode& nssError) { 237 nssError = 0; 238 239 nsCOMPtr<nsIInputStream> fileStream; 240 nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file); 241 if (NS_FAILED(rv)) { 242 return rv; 243 } 244 245 char buf[PIP_PKCS12_BUFFER_SIZE]; 246 uint32_t amount; 247 while (true) { 248 rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount); 249 if (NS_FAILED(rv)) { 250 return rv; 251 } 252 // feed the file data into the decoder 253 SECStatus srv = 254 SEC_PKCS12DecoderUpdate(dcx.get(), (unsigned char*)buf, amount); 255 if (srv != SECSuccess) { 256 nssError = PR_GetError(); 257 return NS_OK; 258 } 259 if (amount < PIP_PKCS12_BUFFER_SIZE) { 260 break; 261 } 262 } 263 return NS_OK; 264 } 265 266 // What to do when the nickname collides with one already in the db. 267 SECItem* nsPKCS12Blob::nicknameCollision(SECItem* oldNick, PRBool* cancel, 268 void* wincx) { 269 *cancel = false; 270 int count = 1; 271 nsCString nickname; 272 nsAutoString nickFromProp; 273 nsresult rv = GetPIPNSSBundleString("P12DefaultNickname", nickFromProp); 274 if (NS_FAILED(rv)) { 275 return nullptr; 276 } 277 NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp); 278 // The user is trying to import a PKCS#12 file that doesn't have the 279 // attribute we use to set the nickname. So in order to reduce the 280 // number of interactions we require with the user, we'll build a nickname 281 // for the user. The nickname isn't prominently displayed in the UI, 282 // so it's OK if we generate one on our own here. 283 // XXX If the NSS API were smarter and actually passed a pointer to 284 // the CERTCertificate* we're importing we could actually just 285 // call default_nickname (which is what the issuance code path 286 // does) and come up with a reasonable nickname. Alas, the NSS 287 // API limits our ability to produce a useful nickname without 288 // bugging the user. :( 289 while (1) { 290 // If we've gotten this far, that means there isn't a certificate 291 // in the database that has the same subject name as the cert we're 292 // trying to import. So we need to come up with a "nickname" to 293 // satisfy the NSS requirement or fail in trying to import. 294 // Basically we use a default nickname from a properties file and 295 // see if a certificate exists with that nickname. If there isn't, then 296 // create update the count by one and append the string '#1' Or 297 // whatever the count currently is, and look for a cert with 298 // that nickname. Keep updating the count until we find a nickname 299 // without a corresponding cert. 300 // XXX If a user imports *many* certs without the 'friendly name' 301 // attribute, then this may take a long time. :( 302 nickname = nickFromPropC; 303 if (count > 1) { 304 nickname.AppendPrintf(" #%d", count); 305 } 306 UniqueCERTCertificate cert( 307 CERT_FindCertByNickname(CERT_GetDefaultCertDB(), nickname.get())); 308 if (!cert) { 309 break; 310 } 311 count++; 312 } 313 UniqueSECItem newNick( 314 SECITEM_AllocItem(nullptr, nullptr, nickname.Length() + 1)); 315 if (!newNick) { 316 return nullptr; 317 } 318 memcpy(newNick->data, nickname.get(), nickname.Length()); 319 newNick->data[nickname.Length()] = 0; 320 321 return newNick.release(); 322 } 323 324 // write bytes to the exported PKCS#12 file 325 void nsPKCS12Blob::writeExportFile(void* arg, const char* buf, 326 unsigned long len) { 327 PRFileDesc* file = static_cast<PRFileDesc*>(arg); 328 MOZ_RELEASE_ASSERT(file); 329 PR_Write(file, buf, len); 330 } 331 332 // Translate PRErrorCode to nsIX509CertDB error 333 uint32_t nsPKCS12Blob::handlePRErrorCode(PRErrorCode aPrerr) { 334 MOZ_ASSERT(aPrerr != 0); 335 uint32_t error = nsIX509CertDB::ERROR_UNKNOWN; 336 switch (aPrerr) { 337 case SEC_ERROR_PKCS12_CERT_COLLISION: 338 error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA; 339 break; 340 // INVALID_ARGS is returned on bad password when importing cert 341 // exported from firefox or generated by openssl 342 case SEC_ERROR_INVALID_ARGS: 343 case SEC_ERROR_BAD_PASSWORD: 344 error = nsIX509CertDB::ERROR_BAD_PASSWORD; 345 break; 346 case SEC_ERROR_BAD_DER: 347 case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE: 348 case SEC_ERROR_PKCS12_INVALID_MAC: 349 error = nsIX509CertDB::ERROR_DECODE_ERROR; 350 break; 351 case SEC_ERROR_PKCS12_DUPLICATE_DATA: 352 error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA; 353 break; 354 } 355 return error; 356 }