OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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 "chrome/browser/extensions/api/networking_private/networking_private_cr ypto.h" | |
6 | |
7 #include <cert.h> | |
8 #include <keyhi.h> | |
9 #include <keythi.h> | |
10 #include <pk11pub.h> | |
11 #include <sechash.h> | |
12 #include <secport.h> | |
13 | |
14 #include "base/base64.h" | |
15 #include "base/memory/scoped_ptr.h" | |
16 #include "base/strings/string_number_conversions.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/strings/stringprintf.h" | |
19 #include "crypto/nss_util.h" | |
20 #include "crypto/rsa_private_key.h" | |
21 #include "net/cert/pem_tokenizer.h" | |
22 #include "net/cert/x509_certificate.h" | |
23 | |
24 const char kTrustedCAExponent[] = "00010001"; | |
25 | |
26 const char kTrustedCAModulus[] = | |
27 "BC2280BD80F63A21003BAE765E357F3DC3645C559486342F058728CDF7698C17B350A7B8" | |
28 "82FADFC7432DD67EABA06FB7137280A44715C1209950CDEC1462095BA498CDD241B6364E" | |
29 "FFE82E32304A81A842A36C9B336ECAB2F55366E02753861A851EA7393F4A778EFB546666" | |
30 "FB5854C05E39C7F550060BE08AD4CEE16A551F8B1700E669A327E60825693C129D8D052C" | |
31 "D62EA231DEB45250D62049DE71A0F9AD204012F1DD25EBD5E6B836F4D68F7FCA43DCD710" | |
32 "5BE63F518A85B3F3FFF6032DCB234F9CAD18E793058CAC529AF74CE9997ABE6E7E4D0AE3" | |
33 "C61CA993FA3AA5915D1CBD66EBCC60DC8674CACFF8921C987D57FA61479EAB80B7E44880" | |
34 "2A92C51B"; | |
35 | |
36 namespace { | |
37 bool GetDERFromPEM(const std::string& pem_data, | |
38 const std::string& pem_type, | |
39 std::string* der_output) { | |
40 std::vector<std::string> headers; | |
41 headers.push_back(pem_type); | |
42 net::PEMTokenizer pem_tok(pem_data, headers); | |
43 if (!pem_tok.GetNext()) { | |
44 return false; | |
45 } | |
46 | |
47 *der_output = pem_tok.data(); | |
48 return true; | |
49 } | |
50 } // namespace | |
51 | |
52 NetworkingPrivateCrypto::NetworkingPrivateCrypto() | |
53 : ca_public_key_(NULL), | |
54 cert_public_key_(NULL), | |
55 cert_(NULL), | |
56 enc_public_key_(NULL) {} | |
57 | |
58 NetworkingPrivateCrypto::~NetworkingPrivateCrypto() { | |
59 if (ca_public_key_) | |
60 SECKEY_DestroyPublicKey(ca_public_key_); | |
61 if (cert_public_key_) | |
62 SECKEY_DestroyPublicKey(cert_public_key_); | |
63 if (cert_) | |
64 CERT_DestroyCertificate(cert_); | |
65 if (enc_public_key_) | |
66 SECKEY_DestroyPublicKey(enc_public_key_); | |
67 } | |
68 | |
69 bool NetworkingPrivateCrypto::VerifyCredentials( | |
70 const std::string& certificate, | |
71 const std::string& signed_data, | |
72 const std::string& unsigned_data, | |
73 const std::string& connected_mac) { | |
74 crypto::EnsureNSSInit(); | |
75 | |
76 net::CertificateList cl = | |
77 net::X509Certificate::CreateCertificateListFromBytes( | |
78 const_cast<char*>(certificate.c_str()), | |
79 certificate.size(), | |
80 net::X509Certificate::FORMAT_AUTO); | |
81 if (!cl.size()) { | |
82 LOG(ERROR) << "Failed to parse certificate."; | |
83 return false; | |
84 } | |
85 | |
86 // Check that the device listed in the certificate is correct. | |
87 std::string subject_name = cl[0]->subject().GetDisplayName(); | |
mef
2013/08/28 18:05:59
Alternatively I can get subject name directly from
Ryan Sleevi
2013/08/28 18:31:26
The above is really only intended for display purp
mef
2013/08/28 21:28:58
Done.
| |
88 // Something like evt_e161 001a11ffacdf | |
89 size_t subject_mac_pos = subject_name.rfind(' '); | |
90 if (subject_mac_pos == std::string::npos) { | |
91 LOG(ERROR) << "Badly formatted subject."; | |
92 return false; | |
93 } | |
94 | |
95 std::string translated_mac; | |
96 RemoveChars(connected_mac, ":", &translated_mac); | |
97 if (base::strcasecmp(translated_mac.c_str(), | |
98 subject_name.substr(subject_mac_pos + 1).c_str())) { | |
Ryan Sleevi
2013/08/28 19:02:37
why not just use std::string::compare?
mef
2013/08/28 21:28:58
Done.
| |
99 LOG(ERROR) << "MAC addresses don't match."; | |
100 return false; | |
101 } | |
102 | |
103 std::string cert_data; | |
104 if (!GetDERFromPEM(certificate, "CERTIFICATE", &cert_data)) { | |
Ryan Sleevi
2013/08/28 19:02:37
Why are you parsing the certificate twice like thi
mef
2013/08/28 21:28:58
Done.
| |
105 LOG(ERROR) << "Failed to parse certificate."; | |
106 return false; | |
107 } | |
108 | |
109 SECItem der_cert; | |
110 der_cert.data = | |
111 reinterpret_cast<unsigned char*>(const_cast<char*>(cert_data.c_str())); | |
112 der_cert.len = cert_data.length(); | |
113 der_cert.type = siDERCertBuffer; | |
114 | |
115 // Parse into a certificate structure. | |
116 cert_ = CERT_NewTempCertificate( | |
117 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); | |
118 | |
119 if (!cert_) { | |
120 LOG(ERROR) << "Failed to parse certificate."; | |
121 return false; | |
122 } | |
123 | |
124 SECStatus verified = SECSuccess; | |
125 | |
126 // TODO(mef): Add exports to third_party/nss/nss/exports_win.def and roll DEPS. | |
127 #if !defined(OS_WIN) | |
128 crypto::PrivateKeyInfoCodec trusted_ca_key_codec(true); | |
mef
2013/08/28 18:05:59
crypto::PrivateKeyInfoCodec is causing linking err
Ryan Sleevi
2013/08/28 18:31:26
Correct, it's not exported, because it's not part
mef
2013/08/28 21:28:58
Done.
| |
129 base::HexStringToBytes(kTrustedCAModulus, trusted_ca_key_codec.modulus()); | |
130 base::HexStringToBytes(kTrustedCAExponent, | |
131 trusted_ca_key_codec.public_exponent()); | |
132 std::vector<uint8> trusted_ca_key_der; | |
133 if (!trusted_ca_key_codec.ExportPublicKey(&trusted_ca_key_der)) { | |
134 LOG(ERROR) << "Failed CA ExportPublicKey"; | |
135 return false; | |
136 } | |
137 | |
138 SECItem trusted_ca_key_der_item; | |
139 trusted_ca_key_der_item.data = | |
140 const_cast<unsigned char*>(&trusted_ca_key_der.front()); | |
141 trusted_ca_key_der_item.len = trusted_ca_key_der.size(); | |
142 | |
143 ca_public_key_ = SECKEY_ImportDERPublicKey(&trusted_ca_key_der_item, CKK_RSA); | |
144 verified = CERT_VerifySignedDataWithPublicKey( | |
mef
2013/08/28 18:05:59
Is it ok to add these to third_party/nss/nss/expor
Ryan Sleevi
2013/08/28 18:31:26
Use http://mxr.mozilla.org/nss/source/lib/nss/nss.
mef
2013/08/28 21:28:58
Done. I did go with CERT_VerifySignedDataWithPubli
| |
145 &cert_->signatureWrap, ca_public_key_, NULL); | |
146 if (verified != SECSuccess) { | |
147 LOG(ERROR) << "Certificate is not issued by trusted CA."; | |
148 return false; | |
149 } | |
150 #endif | |
151 | |
152 // Excellent, the certificate checks out, now make sure that the certificate | |
153 // matches the unsigned data presented. | |
154 // We're going to verify that hash(unsigned_data) == public(signed_data) | |
155 cert_public_key_ = CERT_ExtractPublicKey(cert_); | |
Ryan Sleevi
2013/08/28 18:31:26
Please go through the comments for "we" and look t
mef
2013/08/28 21:28:58
Done.
| |
156 | |
157 if (!cert_public_key_) { | |
158 LOG(ERROR) << "Unable to extract public key from certificate."; | |
159 return false; | |
160 } | |
161 | |
162 struct rsa_hash { | |
163 unsigned char asn1[15]; | |
164 unsigned char data[SHA1_LENGTH]; | |
165 } rsa_hash = {{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, | |
166 0x1a, 0x05, 0x00, 0x04, 0x14}, }; | |
mef
2013/08/28 18:05:59
Is there a better way to add ASN1 prefix in front
Ryan Sleevi
2013/08/28 18:31:26
Yeah, don't use this API - you're totally using th
mef
2013/08/28 21:28:58
Done.
| |
167 | |
168 SECStatus hashed = HASH_HashBuf(HASH_AlgSHA1, | |
169 rsa_hash.data, | |
170 reinterpret_cast<unsigned char*>( | |
171 const_cast<char*>(unsigned_data.c_str())), | |
172 unsigned_data.size()); | |
173 DCHECK(hashed == SECSuccess); | |
174 SECItem signature_item = { | |
175 siBuffer, | |
176 reinterpret_cast<unsigned char*>(const_cast<char*>(signed_data.c_str())), | |
177 static_cast<unsigned int>(signed_data.size())}; | |
178 SECItem hash_item = {siBuffer, reinterpret_cast<unsigned char*>(&rsa_hash), | |
179 sizeof(rsa_hash)}; | |
180 | |
181 verified = PK11_Verify(cert_public_key_, &signature_item, &hash_item, NULL); | |
182 if (verified != SECSuccess) { | |
183 LOG(ERROR) << "Signed blobs did not match."; | |
184 return false; | |
185 } | |
186 | |
187 return true; | |
188 } | |
189 | |
190 bool NetworkingPrivateCrypto::EncryptByteString(const std::string& public_key, | |
191 const std::string& data, | |
192 std::string* encrypted_output) { | |
193 crypto::EnsureNSSInit(); | |
194 | |
195 SECItem pub_key_der_item; | |
196 pub_key_der_item.data = | |
197 reinterpret_cast<unsigned char*>(const_cast<char*>(public_key.c_str())); | |
198 pub_key_der_item.len = public_key.size(); | |
199 | |
200 enc_public_key_ = SECKEY_ImportDERPublicKey(&pub_key_der_item, CKK_RSA); | |
201 if (!enc_public_key_) { | |
202 LOG(ERROR) << "Failed to parse public key."; | |
203 return false; | |
204 } | |
205 | |
206 size_t encrypted_length = SECKEY_SignatureLen(enc_public_key_); | |
207 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
208 | |
209 LOG(INFO) << "Encrypting data with public key."; | |
210 SECStatus encrypted = PK11_PubEncryptPKCS1( | |
mef
2013/08/28 18:05:59
Is it ok to add this to third_party/nss/nss/export
Ryan Sleevi
2013/08/28 18:31:26
Yes, this is part of nss.def.
| |
211 enc_public_key_, | |
212 rsa_output.get(), | |
213 // The API helpfully tells us that this operation will treat this buffer | |
214 // as read only, but fails to mark the parameter const. | |
215 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())), | |
216 data.length(), | |
217 NULL); | |
218 if (encrypted != SECSuccess) { | |
219 LOG(ERROR) << "Error during encryption."; | |
220 return false; | |
221 } | |
222 | |
223 encrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
224 encrypted_length); | |
225 | |
226 return true; | |
227 } | |
228 | |
229 bool NetworkingPrivateCrypto::DecryptByteString( | |
230 const std::string& private_key_pem, | |
231 const std::string& encrypted_data, | |
232 std::string* decrypted_output) { | |
233 crypto::EnsureNSSInit(); | |
234 std::string private_key_der; | |
235 if (!GetDERFromPEM(private_key_pem, "RSA PRIVATE KEY", &private_key_der)) { | |
236 LOG(ERROR) << "Failed to parse private key PEM."; | |
237 return false; | |
238 } | |
239 std::vector<uint8> private_key_data(private_key_der.begin(), | |
240 private_key_der.end()); | |
241 | |
242 LOG(ERROR) << "Private Key Len:" << private_key_data.size(); | |
243 | |
244 // TODO(mef): Figure out correct way to import DER private key. | |
245 scoped_ptr<crypto::RSAPrivateKey> private_key( | |
246 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(private_key_data)); | |
mef
2013/08/28 18:05:59
This returns a failure from ASN.1 parser, any sugg
| |
247 if (!private_key) { | |
248 LOG(ERROR) << "Failed to parse private key DER."; | |
249 return false; | |
250 } | |
251 | |
252 size_t encrypted_length = SECKEY_SignatureLen(private_key->public_key()); | |
253 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
254 | |
255 unsigned output_length = 0; | |
Ryan Sleevi
2013/08/28 19:02:37
Blergh, poor API spec if we're calling it 'unsigne
mef
2013/08/28 21:28:58
Done.
| |
256 | |
257 SECStatus decrypted = PK11_PrivDecryptPKCS1( | |
258 private_key->key(), | |
259 rsa_output.get(), | |
260 &output_length, | |
261 encrypted_length, | |
262 | |
263 // The API helpfully tells us that this operation will treat this buffer | |
264 // as read only, but fails to mark the parameter const. | |
265 reinterpret_cast<unsigned char*>( | |
266 const_cast<char*>(encrypted_data.data())), | |
267 encrypted_data.length()); | |
268 | |
269 if (decrypted != SECSuccess) { | |
270 LOG(ERROR) << "Error during encryption."; | |
271 return false; | |
272 } | |
273 | |
274 decrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
275 output_length); | |
276 | |
277 return true; | |
278 } | |
OLD | NEW |