OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/base/x509_util.h" | |
6 #include "net/base/x509_util_nss.h" | |
wtc
2011/10/17 19:09:27
If we follow the Style Guide strictly, only "net/b
| |
7 | |
8 #include "base/debug/leak_annotations.h" //XXX | |
wtc
2011/10/17 19:09:27
Can you remove the "//XXX" comment?
mattm
2011/10/17 22:54:19
Done.
| |
9 #include "base/logging.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/memory/singleton.h" | |
12 #include "crypto/nss_util.h" | |
13 #include "crypto/nss_util_internal.h" | |
14 #include "crypto/rsa_private_key.h" | |
15 #include "crypto/scoped_nss_types.h" | |
16 | |
17 #include <cert.h> | |
18 #include <cryptohi.h> | |
19 #include <pk11pub.h> | |
20 #include <prerror.h> | |
21 #include <secmod.h> | |
22 #include <secport.h> | |
wtc
2011/10/17 19:09:27
These NSS headers are considered system headers (t
mattm
2011/10/17 22:54:19
Done.
| |
23 | |
24 namespace { | |
25 | |
26 class ObCertOIDWrapper { | |
27 public: | |
28 static ObCertOIDWrapper* GetInstance() { | |
29 // Instantiated as a leaky singleton to allow the singleton to be | |
30 // constructed on a worker thead that is not joined when a process | |
31 // shuts down. | |
32 return Singleton<ObCertOIDWrapper, | |
33 LeakySingletonTraits<ObCertOIDWrapper> >::get(); | |
34 } | |
35 | |
36 SECOidTag ob_cert_oid_tag() const { | |
37 return ob_cert_oid_tag_; | |
38 } | |
39 | |
40 private: | |
41 friend struct DefaultSingletonTraits<ObCertOIDWrapper>; | |
42 | |
43 ObCertOIDWrapper(); | |
44 | |
45 SECOidTag ob_cert_oid_tag_; | |
46 | |
47 DISALLOW_COPY_AND_ASSIGN(ObCertOIDWrapper); | |
48 }; | |
49 | |
50 ObCertOIDWrapper::ObCertOIDWrapper(): ob_cert_oid_tag_(SEC_OID_UNKNOWN) { | |
51 // 1.3.6.1.4.1.11129.2.1.6 | |
52 // (iso.org.dod.internet.private.enterprises.google.googleSecurity. | |
53 // certificateExtensions.originBoundCertificate) | |
54 static const uint8 kObCertOID[] = { | |
55 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x06 | |
56 }; | |
57 SECOidData oid_data; | |
58 memset(&oid_data, 0, sizeof(oid_data)); | |
59 oid_data.oid.data = const_cast<uint8*>(kObCertOID); | |
60 oid_data.oid.len = sizeof(kObCertOID); | |
61 oid_data.offset = SEC_OID_UNKNOWN; | |
62 oid_data.desc = "Origin Bound Certificate"; | |
63 oid_data.mechanism = CKM_INVALID_MECHANISM; | |
64 oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION; | |
65 ob_cert_oid_tag_ = SECOID_AddEntry(&oid_data); | |
66 if (ob_cert_oid_tag_ == SEC_OID_UNKNOWN) | |
67 LOG(ERROR) << "OB_CERT OID tag creation failed"; | |
68 } | |
69 | |
70 // Creates a Certificate object that may be passed to the SignCertificate | |
71 // method to generate an X509 certificate. | |
72 // Returns NULL if an error is encountered in the certificate creation | |
73 // process. | |
74 // Caller responsible for freeing returned certificate object. | |
75 CERTCertificate* CreateCertificate( | |
76 SECKEYPublicKeyStr* public_key, | |
wtc
2011/10/17 19:09:27
Search for "SECKEYPublicKeyStr" and "SECKEYPrivate
mattm
2011/10/17 22:54:19
Done.
| |
77 const std::string& subject, | |
78 uint32 serial_number, | |
79 base::TimeDelta valid_duration) { | |
80 // Create info about public key. | |
81 CERTSubjectPublicKeyInfo* spki = | |
82 SECKEY_CreateSubjectPublicKeyInfo(public_key); | |
83 if (!spki) | |
84 return NULL; | |
85 | |
86 // Create the certificate request. | |
87 CERTName* subject_name = | |
88 CERT_AsciiToName(const_cast<char*>(subject.c_str())); | |
89 CERTCertificateRequest* cert_request = | |
90 CERT_CreateCertificateRequest(subject_name, spki, NULL); | |
91 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
92 | |
93 if (!cert_request) { | |
94 PRErrorCode prerr = PR_GetError(); | |
95 LOG(ERROR) << "Failed to create certificate request: " << prerr; | |
96 CERT_DestroyName(subject_name); | |
97 return NULL; | |
98 } | |
99 | |
100 PRTime now = PR_Now(); | |
101 PRTime not_after = now + valid_duration.InMicroseconds(); | |
102 | |
103 // Note that the time is now in micro-second unit. | |
104 CERTValidity* validity = CERT_CreateValidity(now, not_after); | |
105 CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name, | |
106 validity, cert_request); | |
107 if (!cert) { | |
108 PRErrorCode prerr = PR_GetError(); | |
109 LOG(ERROR) << "Failed to create certificate: " << prerr; | |
110 } | |
111 | |
112 // Cleanup for resources used to generate the cert. | |
113 CERT_DestroyName(subject_name); | |
114 CERT_DestroyValidity(validity); | |
115 CERT_DestroyCertificateRequest(cert_request); | |
116 | |
117 return cert; | |
118 } | |
119 | |
120 // Signs a certificate object, with |key| generating a new X509Certificate | |
121 // and destroying the passed certificate object (even when NULL is returned). | |
122 // The logic of this method references SignCert() in NSS utility certutil: | |
123 // http://mxr.mozilla.org/security/ident?i=SignCert. | |
124 // Returns true on success or false if an error is encountered in the | |
125 // certificate signing process. | |
126 bool SignCertificate( | |
127 CERTCertificate* cert, | |
128 SECKEYPrivateKeyStr* key) { | |
129 // |arena| is used to encode the cert. | |
130 PLArenaPool* arena = cert->arena; | |
131 SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->keyType, | |
132 SEC_OID_SHA1); | |
133 if (algo_id == SEC_OID_UNKNOWN) | |
134 return false; | |
135 | |
136 SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0); | |
137 if (rv != SECSuccess) | |
138 return false; | |
139 | |
140 // Generate a cert of version 3. | |
141 *(cert->version.data) = 2; | |
142 cert->version.len = 1; | |
143 | |
144 SECItem der; | |
145 der.len = 0; | |
146 der.data = NULL; | |
147 | |
148 // Use ASN1 DER to encode the cert. | |
149 void* encode_result = SEC_ASN1EncodeItem( | |
150 arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate)); | |
151 if (!encode_result) | |
152 return false; | |
153 | |
154 // Allocate space to contain the signed cert. | |
155 SECItem* result = SECITEM_AllocItem(arena, NULL, 0); | |
156 if (!result) | |
157 return false; | |
158 | |
159 // Sign the ASN1 encoded cert and save it to |result|. | |
160 rv = SEC_DerSignData(arena, result, der.data, der.len, key, algo_id); | |
161 if (rv != SECSuccess) | |
162 return false; | |
163 | |
164 // Save the signed result to the cert. | |
165 cert->derCert = *result; | |
166 | |
167 return true; | |
168 } | |
169 | |
170 } // namespace | |
171 | |
172 namespace net { | |
173 | |
174 namespace x509_util { | |
175 | |
176 CERTCertificate* CreateSelfSignedCert( | |
177 SECKEYPublicKeyStr* public_key, | |
178 SECKEYPrivateKeyStr* key, | |
179 const std::string& subject, | |
180 uint32 serial_number, | |
181 base::TimeDelta valid_duration) { | |
182 CERTCertificate* cert = CreateCertificate(public_key, | |
183 subject, | |
184 serial_number, | |
185 valid_duration); | |
186 if (!cert) | |
187 return NULL; | |
188 | |
189 if (SignCertificate(cert, key)) | |
190 return cert; | |
191 | |
192 CERT_DestroyCertificate(cert); | |
193 return NULL; | |
wtc
2011/10/17 19:09:27
Nit: let's rewrite lines 189-193 as follows:
if
mattm
2011/10/17 22:54:19
Done.
| |
194 } | |
195 | |
196 bool CreateOriginBoundCert( | |
197 crypto::RSAPrivateKey* key, | |
198 const std::string& origin, | |
199 uint32 serial_number, | |
200 base::TimeDelta valid_duration, | |
201 std::string* der_cert) { | |
202 DCHECK(key); | |
203 | |
204 SECKEYPublicKeyStr* public_key; | |
205 SECKEYPrivateKeyStr* private_key; | |
206 #if defined(USE_NSS) | |
207 public_key = key->public_key(); | |
208 private_key = key->key(); | |
209 #else | |
210 crypto::ScopedSECKEYPublicKey public_key_holder; | |
211 crypto::ScopedSECKEYPrivateKey private_key_holder; | |
wtc
2011/10/17 19:09:27
Nit: our convention is to name this kind of Scoped
mattm
2011/10/17 22:54:19
Done.
| |
212 { | |
213 // Based on the NSS RSAPrivateKey::CreateFromPrivateKeyInfoWithParams. | |
214 // This method currently leaks some memory. | |
215 // See http://crbug.com/34742. | |
216 ANNOTATE_SCOPED_MEMORY_LEAK; | |
217 crypto::EnsureNSSInit(); | |
218 | |
219 std::vector<uint8> key_data; | |
220 key->ExportPrivateKey(&key_data); | |
221 | |
222 crypto::ScopedPK11Slot slot(crypto::GetPrivateNSSKeySlot()); | |
223 if (!slot.get()) | |
224 return NULL; | |
225 | |
226 SECItem der_private_key_info; | |
227 der_private_key_info.data = const_cast<unsigned char*>(&key_data.front()); | |
wtc
2011/10/17 19:09:27
Nit: &key_data.front() => &key_data[0]
mattm
2011/10/17 22:54:19
Done.
| |
228 der_private_key_info.len = key_data.size(); | |
229 // Allow the private key to be used for key unwrapping, data decryption, | |
230 // and signature generation. | |
231 const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | | |
232 KU_DIGITAL_SIGNATURE; | |
wtc
2011/10/17 19:09:27
Nit: align this with KU_KEY_ENCIPHERMENT on the pr
mattm
2011/10/17 22:54:19
Done.
| |
233 SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( | |
234 slot.get(), &der_private_key_info, NULL, NULL, PR_FALSE, PR_FALSE, | |
235 key_usage, &private_key, NULL); | |
236 private_key_holder.reset(private_key); | |
237 if (rv != SECSuccess) { | |
238 NOTREACHED(); | |
239 return NULL; | |
240 } | |
241 | |
242 public_key = SECKEY_ConvertToPublicKey(private_key); | |
243 if (!public_key) { | |
244 NOTREACHED(); | |
245 return NULL; | |
246 } | |
247 public_key_holder.reset(public_key); | |
248 } | |
249 #endif | |
250 | |
251 CERTCertificate* cert = CreateCertificate(public_key, | |
252 "CN=anonymous.invalid", | |
253 serial_number, | |
254 valid_duration); | |
255 | |
256 if (!cert) | |
257 return false; | |
258 | |
259 // Create opaque handle used to add extensions later. | |
260 void* cert_handle; | |
261 if ((cert_handle = CERT_StartCertExtensions(cert)) == NULL) { | |
262 LOG(ERROR) << "Unable to get opaque handle for adding extensions"; | |
263 CERT_DestroyCertificate(cert); | |
264 return false; | |
265 } | |
266 | |
267 // Create SECItem for IA5String encoding. | |
268 SECItem origin_string_item = { | |
269 siAsciiString, | |
270 (unsigned char*)origin.data(), | |
271 origin.size() | |
272 }; | |
273 | |
274 // IA5Encode and arena allocate SECItem | |
275 SECItem* asn1_origin_string = SEC_ASN1EncodeItem( | |
276 cert->arena, NULL, &origin_string_item, | |
277 SEC_ASN1_GET(SEC_IA5StringTemplate)); | |
278 if (asn1_origin_string == NULL) { | |
279 LOG(ERROR) << "Unable to get ASN1 encoding for origin in ob_cert extension"; | |
280 CERT_DestroyCertificate(cert); | |
281 return false; | |
282 } | |
283 | |
284 // Add the extension to the opaque handle | |
285 if (CERT_AddExtension(cert_handle, | |
286 ObCertOIDWrapper::GetInstance()->ob_cert_oid_tag(), | |
287 asn1_origin_string, | |
288 PR_TRUE, PR_TRUE) != SECSuccess){ | |
289 LOG(ERROR) << "Unable to add origin bound cert extension to opaque handle"; | |
290 CERT_DestroyCertificate(cert); | |
291 return false; | |
292 } | |
293 | |
294 // Copy extension into x509 cert | |
295 if (CERT_FinishExtensions(cert_handle) != SECSuccess){ | |
296 LOG(ERROR) << "Unable to copy extension to X509 cert"; | |
297 CERT_DestroyCertificate(cert); | |
298 return false; | |
299 } | |
300 | |
301 if (SignCertificate(cert, private_key)) { | |
302 DCHECK(cert->derCert.len); | |
303 // XXX copied from X509Certificate::GetDEREncoded | |
304 der_cert->clear(); | |
305 der_cert->append(reinterpret_cast<char*>(cert->derCert.data), | |
306 cert->derCert.len); | |
307 CERT_DestroyCertificate(cert); | |
308 return true; | |
309 } | |
310 | |
311 CERT_DestroyCertificate(cert); | |
312 return false; | |
313 } | |
314 | |
315 } // namespace x509_util | |
316 | |
317 } // namespace net | |
OLD | NEW |