1/* 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 120 121NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) { 122 std::string der; 123 if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) 124 return NULL; 125 126 SECItem der_cert; 127 der_cert.data = reinterpret_cast<unsigned char *>(const_cast<char *>( 128 der.data())); 129 der_cert.len = checked_cast<unsigned int>(der.size()); 130 CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), 131 &der_cert, NULL, PR_FALSE, PR_TRUE); 132 133 if (!cert) 134 return NULL; 135 136 NSSCertificate* ret = new NSSCertificate(cert); 137 CERT_DestroyCertificate(cert); 138 return ret; 139} 140 141NSSCertificate *NSSCertificate::GetReference() const { 142 return new NSSCertificate(certificate_, chain_.get()); 143} 144 145std::string NSSCertificate::ToPEMString() const { 146 return SSLIdentity::DerToPem(kPemTypeCertificate, 147 certificate_->derCert.data, 148 certificate_->derCert.len); 149} 150 151void NSSCertificate::ToDER(Buffer* der_buffer) const { 152 der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len); 153} 154 155static bool Certifies(CERTCertificate* parent, CERTCertificate* child) { 156 // TODO(bemasc): Identify stricter validation checks to use here. In the 157 // context of some future identity standard, it might make sense to check 158 // the certificates' roles, expiration dates, self-signatures (if 159 // self-signed), certificate transparency logging, or many other attributes. 160 // NOTE: Future changes to this validation may reject some previously allowed 161 // certificate chains. Users should be advised not to deploy chained 162 // certificates except in controlled environments until the validity 163 // requirements are finalized. 164 165 // Check that the parent's name is the same as the child's claimed issuer. 166 SECComparison name_status = 167 CERT_CompareName(&child->issuer, &parent->subject); 168 if (name_status != SECEqual) 169 return false; 170 171 // Extract the parent's public key, or fail if the key could not be read 172 // (e.g. certificate is corrupted). 173 SECKEYPublicKey* parent_key = CERT_ExtractPublicKey(parent); 174 if (!parent_key) 175 return false; 176 177 // Check that the parent's privkey was actually used to generate the child's 178 // signature. 179 SECStatus verified = CERT_VerifySignedDataWithPublicKey( 180 &child->signatureWrap, parent_key, NULL); 181 SECKEY_DestroyPublicKey(parent_key); 182 return verified == SECSuccess; 183} 184 185bool NSSCertificate::IsValidChain(const CERTCertList* cert_list) { 186 CERTCertListNode* child = CERT_LIST_HEAD(cert_list); 187 for (CERTCertListNode* parent = CERT_LIST_NEXT(child); 188 !CERT_LIST_END(parent, cert_list); 189 child = parent, parent = CERT_LIST_NEXT(parent)) { 190 if (!Certifies(parent->cert, child->cert)) 191 return false; 192 } 193 return true; 194} 195 196bool NSSCertificate::GetDigestLength(const std::string& algorithm, 197 size_t* length) { 198 const SECHashObject *ho; 199 200 if (!GetDigestObject(algorithm, &ho)) 201 return false; 202 203 *length = ho->length; 204 205 return true; 206} 207 208bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const { 209 // The function sec_DecodeSigAlg in NSS provides this mapping functionality. 210 // Unfortunately it is private, so the functionality must be duplicated here. 211 // See https://bugzilla.mozilla.org/show_bug.cgi?id=925165 . 212 SECOidTag sig_alg = SECOID_GetAlgorithmTag(&certificate_->signature); 213 switch (sig_alg) { 214 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: 215 *algorithm = DIGEST_MD5; 216 break; 217 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: 218 case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: 219 case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: 220 case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: 221 case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: 222 case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: 223 case SEC_OID_MISSI_DSS: 224 case SEC_OID_MISSI_KEA_DSS: 225 case SEC_OID_MISSI_KEA_DSS_OLD: 226 case SEC_OID_MISSI_DSS_OLD: 227 *algorithm = DIGEST_SHA_1; 228 break; 229 case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: 230 case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: 231 case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: 232 *algorithm = DIGEST_SHA_224; 233 break; 234 case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: 235 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: 236 case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: 237 *algorithm = DIGEST_SHA_256; 238 break; 239 case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: 240 case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: 241 *algorithm = DIGEST_SHA_384; 242 break; 243 case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: 244 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: 245 *algorithm = DIGEST_SHA_512; 246 break; 247 default: 248 // Unknown algorithm. There are several unhandled options that are less 249 // common and more complex. 250 algorithm->clear(); 251 return false; 252 } 253 return true; 254} 255 256bool NSSCertificate::ComputeDigest(const std::string& algorithm, 257 unsigned char* digest, 258 size_t size, 259 size_t* length) const { 260 const SECHashObject *ho; 261 262 if (!GetDigestObject(algorithm, &ho)) 263 return false; 264 265 if (size < ho->length) // Sanity check for fit 266 return false; 267 268 SECStatus rv = HASH_HashBuf(ho->type, digest, 269 certificate_->derCert.data, 270 certificate_->derCert.len); 271 if (rv != SECSuccess) 272 return false; 273 274 *length = ho->length; 275 276 return true; 277} 278 279bool NSSCertificate::GetChain(SSLCertChain** chain) const { 280 if (!chain_) 281 return false; 282 283 *chain = chain_->Copy(); 284 return true; 285} 286 287bool NSSCertificate::Equals(const NSSCertificate *tocompare) const { 288 if (!certificate_->derCert.len) 289 return false; 290 if (!tocompare->certificate_->derCert.len) 291 return false; 292 293 if (certificate_->derCert.len != tocompare->certificate_->derCert.len) 294 return false; 295 296 return memcmp(certificate_->derCert.data, 297 tocompare->certificate_->derCert.data, 298 certificate_->derCert.len) == 0; 299} 300 301 302bool NSSCertificate::GetDigestObject(const std::string &algorithm, 303 const SECHashObject **hop) { 304 const SECHashObject *ho; 305 HASH_HashType hash_type; 306 307 if (algorithm == DIGEST_SHA_1) { 308 hash_type = HASH_AlgSHA1; 309 // HASH_AlgSHA224 is not supported in the chromium linux build system. 310#if 0 311 } else if (algorithm == DIGEST_SHA_224) { 312 hash_type = HASH_AlgSHA224; 313#endif 314 } else if (algorithm == DIGEST_SHA_256) { 315 hash_type = HASH_AlgSHA256; 316 } else if (algorithm == DIGEST_SHA_384) { 317 hash_type = HASH_AlgSHA384; 318 } else if (algorithm == DIGEST_SHA_512) { 319 hash_type = HASH_AlgSHA512; 320 } else { 321 return false; 322 } 323 324 ho = HASH_GetHashObject(hash_type); 325 326 ASSERT(ho->length >= 20); // Can't happen 327 *hop = ho; 328 329 return true; 330} 331 332 333NSSIdentity* NSSIdentity::GenerateInternal(const SSLIdentityParams& params) { 334 std::string subject_name_string = "CN=" + params.common_name; 335 CERTName *subject_name = CERT_AsciiToName( 336 const_cast<char *>(subject_name_string.c_str())); 337 NSSIdentity *identity = NULL; 338 CERTSubjectPublicKeyInfo *spki = NULL; 339 CERTCertificateRequest *certreq = NULL; 340 CERTValidity *validity = NULL; 341 CERTCertificate *certificate = NULL; 342 NSSKeyPair *keypair = NSSKeyPair::Generate(); 343 SECItem inner_der; 344 SECStatus rv; 345 PLArenaPool* arena; 346 SECItem signed_cert; 347 PRTime now = PR_Now(); 348 PRTime not_before = 349 now + static_cast<PRTime>(params.not_before) * PR_USEC_PER_SEC; 350 PRTime not_after = 351 now + static_cast<PRTime>(params.not_after) * PR_USEC_PER_SEC; 352 353 inner_der.len = 0; 354 inner_der.data = NULL; 355 356 if (!keypair) { 357 LOG(LS_ERROR) << "Couldn't generate key pair"; 358 goto fail; 359 } 360 361 if (!subject_name) { 362 LOG(LS_ERROR) << "Couldn't convert subject name " << subject_name; 363 goto fail; 364 } 365 366 spki = SECKEY_CreateSubjectPublicKeyInfo(keypair->pubkey()); 367 if (!spki) { 368 LOG(LS_ERROR) << "Couldn't create SPKI"; 369 goto fail; 370 } 371 372 certreq = CERT_CreateCertificateRequest(subject_name, spki, NULL); 373 if (!certreq) { 374 LOG(LS_ERROR) << "Couldn't create certificate signing request"; 375 goto fail; 376 } 377 378 validity = CERT_CreateValidity(not_before, not_after); 379 if (!validity) { 380 LOG(LS_ERROR) << "Couldn't create validity"; 381 goto fail; 382 } 383 384 unsigned long serial; 385 // Note: This serial in principle could collide, but it's unlikely 386 rv = PK11_GenerateRandom(reinterpret_cast<unsigned char *>(&serial), 387 sizeof(serial)); 388 if (rv != SECSuccess) { 389 LOG(LS_ERROR) << "Couldn't generate random serial"; 390 goto fail; 391 } 392 393 certificate = CERT_CreateCertificate(serial, subject_name, validity, certreq); 394 if (!certificate) { 395 LOG(LS_ERROR) << "Couldn't create certificate"; 396 goto fail; 397 } 398 399 arena = certificate->arena; 400 401 rv = SECOID_SetAlgorithmID(arena, &certificate->signature, 402 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL); 403 if (rv != SECSuccess) 404 goto fail; 405 406 // Set version to X509v3. 407 *(certificate->version.data) = 2; 408 certificate->version.len = 1; 409 410 if (!SEC_ASN1EncodeItem(arena, &inner_der, certificate, 411 SEC_ASN1_GET(CERT_CertificateTemplate))) 412 goto fail; 413 414 rv = SEC_DerSignData(arena, &signed_cert, inner_der.data, inner_der.len, 415 keypair->privkey(), 416 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION); 417 if (rv != SECSuccess) { 418 LOG(LS_ERROR) << "Couldn't sign certificate"; 419 goto fail; 420 } 421 certificate->derCert = signed_cert; 422 423 identity = new NSSIdentity(keypair, new NSSCertificate(certificate)); 424 425 goto done; 426 427 fail: 428 delete keypair; 429 430 done: 431 if (certificate) CERT_DestroyCertificate(certificate); 432 if (subject_name) CERT_DestroyName(subject_name); 433 if (spki) SECKEY_DestroySubjectPublicKeyInfo(spki); 434 if (certreq) CERT_DestroyCertificateRequest(certreq); 435 if (validity) CERT_DestroyValidity(validity); 436 return identity; 437} 438 439NSSIdentity* NSSIdentity::Generate(const std::string &common_name) { 440 SSLIdentityParams params; 441 params.common_name = common_name; 442 params.not_before = CERTIFICATE_WINDOW; 443 params.not_after = CERTIFICATE_LIFETIME; 444 return GenerateInternal(params); 445} 446 447NSSIdentity* NSSIdentity::GenerateForTest(const SSLIdentityParams& params) { 448 return GenerateInternal(params); 449} 450 451SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key, 452 const std::string& certificate) { 453 std::string private_key_der; 454 if (!SSLIdentity::PemToDer( 455 kPemTypeRsaPrivateKey, private_key, &private_key_der)) 456 return NULL; 457 458 SECItem private_key_item; 459 private_key_item.data = reinterpret_cast<unsigned char *>( 460 const_cast<char *>(private_key_der.c_str())); 461 private_key_item.len = checked_cast<unsigned int>(private_key_der.size()); 462 463 const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | 464 KU_DIGITAL_SIGNATURE; 465 466 SECKEYPrivateKey* privkey = NULL; 467 SECStatus rv = 468 PK11_ImportDERPrivateKeyInfoAndReturnKey(NSSContext::GetSlot(), 469 &private_key_item, 470 NULL, NULL, PR_FALSE, PR_FALSE, 471 key_usage, &privkey, NULL); 472 if (rv != SECSuccess) { 473 LOG(LS_ERROR) << "Couldn't import private key"; 474 return NULL; 475 } 476 477 SECKEYPublicKey *pubkey = SECKEY_ConvertToPublicKey(privkey); 478 if (rv != SECSuccess) { 479 SECKEY_DestroyPrivateKey(privkey); 480 LOG(LS_ERROR) << "Couldn't convert private key to public key"; 481 return NULL; 482 } 483 484 // Assign to a scoped_ptr so we don't leak on error. 485 scoped_ptr<NSSKeyPair> keypair(new NSSKeyPair(privkey, pubkey)); 486 487 scoped_ptr<NSSCertificate> cert(NSSCertificate::FromPEMString(certificate)); 488 if (!cert) { 489 LOG(LS_ERROR) << "Couldn't parse certificate"; 490 return NULL; 491 } 492 493 // TODO(ekr@rtfm.com): Check the public key against the certificate. 494 495 return new NSSIdentity(keypair.release(), cert.release()); 496} 497 498NSSIdentity *NSSIdentity::GetReference() const { 499 NSSKeyPair *keypair = keypair_->GetReference(); 500 if (!keypair) 501 return NULL; 502 503 NSSCertificate *certificate = certificate_->GetReference(); 504 if (!certificate) { 505 delete keypair; 506 return NULL; 507 } 508 509 return new NSSIdentity(keypair, certificate); 510} 511 512 513NSSCertificate &NSSIdentity::certificate() const { 514 return *certificate_; 515} 516 517 518} // rtc namespace 519 520#endif // HAVE_NSS_SSL_H 521 522