blob: b34ce1dbbd3ece729e411ee6da12d5fb339d7c19 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <algorithm>
12#include <string>
13#include <vector>
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif // HAVE_CONFIG_H
18
19#if HAVE_NSS_SSL_H
20
21#include "webrtc/base/nssidentity.h"
22
23#include "cert.h"
24#include "cryptohi.h"
25#include "keyhi.h"
26#include "nss.h"
27#include "pk11pub.h"
28#include "sechash.h"
29
30#include "webrtc/base/logging.h"
31#include "webrtc/base/helpers.h"
32#include "webrtc/base/nssstreamadapter.h"
33#include "webrtc/base/safe_conversions.h"
34
35namespace rtc {
36
37// Certificate validity lifetime in seconds.
38static const int CERTIFICATE_LIFETIME = 60*60*24*30; // 30 days, arbitrarily
39// Certificate validity window in seconds.
40// This is to compensate for slightly incorrect system clocks.
41static const int CERTIFICATE_WINDOW = -60*60*24;
42
43NSSKeyPair::~NSSKeyPair() {
44 if (privkey_)
45 SECKEY_DestroyPrivateKey(privkey_);
46 if (pubkey_)
47 SECKEY_DestroyPublicKey(pubkey_);
48}
49
50NSSKeyPair *NSSKeyPair::Generate() {
51 SECKEYPrivateKey *privkey = NULL;
52 SECKEYPublicKey *pubkey = NULL;
53 PK11RSAGenParams rsaparams;
54 rsaparams.keySizeInBits = 1024;
55 rsaparams.pe = 0x010001; // 65537 -- a common RSA public exponent.
56
57 privkey = PK11_GenerateKeyPair(NSSContext::GetSlot(),
58 CKM_RSA_PKCS_KEY_PAIR_GEN,
59 &rsaparams, &pubkey, PR_FALSE /*permanent*/,
60 PR_FALSE /*sensitive*/, NULL);
61 if (!privkey) {
62 LOG(LS_ERROR) << "Couldn't generate key pair";
63 return NULL;
64 }
65
66 return new NSSKeyPair(privkey, pubkey);
67}
68
69// Just make a copy.
70NSSKeyPair *NSSKeyPair::GetReference() {
71 SECKEYPrivateKey *privkey = SECKEY_CopyPrivateKey(privkey_);
72 if (!privkey)
73 return NULL;
74
75 SECKEYPublicKey *pubkey = SECKEY_CopyPublicKey(pubkey_);
76 if (!pubkey) {
77 SECKEY_DestroyPrivateKey(privkey);
78 return NULL;
79 }
80
81 return new NSSKeyPair(privkey, pubkey);
82}
83
84NSSCertificate::NSSCertificate(CERTCertificate* cert)
85 : certificate_(CERT_DupCertificate(cert)) {
86 ASSERT(certificate_ != NULL);
87}
88
89static void DeleteCert(SSLCertificate* cert) {
90 delete cert;
91}
92
93NSSCertificate::NSSCertificate(CERTCertList* cert_list) {
94 // Copy the first cert into certificate_.
95 CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
96 certificate_ = CERT_DupCertificate(node->cert);
97
98 // Put any remaining certificates into the chain.
99 node = CERT_LIST_NEXT(node);
100 std::vector<SSLCertificate*> certs;
101 for (; !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
102 certs.push_back(new NSSCertificate(node->cert));
103 }
104
105 if (!certs.empty())
106 chain_.reset(new SSLCertChain(certs));
107
108 // The SSLCertChain constructor copies its input, so now we have to delete
109 // the originals.
110 std::for_each(certs.begin(), certs.end(), DeleteCert);
111}
112
113NSSCertificate::NSSCertificate(CERTCertificate* cert, SSLCertChain* chain)
114 : certificate_(CERT_DupCertificate(cert)) {
115 ASSERT(certificate_ != NULL);
116 if (chain)
117 chain_.reset(chain->Copy());
118}
119
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000120NSSCertificate::~NSSCertificate() {
121 if (certificate_)
122 CERT_DestroyCertificate(certificate_);
123}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000124
125NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) {
126 std::string der;
127 if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der))
128 return NULL;
129
130 SECItem der_cert;
131 der_cert.data = reinterpret_cast<unsigned char *>(const_cast<char *>(
132 der.data()));
133 der_cert.len = checked_cast<unsigned int>(der.size());
134 CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
135 &der_cert, NULL, PR_FALSE, PR_TRUE);
136
137 if (!cert)
138 return NULL;
139
140 NSSCertificate* ret = new NSSCertificate(cert);
141 CERT_DestroyCertificate(cert);
142 return ret;
143}
144
145NSSCertificate *NSSCertificate::GetReference() const {
146 return new NSSCertificate(certificate_, chain_.get());
147}
148
149std::string NSSCertificate::ToPEMString() const {
150 return SSLIdentity::DerToPem(kPemTypeCertificate,
151 certificate_->derCert.data,
152 certificate_->derCert.len);
153}
154
155void NSSCertificate::ToDER(Buffer* der_buffer) const {
156 der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len);
157}
158
159static bool Certifies(CERTCertificate* parent, CERTCertificate* child) {
160 // TODO(bemasc): Identify stricter validation checks to use here. In the
161 // context of some future identity standard, it might make sense to check
162 // the certificates' roles, expiration dates, self-signatures (if
163 // self-signed), certificate transparency logging, or many other attributes.
164 // NOTE: Future changes to this validation may reject some previously allowed
165 // certificate chains. Users should be advised not to deploy chained
166 // certificates except in controlled environments until the validity
167 // requirements are finalized.
168
169 // Check that the parent's name is the same as the child's claimed issuer.
170 SECComparison name_status =
171 CERT_CompareName(&child->issuer, &parent->subject);
172 if (name_status != SECEqual)
173 return false;
174
175 // Extract the parent's public key, or fail if the key could not be read
176 // (e.g. certificate is corrupted).
177 SECKEYPublicKey* parent_key = CERT_ExtractPublicKey(parent);
178 if (!parent_key)
179 return false;
180
181 // Check that the parent's privkey was actually used to generate the child's
182 // signature.
183 SECStatus verified = CERT_VerifySignedDataWithPublicKey(
184 &child->signatureWrap, parent_key, NULL);
185 SECKEY_DestroyPublicKey(parent_key);
186 return verified == SECSuccess;
187}
188
189bool NSSCertificate::IsValidChain(const CERTCertList* cert_list) {
190 CERTCertListNode* child = CERT_LIST_HEAD(cert_list);
191 for (CERTCertListNode* parent = CERT_LIST_NEXT(child);
192 !CERT_LIST_END(parent, cert_list);
193 child = parent, parent = CERT_LIST_NEXT(parent)) {
194 if (!Certifies(parent->cert, child->cert))
195 return false;
196 }
197 return true;
198}
199
200bool NSSCertificate::GetDigestLength(const std::string& algorithm,
201 size_t* length) {
202 const SECHashObject *ho;
203
204 if (!GetDigestObject(algorithm, &ho))
205 return false;
206
207 *length = ho->length;
208
209 return true;
210}
211
212bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const {
213 // The function sec_DecodeSigAlg in NSS provides this mapping functionality.
214 // Unfortunately it is private, so the functionality must be duplicated here.
215 // See https://bugzilla.mozilla.org/show_bug.cgi?id=925165 .
216 SECOidTag sig_alg = SECOID_GetAlgorithmTag(&certificate_->signature);
217 switch (sig_alg) {
218 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
219 *algorithm = DIGEST_MD5;
220 break;
221 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
222 case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
223 case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
224 case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
225 case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
226 case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
227 case SEC_OID_MISSI_DSS:
228 case SEC_OID_MISSI_KEA_DSS:
229 case SEC_OID_MISSI_KEA_DSS_OLD:
230 case SEC_OID_MISSI_DSS_OLD:
231 *algorithm = DIGEST_SHA_1;
232 break;
233 case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
234 case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
235 case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
236 *algorithm = DIGEST_SHA_224;
237 break;
238 case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
239 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
240 case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST:
241 *algorithm = DIGEST_SHA_256;
242 break;
243 case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
244 case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
245 *algorithm = DIGEST_SHA_384;
246 break;
247 case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
248 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
249 *algorithm = DIGEST_SHA_512;
250 break;
251 default:
252 // Unknown algorithm. There are several unhandled options that are less
253 // common and more complex.
254 algorithm->clear();
255 return false;
256 }
257 return true;
258}
259
260bool NSSCertificate::ComputeDigest(const std::string& algorithm,
261 unsigned char* digest,
262 size_t size,
263 size_t* length) const {
264 const SECHashObject *ho;
265
266 if (!GetDigestObject(algorithm, &ho))
267 return false;
268
269 if (size < ho->length) // Sanity check for fit
270 return false;
271
272 SECStatus rv = HASH_HashBuf(ho->type, digest,
273 certificate_->derCert.data,
274 certificate_->derCert.len);
275 if (rv != SECSuccess)
276 return false;
277
278 *length = ho->length;
279
280 return true;
281}
282
283bool NSSCertificate::GetChain(SSLCertChain** chain) const {
284 if (!chain_)
285 return false;
286
287 *chain = chain_->Copy();
288 return true;
289}
290
291bool NSSCertificate::Equals(const NSSCertificate *tocompare) const {
292 if (!certificate_->derCert.len)
293 return false;
294 if (!tocompare->certificate_->derCert.len)
295 return false;
296
297 if (certificate_->derCert.len != tocompare->certificate_->derCert.len)
298 return false;
299
300 return memcmp(certificate_->derCert.data,
301 tocompare->certificate_->derCert.data,
302 certificate_->derCert.len) == 0;
303}
304
305
306bool NSSCertificate::GetDigestObject(const std::string &algorithm,
307 const SECHashObject **hop) {
308 const SECHashObject *ho;
309 HASH_HashType hash_type;
310
311 if (algorithm == DIGEST_SHA_1) {
312 hash_type = HASH_AlgSHA1;
313 // HASH_AlgSHA224 is not supported in the chromium linux build system.
314#if 0
315 } else if (algorithm == DIGEST_SHA_224) {
316 hash_type = HASH_AlgSHA224;
317#endif
318 } else if (algorithm == DIGEST_SHA_256) {
319 hash_type = HASH_AlgSHA256;
320 } else if (algorithm == DIGEST_SHA_384) {
321 hash_type = HASH_AlgSHA384;
322 } else if (algorithm == DIGEST_SHA_512) {
323 hash_type = HASH_AlgSHA512;
324 } else {
325 return false;
326 }
327
328 ho = HASH_GetHashObject(hash_type);
329
330 ASSERT(ho->length >= 20); // Can't happen
331 *hop = ho;
332
333 return true;
334}
335
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000336NSSIdentity::NSSIdentity(NSSKeyPair* keypair, NSSCertificate* cert)
337 : keypair_(keypair), certificate_(cert) {
338}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000339
340NSSIdentity* NSSIdentity::GenerateInternal(const SSLIdentityParams& params) {
341 std::string subject_name_string = "CN=" + params.common_name;
342 CERTName *subject_name = CERT_AsciiToName(
343 const_cast<char *>(subject_name_string.c_str()));
344 NSSIdentity *identity = NULL;
345 CERTSubjectPublicKeyInfo *spki = NULL;
346 CERTCertificateRequest *certreq = NULL;
347 CERTValidity *validity = NULL;
348 CERTCertificate *certificate = NULL;
349 NSSKeyPair *keypair = NSSKeyPair::Generate();
350 SECItem inner_der;
351 SECStatus rv;
352 PLArenaPool* arena;
353 SECItem signed_cert;
354 PRTime now = PR_Now();
355 PRTime not_before =
356 now + static_cast<PRTime>(params.not_before) * PR_USEC_PER_SEC;
357 PRTime not_after =
358 now + static_cast<PRTime>(params.not_after) * PR_USEC_PER_SEC;
359
360 inner_der.len = 0;
361 inner_der.data = NULL;
362
363 if (!keypair) {
364 LOG(LS_ERROR) << "Couldn't generate key pair";
365 goto fail;
366 }
367
368 if (!subject_name) {
369 LOG(LS_ERROR) << "Couldn't convert subject name " << subject_name;
370 goto fail;
371 }
372
373 spki = SECKEY_CreateSubjectPublicKeyInfo(keypair->pubkey());
374 if (!spki) {
375 LOG(LS_ERROR) << "Couldn't create SPKI";
376 goto fail;
377 }
378
379 certreq = CERT_CreateCertificateRequest(subject_name, spki, NULL);
380 if (!certreq) {
381 LOG(LS_ERROR) << "Couldn't create certificate signing request";
382 goto fail;
383 }
384
385 validity = CERT_CreateValidity(not_before, not_after);
386 if (!validity) {
387 LOG(LS_ERROR) << "Couldn't create validity";
388 goto fail;
389 }
390
391 unsigned long serial;
392 // Note: This serial in principle could collide, but it's unlikely
393 rv = PK11_GenerateRandom(reinterpret_cast<unsigned char *>(&serial),
394 sizeof(serial));
395 if (rv != SECSuccess) {
396 LOG(LS_ERROR) << "Couldn't generate random serial";
397 goto fail;
398 }
399
400 certificate = CERT_CreateCertificate(serial, subject_name, validity, certreq);
401 if (!certificate) {
402 LOG(LS_ERROR) << "Couldn't create certificate";
403 goto fail;
404 }
405
406 arena = certificate->arena;
407
408 rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
409 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
410 if (rv != SECSuccess)
411 goto fail;
412
413 // Set version to X509v3.
414 *(certificate->version.data) = 2;
415 certificate->version.len = 1;
416
417 if (!SEC_ASN1EncodeItem(arena, &inner_der, certificate,
418 SEC_ASN1_GET(CERT_CertificateTemplate)))
419 goto fail;
420
421 rv = SEC_DerSignData(arena, &signed_cert, inner_der.data, inner_der.len,
422 keypair->privkey(),
423 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION);
424 if (rv != SECSuccess) {
425 LOG(LS_ERROR) << "Couldn't sign certificate";
426 goto fail;
427 }
428 certificate->derCert = signed_cert;
429
430 identity = new NSSIdentity(keypair, new NSSCertificate(certificate));
431
432 goto done;
433
434 fail:
435 delete keypair;
436
437 done:
438 if (certificate) CERT_DestroyCertificate(certificate);
439 if (subject_name) CERT_DestroyName(subject_name);
440 if (spki) SECKEY_DestroySubjectPublicKeyInfo(spki);
441 if (certreq) CERT_DestroyCertificateRequest(certreq);
442 if (validity) CERT_DestroyValidity(validity);
443 return identity;
444}
445
446NSSIdentity* NSSIdentity::Generate(const std::string &common_name) {
447 SSLIdentityParams params;
448 params.common_name = common_name;
449 params.not_before = CERTIFICATE_WINDOW;
450 params.not_after = CERTIFICATE_LIFETIME;
451 return GenerateInternal(params);
452}
453
454NSSIdentity* NSSIdentity::GenerateForTest(const SSLIdentityParams& params) {
455 return GenerateInternal(params);
456}
457
458SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key,
459 const std::string& certificate) {
460 std::string private_key_der;
461 if (!SSLIdentity::PemToDer(
462 kPemTypeRsaPrivateKey, private_key, &private_key_der))
463 return NULL;
464
465 SECItem private_key_item;
466 private_key_item.data = reinterpret_cast<unsigned char *>(
467 const_cast<char *>(private_key_der.c_str()));
468 private_key_item.len = checked_cast<unsigned int>(private_key_der.size());
469
470 const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT |
471 KU_DIGITAL_SIGNATURE;
472
473 SECKEYPrivateKey* privkey = NULL;
474 SECStatus rv =
475 PK11_ImportDERPrivateKeyInfoAndReturnKey(NSSContext::GetSlot(),
476 &private_key_item,
477 NULL, NULL, PR_FALSE, PR_FALSE,
478 key_usage, &privkey, NULL);
479 if (rv != SECSuccess) {
480 LOG(LS_ERROR) << "Couldn't import private key";
481 return NULL;
482 }
483
484 SECKEYPublicKey *pubkey = SECKEY_ConvertToPublicKey(privkey);
485 if (rv != SECSuccess) {
486 SECKEY_DestroyPrivateKey(privkey);
487 LOG(LS_ERROR) << "Couldn't convert private key to public key";
488 return NULL;
489 }
490
491 // Assign to a scoped_ptr so we don't leak on error.
492 scoped_ptr<NSSKeyPair> keypair(new NSSKeyPair(privkey, pubkey));
493
494 scoped_ptr<NSSCertificate> cert(NSSCertificate::FromPEMString(certificate));
495 if (!cert) {
496 LOG(LS_ERROR) << "Couldn't parse certificate";
497 return NULL;
498 }
499
500 // TODO(ekr@rtfm.com): Check the public key against the certificate.
501
502 return new NSSIdentity(keypair.release(), cert.release());
503}
504
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000505NSSIdentity::~NSSIdentity() {
506 LOG(LS_INFO) << "Destroying NSS identity";
507}
508
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000509NSSIdentity *NSSIdentity::GetReference() const {
510 NSSKeyPair *keypair = keypair_->GetReference();
511 if (!keypair)
512 return NULL;
513
514 NSSCertificate *certificate = certificate_->GetReference();
515 if (!certificate) {
516 delete keypair;
517 return NULL;
518 }
519
520 return new NSSIdentity(keypair, certificate);
521}
522
523
524NSSCertificate &NSSIdentity::certificate() const {
525 return *certificate_;
526}
527
528
529} // rtc namespace
530
531#endif // HAVE_NSS_SSL_H
532