1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/test/cert_test_util.h"
6
7 #include <pk11pub.h>
8 #include <secmodt.h>
9 #include <string.h>
10
11 #include <memory>
12
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/logging.h"
16 #include "crypto/ec_private_key.h"
17 #include "crypto/nss_key_util.h"
18 #include "crypto/nss_util.h"
19 #include "crypto/scoped_nss_types.h"
20 #include "net/cert/cert_type.h"
21 #include "net/cert/x509_util_nss.h"
22 #include "third_party/boringssl/src/include/openssl/bytestring.h"
23 #include "third_party/boringssl/src/include/openssl/ec.h"
24 #include "third_party/boringssl/src/include/openssl/ec_key.h"
25 #include "third_party/boringssl/src/include/openssl/evp.h"
26 #include "third_party/boringssl/src/include/openssl/mem.h"
27
28 namespace net {
29
ImportSensitiveKeyFromFile(const base::FilePath & dir,const std::string & key_filename,PK11SlotInfo * slot)30 bool ImportSensitiveKeyFromFile(const base::FilePath& dir,
31 const std::string& key_filename,
32 PK11SlotInfo* slot) {
33 base::FilePath key_path = dir.AppendASCII(key_filename);
34 std::string key_pkcs8;
35 bool success = base::ReadFileToString(key_path, &key_pkcs8);
36 if (!success) {
37 LOG(ERROR) << "Failed to read file " << key_path.value();
38 return false;
39 }
40
41 std::vector<uint8_t> key_vector(key_pkcs8.begin(), key_pkcs8.end());
42
43 // Prior to NSS 3.30, NSS cannot import unencrypted ECDSA private keys. Detect
44 // such keys and encrypt with an empty password before importing. Once our
45 // minimum version is raised to NSS 3.30, this logic can be removed. See
46 // https://bugzilla.mozilla.org/show_bug.cgi?id=1295121
47 CBS cbs;
48 CBS_init(&cbs, key_vector.data(), key_vector.size());
49 bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_parse_private_key(&cbs));
50 if (!evp_pkey) {
51 LOG(ERROR) << "Could not parse private key from file " << key_path.value();
52 return false;
53 }
54 if (EVP_PKEY_id(evp_pkey.get()) == EVP_PKEY_EC) {
55 std::unique_ptr<crypto::ECPrivateKey> ec_private_key =
56 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(key_vector);
57 std::vector<uint8_t> encrypted;
58 if (!ec_private_key ||
59 !ec_private_key->ExportEncryptedPrivateKey(&encrypted)) {
60 LOG(ERROR) << "Error importing private key from file "
61 << key_path.value();
62 return false;
63 }
64
65 SECItem encrypted_item = {siBuffer, encrypted.data(),
66 static_cast<unsigned>(encrypted.size())};
67 SECKEYEncryptedPrivateKeyInfo epki;
68 memset(&epki, 0, sizeof(epki));
69 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
70 if (SEC_QuickDERDecodeItem(
71 arena.get(), &epki,
72 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate),
73 &encrypted_item) != SECSuccess) {
74 LOG(ERROR) << "Error importing private key from file "
75 << key_path.value();
76 return false;
77 }
78
79 // NSS uses the serialized public key in X9.62 form as the "public value"
80 // for key ID purposes.
81 EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(ec_private_key->key());
82 bssl::ScopedCBB cbb;
83 uint8_t* public_value;
84 size_t public_value_len;
85 if (!CBB_init(cbb.get(), 0) ||
86 !EC_POINT_point2cbb(cbb.get(), EC_KEY_get0_group(ec_key),
87 EC_KEY_get0_public_key(ec_key),
88 POINT_CONVERSION_UNCOMPRESSED, nullptr) ||
89 !CBB_finish(cbb.get(), &public_value, &public_value_len)) {
90 LOG(ERROR) << "Error importing private key from file "
91 << key_path.value();
92 return false;
93 }
94 bssl::UniquePtr<uint8_t> scoped_public_value(public_value);
95 SECItem public_item = {siBuffer, public_value,
96 static_cast<unsigned>(public_value_len)};
97
98 SECItem password_item = {siBuffer, nullptr, 0};
99 if (PK11_ImportEncryptedPrivateKeyInfo(
100 slot, &epki, &password_item, nullptr /* nickname */, &public_item,
101 PR_TRUE /* permanent */, PR_TRUE /* private */, ecKey,
102 KU_DIGITAL_SIGNATURE, nullptr /* wincx */) != SECSuccess) {
103 LOG(ERROR) << "Error importing private key from file "
104 << key_path.value();
105 return false;
106 }
107 return true;
108 }
109
110 crypto::ScopedSECKEYPrivateKey private_key(
111 crypto::ImportNSSKeyFromPrivateKeyInfo(slot, key_vector,
112 true /* permanent */));
113 LOG_IF(ERROR, !private_key) << "Could not create key from file "
114 << key_path.value();
115 return !!private_key;
116 }
117
ImportClientCertToSlot(CERTCertificate * nss_cert,PK11SlotInfo * slot)118 bool ImportClientCertToSlot(CERTCertificate* nss_cert, PK11SlotInfo* slot) {
119 std::string nickname =
120 x509_util::GetDefaultUniqueNickname(nss_cert, USER_CERT, slot);
121 SECStatus rv = PK11_ImportCert(slot, nss_cert, CK_INVALID_HANDLE,
122 nickname.c_str(), PR_FALSE);
123 if (rv != SECSuccess) {
124 LOG(ERROR) << "Could not import cert";
125 return false;
126 }
127 return true;
128 }
129
ImportClientCertToSlot(const scoped_refptr<X509Certificate> & cert,PK11SlotInfo * slot)130 ScopedCERTCertificate ImportClientCertToSlot(
131 const scoped_refptr<X509Certificate>& cert,
132 PK11SlotInfo* slot) {
133 ScopedCERTCertificate nss_cert =
134 x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
135 if (!nss_cert)
136 return nullptr;
137
138 if (!ImportClientCertToSlot(nss_cert.get(), slot))
139 return nullptr;
140
141 return nss_cert;
142 }
143
ImportClientCertAndKeyFromFile(const base::FilePath & dir,const std::string & cert_filename,const std::string & key_filename,PK11SlotInfo * slot,ScopedCERTCertificate * nss_cert)144 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
145 const base::FilePath& dir,
146 const std::string& cert_filename,
147 const std::string& key_filename,
148 PK11SlotInfo* slot,
149 ScopedCERTCertificate* nss_cert) {
150 if (!ImportSensitiveKeyFromFile(dir, key_filename, slot)) {
151 LOG(ERROR) << "Could not import private key from file " << key_filename;
152 return nullptr;
153 }
154
155 scoped_refptr<X509Certificate> cert(ImportCertFromFile(dir, cert_filename));
156
157 if (!cert.get()) {
158 LOG(ERROR) << "Failed to parse cert from file " << cert_filename;
159 return nullptr;
160 }
161
162 *nss_cert = ImportClientCertToSlot(cert, slot);
163 if (!*nss_cert)
164 return nullptr;
165
166 // |cert| continues to point to the original X509Certificate before the
167 // import to |slot|. However this should not make a difference as NSS handles
168 // state globally.
169 return cert;
170 }
171
ImportClientCertAndKeyFromFile(const base::FilePath & dir,const std::string & cert_filename,const std::string & key_filename,PK11SlotInfo * slot)172 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
173 const base::FilePath& dir,
174 const std::string& cert_filename,
175 const std::string& key_filename,
176 PK11SlotInfo* slot) {
177 ScopedCERTCertificate nss_cert;
178 return ImportClientCertAndKeyFromFile(dir, cert_filename, key_filename, slot,
179 &nss_cert);
180 }
181
ImportCERTCertificateFromFile(const base::FilePath & certs_dir,const std::string & cert_file)182 ScopedCERTCertificate ImportCERTCertificateFromFile(
183 const base::FilePath& certs_dir,
184 const std::string& cert_file) {
185 scoped_refptr<X509Certificate> cert =
186 ImportCertFromFile(certs_dir, cert_file);
187 if (!cert)
188 return nullptr;
189 return x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
190 }
191
CreateCERTCertificateListFromFile(const base::FilePath & certs_dir,const std::string & cert_file,int format)192 ScopedCERTCertificateList CreateCERTCertificateListFromFile(
193 const base::FilePath& certs_dir,
194 const std::string& cert_file,
195 int format) {
196 CertificateList certs =
197 CreateCertificateListFromFile(certs_dir, cert_file, format);
198 ScopedCERTCertificateList nss_certs;
199 for (const auto& cert : certs) {
200 ScopedCERTCertificate nss_cert =
201 x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
202 if (!nss_cert)
203 return {};
204 nss_certs.push_back(std::move(nss_cert));
205 }
206 return nss_certs;
207 }
208
209 } // namespace net
210