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