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