// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/test/cert_test_util.h" #include #include #include #include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "crypto/ec_private_key.h" #include "crypto/nss_key_util.h" #include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" #include "net/cert/cert_type.h" #include "net/cert/x509_util_nss.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/ec.h" #include "third_party/boringssl/src/include/openssl/ec_key.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/mem.h" namespace net { bool ImportSensitiveKeyFromFile(const base::FilePath& dir, base::StringPiece key_filename, PK11SlotInfo* slot) { base::FilePath key_path = dir.AppendASCII(key_filename); std::string key_pkcs8; bool success = base::ReadFileToString(key_path, &key_pkcs8); if (!success) { LOG(ERROR) << "Failed to read file " << key_path.value(); return false; } std::vector key_vector(key_pkcs8.begin(), key_pkcs8.end()); // Prior to NSS 3.30, NSS cannot import unencrypted ECDSA private keys. Detect // such keys and encrypt with an empty password before importing. Once our // minimum version is raised to NSS 3.30, this logic can be removed. See // https://bugzilla.mozilla.org/show_bug.cgi?id=1295121 CBS cbs; CBS_init(&cbs, key_vector.data(), key_vector.size()); bssl::UniquePtr evp_pkey(EVP_parse_private_key(&cbs)); if (!evp_pkey) { LOG(ERROR) << "Could not parse private key from file " << key_path.value(); return false; } if (EVP_PKEY_id(evp_pkey.get()) == EVP_PKEY_EC) { std::unique_ptr ec_private_key = crypto::ECPrivateKey::CreateFromPrivateKeyInfo(key_vector); std::vector encrypted; if (!ec_private_key || !ec_private_key->ExportEncryptedPrivateKey(&encrypted)) { LOG(ERROR) << "Error importing private key from file " << key_path.value(); return false; } SECItem encrypted_item = {siBuffer, encrypted.data(), static_cast(encrypted.size())}; SECKEYEncryptedPrivateKeyInfo epki; memset(&epki, 0, sizeof(epki)); crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (SEC_QuickDERDecodeItem( arena.get(), &epki, SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), &encrypted_item) != SECSuccess) { LOG(ERROR) << "Error importing private key from file " << key_path.value(); return false; } // NSS uses the serialized public key in X9.62 form as the "public value" // for key ID purposes. EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(ec_private_key->key()); bssl::ScopedCBB cbb; uint8_t* public_value; size_t public_value_len; if (!CBB_init(cbb.get(), 0) || !EC_POINT_point2cbb(cbb.get(), EC_KEY_get0_group(ec_key), EC_KEY_get0_public_key(ec_key), POINT_CONVERSION_UNCOMPRESSED, nullptr) || !CBB_finish(cbb.get(), &public_value, &public_value_len)) { LOG(ERROR) << "Error importing private key from file " << key_path.value(); return false; } bssl::UniquePtr scoped_public_value(public_value); SECItem public_item = {siBuffer, public_value, static_cast(public_value_len)}; SECItem password_item = {siBuffer, nullptr, 0}; if (PK11_ImportEncryptedPrivateKeyInfo( slot, &epki, &password_item, nullptr /* nickname */, &public_item, PR_TRUE /* permanent */, PR_TRUE /* private */, ecKey, KU_DIGITAL_SIGNATURE, nullptr /* wincx */) != SECSuccess) { LOG(ERROR) << "Error importing private key from file " << key_path.value(); return false; } return true; } crypto::ScopedSECKEYPrivateKey private_key( crypto::ImportNSSKeyFromPrivateKeyInfo(slot, key_vector, true /* permanent */)); LOG_IF(ERROR, !private_key) << "Could not create key from file " << key_path.value(); return !!private_key; } bool ImportClientCertToSlot(CERTCertificate* nss_cert, PK11SlotInfo* slot) { std::string nickname = x509_util::GetDefaultUniqueNickname(nss_cert, USER_CERT, slot); SECStatus rv = PK11_ImportCert(slot, nss_cert, CK_INVALID_HANDLE, nickname.c_str(), PR_FALSE); if (rv != SECSuccess) { LOG(ERROR) << "Could not import cert"; return false; } return true; } ScopedCERTCertificate ImportClientCertToSlot( const scoped_refptr& cert, PK11SlotInfo* slot) { ScopedCERTCertificate nss_cert = x509_util::CreateCERTCertificateFromX509Certificate(cert.get()); if (!nss_cert) return nullptr; if (!ImportClientCertToSlot(nss_cert.get(), slot)) return nullptr; return nss_cert; } scoped_refptr ImportClientCertAndKeyFromFile( const base::FilePath& dir, base::StringPiece cert_filename, base::StringPiece key_filename, PK11SlotInfo* slot, ScopedCERTCertificate* nss_cert) { if (!ImportSensitiveKeyFromFile(dir, key_filename, slot)) { LOG(ERROR) << "Could not import private key from file " << key_filename; return nullptr; } scoped_refptr cert(ImportCertFromFile(dir, cert_filename)); if (!cert.get()) { LOG(ERROR) << "Failed to parse cert from file " << cert_filename; return nullptr; } *nss_cert = ImportClientCertToSlot(cert, slot); if (!*nss_cert) return nullptr; // |cert| continues to point to the original X509Certificate before the // import to |slot|. However this should not make a difference as NSS handles // state globally. return cert; } scoped_refptr ImportClientCertAndKeyFromFile( const base::FilePath& dir, base::StringPiece cert_filename, base::StringPiece key_filename, PK11SlotInfo* slot) { ScopedCERTCertificate nss_cert; return ImportClientCertAndKeyFromFile(dir, cert_filename, key_filename, slot, &nss_cert); } ScopedCERTCertificate ImportCERTCertificateFromFile( const base::FilePath& certs_dir, base::StringPiece cert_file) { scoped_refptr cert = ImportCertFromFile(certs_dir, cert_file); if (!cert) return nullptr; return x509_util::CreateCERTCertificateFromX509Certificate(cert.get()); } ScopedCERTCertificateList CreateCERTCertificateListFromFile( const base::FilePath& certs_dir, base::StringPiece cert_file, int format) { CertificateList certs = CreateCertificateListFromFile(certs_dir, cert_file, format); ScopedCERTCertificateList nss_certs; for (const auto& cert : certs) { ScopedCERTCertificate nss_cert = x509_util::CreateCERTCertificateFromX509Certificate(cert.get()); if (!nss_cert) return {}; nss_certs.push_back(std::move(nss_cert)); } return nss_certs; } } // namespace net