1 // Copyright (c) 2012 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/common/net/x509_certificate_model.h" 6 7 #include <cert.h> 8 #include <cms.h> 9 #include <hasht.h> 10 #include <keyhi.h> // SECKEY_DestroyPrivateKey 11 #include <keythi.h> // SECKEYPrivateKey 12 #include <pk11pub.h> // PK11_FindKeyByAnyCert 13 #include <seccomon.h> // SECItem 14 #include <sechash.h> 15 16 #include "base/logging.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h" 19 #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h" 20 #include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h" 21 #include "crypto/nss_util.h" 22 #include "crypto/scoped_nss_types.h" 23 #include "net/cert/x509_certificate.h" 24 25 namespace psm = mozilla_security_manager; 26 27 namespace { 28 29 // Convert a char* return value from NSS into a std::string and free the NSS 30 // memory. If the arg is NULL, an empty string will be returned instead. 31 std::string Stringize(char* nss_text, const std::string& alternative_text) { 32 if (!nss_text) 33 return alternative_text; 34 35 std::string s = nss_text; 36 PORT_Free(nss_text); 37 return s; 38 } 39 40 // Hash a certificate using the given algorithm, return the result as a 41 // colon-seperated hex string. The len specified is the number of bytes 42 // required for storing the raw fingerprint. 43 // (It's a bit redundant that the caller needs to specify len in addition to the 44 // algorithm, but given the limited uses, not worth fixing.) 45 std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) { 46 unsigned char fingerprint[HASH_LENGTH_MAX]; 47 48 DCHECK(NULL != cert->derCert.data); 49 DCHECK_NE(0U, cert->derCert.len); 50 DCHECK_LE(len, HASH_LENGTH_MAX); 51 memset(fingerprint, 0, len); 52 SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data, 53 cert->derCert.len); 54 DCHECK_EQ(rv, SECSuccess); 55 return x509_certificate_model::ProcessRawBytes(fingerprint, len); 56 } 57 58 std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) { 59 return psm::GetOIDText(&algorithm_id->algorithm); 60 } 61 62 std::string ProcessExtension( 63 const std::string& critical_label, 64 const std::string& non_critical_label, 65 CERTCertExtension* extension) { 66 std::string criticality = 67 extension->critical.data && extension->critical.data[0] ? 68 critical_label : non_critical_label; 69 return criticality + "\n" + 70 psm::ProcessExtensionData(SECOID_FindOIDTag(&extension->id), 71 &extension->value); 72 } 73 74 //////////////////////////////////////////////////////////////////////////////// 75 // NSS certificate export functions. 76 77 class FreeNSSCMSMessage { 78 public: 79 inline void operator()(NSSCMSMessage* x) const { 80 NSS_CMSMessage_Destroy(x); 81 } 82 }; 83 typedef scoped_ptr_malloc<NSSCMSMessage, FreeNSSCMSMessage> 84 ScopedNSSCMSMessage; 85 86 class FreeNSSCMSSignedData { 87 public: 88 inline void operator()(NSSCMSSignedData* x) const { 89 NSS_CMSSignedData_Destroy(x); 90 } 91 }; 92 typedef scoped_ptr_malloc<NSSCMSSignedData, FreeNSSCMSSignedData> 93 ScopedNSSCMSSignedData; 94 95 } // namespace 96 97 namespace x509_certificate_model { 98 99 using net::X509Certificate; 100 using std::string; 101 102 string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) { 103 string name = ProcessIDN( 104 Stringize(CERT_GetCommonName(&cert_handle->subject), std::string())); 105 if (!name.empty()) 106 return name; 107 return GetNickname(cert_handle); 108 } 109 110 string GetNickname(X509Certificate::OSCertHandle cert_handle) { 111 string name; 112 if (cert_handle->nickname) { 113 name = cert_handle->nickname; 114 // Hack copied from mozilla: Cut off text before first :, which seems to 115 // just be the token name. 116 size_t colon_pos = name.find(':'); 117 if (colon_pos != string::npos) 118 name = name.substr(colon_pos + 1); 119 } 120 return name; 121 } 122 123 string GetTokenName(X509Certificate::OSCertHandle cert_handle) { 124 return psm::GetCertTokenName(cert_handle); 125 } 126 127 string GetVersion(X509Certificate::OSCertHandle cert_handle) { 128 // If the version field is omitted from the certificate, the default 129 // value is v1(0). 130 unsigned long version = 0; 131 if (cert_handle->version.len == 0 || 132 SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess) { 133 return base::UintToString(version + 1); 134 } 135 return std::string(); 136 } 137 138 net::CertType GetType(X509Certificate::OSCertHandle cert_handle) { 139 return psm::GetCertType(cert_handle); 140 } 141 142 string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) { 143 if (cert_handle->emailAddr) 144 return cert_handle->emailAddr; 145 return std::string(); 146 } 147 148 void GetUsageStrings(X509Certificate::OSCertHandle cert_handle, 149 std::vector<string>* usages) { 150 psm::GetCertUsageStrings(cert_handle, usages); 151 } 152 153 string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) { 154 SECItem key_usage; 155 key_usage.data = NULL; 156 string key_usage_str; 157 if (CERT_FindKeyUsageExtension(cert_handle, &key_usage) == SECSuccess) { 158 key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ','); 159 PORT_Free(key_usage.data); 160 } 161 return key_usage_str; 162 } 163 164 string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle, 165 const string& alternative_text) { 166 return Stringize(CERT_Hexify(&cert_handle->serialNumber, true), 167 alternative_text); 168 } 169 170 string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle, 171 const string& alternative_text) { 172 return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text); 173 } 174 175 string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle, 176 const string& alternative_text) { 177 return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text); 178 } 179 180 string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle, 181 const string& alternative_text) { 182 return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text); 183 } 184 185 string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle, 186 const string& alternative_text) { 187 return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text); 188 } 189 190 string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle, 191 const string& alternative_text) { 192 return Stringize(CERT_GetOrgUnitName(&cert_handle->subject), 193 alternative_text); 194 } 195 196 string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle, 197 const string& alternative_text) { 198 return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text); 199 } 200 201 bool GetTimes(X509Certificate::OSCertHandle cert_handle, 202 base::Time* issued, base::Time* expires) { 203 PRTime pr_issued, pr_expires; 204 if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) { 205 *issued = crypto::PRTimeToBaseTime(pr_issued); 206 *expires = crypto::PRTimeToBaseTime(pr_expires); 207 return true; 208 } 209 return false; 210 } 211 212 string GetTitle(X509Certificate::OSCertHandle cert_handle) { 213 return psm::GetCertTitle(cert_handle); 214 } 215 216 string GetIssuerName(X509Certificate::OSCertHandle cert_handle) { 217 return psm::ProcessName(&cert_handle->issuer); 218 } 219 220 string GetSubjectName(X509Certificate::OSCertHandle cert_handle) { 221 return psm::ProcessName(&cert_handle->subject); 222 } 223 224 void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle, 225 std::vector<string>* email_addresses) { 226 for (const char* addr = CERT_GetFirstEmailAddress(cert_handle); 227 addr; addr = CERT_GetNextEmailAddress(cert_handle, addr)) { 228 // The first email addr (from Subject) may be duplicated in Subject 229 // Alternative Name, so check subsequent addresses are not equal to the 230 // first one before adding to the list. 231 if (!email_addresses->size() || (*email_addresses)[0] != addr) 232 email_addresses->push_back(addr); 233 } 234 } 235 236 void GetNicknameStringsFromCertList( 237 const std::vector<scoped_refptr<X509Certificate> >& certs, 238 const string& cert_expired, 239 const string& cert_not_yet_valid, 240 std::vector<string>* nick_names) { 241 CERTCertList* cert_list = CERT_NewCertList(); 242 for (size_t i = 0; i < certs.size(); ++i) { 243 CERT_AddCertToListTail( 244 cert_list, 245 CERT_DupCertificate(certs[i]->os_cert_handle())); 246 } 247 // Would like to use CERT_GetCertNicknameWithValidity on each cert 248 // individually instead of having to build a CERTCertList for this, but that 249 // function is not exported. 250 CERTCertNicknames* cert_nicknames = CERT_NicknameStringsFromCertList( 251 cert_list, 252 const_cast<char*>(cert_expired.c_str()), 253 const_cast<char*>(cert_not_yet_valid.c_str())); 254 DCHECK_EQ(cert_nicknames->numnicknames, 255 static_cast<int>(certs.size())); 256 257 for (int i = 0; i < cert_nicknames->numnicknames; ++i) 258 nick_names->push_back(cert_nicknames->nicknames[i]); 259 260 CERT_FreeNicknames(cert_nicknames); 261 CERT_DestroyCertList(cert_list); 262 } 263 264 // For background see this discussion on dev-tech-crypto.lists.mozilla.org: 265 // http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX 266 // 267 // NOTE: This function relies on the convention that the same PKCS#11 ID 268 // is shared between a certificate and its associated private and public 269 // keys. I tried to implement this with PK11_GetLowLevelKeyIDForCert(), 270 // but that always returns NULL on Chrome OS for me. 271 std::string GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle) { 272 std::string pkcs11_id; 273 SECKEYPrivateKey *priv_key = PK11_FindKeyByAnyCert(cert_handle, 274 NULL /* wincx */); 275 if (priv_key) { 276 // Get the CKA_ID attribute for a key. 277 SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key); 278 if (sec_item) { 279 pkcs11_id = base::HexEncode(sec_item->data, sec_item->len); 280 SECITEM_FreeItem(sec_item, PR_TRUE); 281 } 282 SECKEY_DestroyPrivateKey(priv_key); 283 } 284 return pkcs11_id; 285 } 286 287 void GetExtensions( 288 const string& critical_label, 289 const string& non_critical_label, 290 X509Certificate::OSCertHandle cert_handle, 291 Extensions* extensions) { 292 if (cert_handle->extensions) { 293 for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) { 294 Extension extension; 295 extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id); 296 extension.value = ProcessExtension( 297 critical_label, non_critical_label, cert_handle->extensions[i]); 298 extensions->push_back(extension); 299 } 300 } 301 } 302 303 string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) { 304 return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH); 305 } 306 307 string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) { 308 return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH); 309 } 310 311 void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle, 312 X509Certificate::OSCertHandles* cert_handles) { 313 CERTCertList* cert_list = 314 CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer); 315 CERTCertListNode* node; 316 for (node = CERT_LIST_HEAD(cert_list); 317 !CERT_LIST_END(node, cert_list); 318 node = CERT_LIST_NEXT(node)) { 319 cert_handles->push_back(CERT_DupCertificate(node->cert)); 320 } 321 CERT_DestroyCertList(cert_list); 322 } 323 324 void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) { 325 for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin()); 326 i != cert_handles->end(); ++i) 327 CERT_DestroyCertificate(*i); 328 cert_handles->clear(); 329 } 330 331 string GetDerString(X509Certificate::OSCertHandle cert_handle) { 332 return string(reinterpret_cast<const char*>(cert_handle->derCert.data), 333 cert_handle->derCert.len); 334 } 335 336 string GetCMSString(const X509Certificate::OSCertHandles& cert_chain, 337 size_t start, size_t end) { 338 crypto::ScopedPLArenaPool arena(PORT_NewArena(1024)); 339 DCHECK(arena.get()); 340 341 ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get())); 342 DCHECK(message.get()); 343 344 // First, create SignedData with the certificate only (no chain). 345 ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly( 346 message.get(), cert_chain[start], PR_FALSE)); 347 if (!signed_data.get()) { 348 DLOG(ERROR) << "NSS_CMSSignedData_Create failed"; 349 return std::string(); 350 } 351 // Add the rest of the chain (if any). 352 for (size_t i = start + 1; i < end; ++i) { 353 if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) != 354 SECSuccess) { 355 DLOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i; 356 return std::string(); 357 } 358 } 359 360 NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get()); 361 if (NSS_CMSContentInfo_SetContent_SignedData( 362 message.get(), cinfo, signed_data.get()) == SECSuccess) { 363 ignore_result(signed_data.release()); 364 } else { 365 DLOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed"; 366 return std::string(); 367 } 368 369 SECItem cert_p7 = { siBuffer, NULL, 0 }; 370 NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL, 371 &cert_p7, arena.get(), NULL, 372 NULL, NULL, NULL, NULL, 373 NULL); 374 if (!ecx) { 375 DLOG(ERROR) << "NSS_CMSEncoder_Start failed"; 376 return std::string(); 377 } 378 379 if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) { 380 DLOG(ERROR) << "NSS_CMSEncoder_Finish failed"; 381 return std::string(); 382 } 383 384 return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len); 385 } 386 387 string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) { 388 return ProcessSecAlgorithmInternal(&cert_handle->signature); 389 } 390 391 string ProcessSecAlgorithmSubjectPublicKey( 392 X509Certificate::OSCertHandle cert_handle) { 393 return ProcessSecAlgorithmInternal( 394 &cert_handle->subjectPublicKeyInfo.algorithm); 395 } 396 397 string ProcessSecAlgorithmSignatureWrap( 398 X509Certificate::OSCertHandle cert_handle) { 399 return ProcessSecAlgorithmInternal( 400 &cert_handle->signatureWrap.signatureAlgorithm); 401 } 402 403 string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) { 404 return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo); 405 } 406 407 string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) { 408 return ProcessRawBits(cert_handle->signatureWrap.signature.data, 409 cert_handle->signatureWrap.signature.len); 410 } 411 412 void RegisterDynamicOids() { 413 psm::RegisterDynamicOids(); 414 } 415 416 } // namespace x509_certificate_model 417