Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/x509_util.h" | 5 #include "net/base/x509_util.h" |
| 6 #include "net/base/x509_util_nss.h" | 6 #include "net/base/x509_util_nss.h" |
| 7 | 7 |
| 8 #include <cert.h> | 8 #include <cert.h> |
| 9 #include <cryptohi.h> | 9 #include <cryptohi.h> |
| 10 #include <pk11pub.h> | 10 #include <pk11pub.h> |
| 11 #include <prerror.h> | 11 #include <prerror.h> |
| 12 #include <secmod.h> | 12 #include <secmod.h> |
| 13 #include <secport.h> | 13 #include <secport.h> |
| 14 | 14 |
| 15 #include "base/debug/leak_annotations.h" | 15 #include "base/debug/leak_annotations.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 18 #include "base/memory/singleton.h" | 18 #include "base/memory/singleton.h" |
| 19 #include "crypto/nss_util.h" | 19 #include "crypto/nss_util.h" |
| 20 #include "crypto/nss_util_internal.h" | 20 #include "crypto/nss_util_internal.h" |
| 21 #include "crypto/rsa_private_key.h" | 21 #include "crypto/rsa_private_key.h" |
| 22 #include "crypto/scoped_nss_types.h" | 22 #include "crypto/scoped_nss_types.h" |
| 23 #include "net/base/x509_certificate.h" | |
| 24 #include "net/third_party/mozilla_security_manager/nsNSSCertTrust.h" | |
| 25 | |
| 26 namespace msm = mozilla_security_manager; | |
| 23 | 27 |
| 24 namespace { | 28 namespace { |
| 25 | 29 |
| 26 class ObCertOIDWrapper { | 30 class ObCertOIDWrapper { |
| 27 public: | 31 public: |
| 28 static ObCertOIDWrapper* GetInstance() { | 32 static ObCertOIDWrapper* GetInstance() { |
| 29 // Instantiated as a leaky singleton to allow the singleton to be | 33 // Instantiated as a leaky singleton to allow the singleton to be |
| 30 // constructed on a worker thead that is not joined when a process | 34 // constructed on a worker thead that is not joined when a process |
| 31 // shuts down. | 35 // shuts down. |
| 32 return Singleton<ObCertOIDWrapper, | 36 return Singleton<ObCertOIDWrapper, |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 // Sign the ASN1 encoded cert and save it to |result|. | 163 // Sign the ASN1 encoded cert and save it to |result|. |
| 160 rv = SEC_DerSignData(arena, result, der.data, der.len, key, algo_id); | 164 rv = SEC_DerSignData(arena, result, der.data, der.len, key, algo_id); |
| 161 if (rv != SECSuccess) | 165 if (rv != SECSuccess) |
| 162 return false; | 166 return false; |
| 163 | 167 |
| 164 // Save the signed result to the cert. | 168 // Save the signed result to the cert. |
| 165 cert->derCert = *result; | 169 cert->derCert = *result; |
| 166 | 170 |
| 167 return true; | 171 return true; |
| 168 } | 172 } |
| 169 | |
|
wtc
2011/12/08 00:07:43
Nit: keep this blank line. This blank line matche
Greg Spencer (Chromium)
2011/12/09 18:51:38
Done.
| |
| 170 } // namespace | 173 } // namespace |
| 171 | 174 |
| 172 namespace net { | 175 namespace net { |
| 173 | 176 |
| 174 namespace x509_util { | 177 namespace x509_util { |
| 175 | 178 |
| 176 CERTCertificate* CreateSelfSignedCert( | 179 CERTCertificate* CreateSelfSignedCert( |
| 177 SECKEYPublicKey* public_key, | 180 SECKEYPublicKey* public_key, |
| 178 SECKEYPrivateKey* private_key, | 181 SECKEYPrivateKey* private_key, |
| 179 const std::string& subject, | 182 const std::string& subject, |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 | 309 |
| 307 DCHECK(cert->derCert.len); | 310 DCHECK(cert->derCert.len); |
| 308 // XXX copied from X509Certificate::GetDEREncoded | 311 // XXX copied from X509Certificate::GetDEREncoded |
| 309 der_cert->clear(); | 312 der_cert->clear(); |
| 310 der_cert->append(reinterpret_cast<char*>(cert->derCert.data), | 313 der_cert->append(reinterpret_cast<char*>(cert->derCert.data), |
| 311 cert->derCert.len); | 314 cert->derCert.len); |
| 312 CERT_DestroyCertificate(cert); | 315 CERT_DestroyCertificate(cert); |
| 313 return true; | 316 return true; |
| 314 } | 317 } |
| 315 | 318 |
| 319 CertType GetCertType(const X509Certificate* cert) { | |
| 320 DCHECK(cert); | |
| 321 msm::nsNSSCertTrust trust(cert->os_cert_handle()->trust); | |
| 322 if (trust.HasAnyUser()) | |
| 323 return USER_CERT; | |
| 324 if (trust.HasAnyCA() || CERT_IsCACert(cert->os_cert_handle(), NULL)) | |
| 325 return CA_CERT; | |
| 326 if (trust.HasPeer(PR_TRUE, PR_FALSE, PR_FALSE)) | |
| 327 return SERVER_CERT; | |
| 328 return UNKNOWN_CERT; | |
| 329 } | |
| 330 | |
| 331 std::string GetDefaultCertificateLabel(const X509Certificate* cert) { | |
| 332 DCHECK(cert); | |
| 333 std::string result; | |
| 334 | |
| 335 #if defined(OS_CHROMEOS) | |
| 336 // When we have label support, we want to keep any existing label by default. | |
| 337 result = GetLabel(cert); | |
| 338 if (!result.empty()) | |
| 339 return result; | |
| 340 #endif | |
|
wtc
2011/12/08 00:07:43
This behavior (lines 335-340) is difficult to docu
Greg Spencer (Chromium)
2011/12/09 18:51:38
We talked about this: I've removed this from the f
| |
| 341 | |
| 342 switch (GetCertType(cert)) { | |
| 343 case CA_CERT: { | |
| 344 char *nickname = CERT_MakeCANickname(cert->os_cert_handle()); | |
|
wtc
2011/12/08 00:07:43
Nit: put '*' next to 'char'.
Greg Spencer (Chromium)
2011/12/09 18:51:38
Done.
| |
| 345 result = nickname; | |
| 346 PORT_Free(nickname); | |
| 347 break; | |
| 348 } | |
| 349 case USER_CERT: { | |
| 350 // Create a nickname for this user certificate. | |
| 351 // We use the scheme used by Firefox: | |
|
wtc
2011/12/08 00:07:43
Nit: extraneous space before "We".
Greg Spencer (Chromium)
2011/12/09 18:51:38
Fixed.
| |
| 352 // --> <subject's common name>'s <issuer's common name> ID. | |
| 353 // TODO(gspencer): internationalize this: it's wrong to | |
| 354 // hard code English. | |
| 355 | |
| 356 std::string username, ca_name; | |
| 357 char* temp_username = CERT_GetCommonName( | |
| 358 &cert->os_cert_handle()->subject); | |
| 359 char* temp_ca_name = CERT_GetCommonName(&cert->os_cert_handle()->issuer); | |
| 360 if (temp_username) { | |
| 361 username = temp_username; | |
| 362 PORT_Free(temp_username); | |
| 363 } | |
| 364 if (temp_ca_name) { | |
| 365 ca_name = temp_ca_name; | |
| 366 PORT_Free(temp_ca_name); | |
| 367 } | |
| 368 result = username + "'s " + ca_name + " ID"; | |
| 369 break; | |
| 370 } | |
| 371 case SERVER_CERT: { | |
| 372 result = cert->subject().GetDisplayName(); | |
| 373 break; | |
| 374 } | |
|
wtc
2011/12/08 00:07:43
Nit: this case doesn't need curly braces because i
Greg Spencer (Chromium)
2011/12/09 18:51:38
Done.
| |
| 375 case UNKNOWN_CERT: | |
| 376 default: | |
| 377 break; | |
| 378 } | |
| 379 return result; | |
| 380 } | |
| 381 | |
| 382 #if defined(OS_CHROMEOS) | |
| 383 bool SetLabel(X509Certificate* cert, const std::string& label) { | |
| 384 DCHECK(cert); | |
| 385 // If the slot isn't initialized, then do nothing. | |
| 386 if (!cert->os_cert_handle()->slot) | |
| 387 return true; | |
| 388 | |
| 389 // First we set the nickname on the cert itself. This doesn't | |
| 390 // work on production NSS yet (it's not implemented), but ChromeOS | |
| 391 // has a patched version that it will work on. | |
| 392 SECItem sec_label; | |
| 393 sec_label.type = siUTF8String; | |
|
wtc
2011/12/08 00:07:43
PK11_WriteRawAttribute does not use the 'type' fie
Greg Spencer (Chromium)
2011/12/09 18:51:38
Done.
| |
| 394 sec_label.data = reinterpret_cast<unsigned char*>( | |
| 395 const_cast<char*>(label.c_str())); | |
| 396 sec_label.len = label.size(); | |
| 397 SECStatus srv = PK11_WriteRawAttribute(PK11_TypeCert, | |
| 398 cert->os_cert_handle(), | |
| 399 CKA_LABEL, | |
| 400 &sec_label); | |
|
wtc
2011/12/08 00:07:43
IMPORTANT: my concern about this solution is that
Greg Spencer (Chromium)
2011/12/09 18:51:38
I've stopped trying to set the nickname later, and
| |
| 401 if (srv != SECSuccess) | |
| 402 LOG(WARNING) << "Unable to set certificate label to " | |
| 403 << label; | |
|
wtc
2011/12/08 00:07:43
Nit: please add curly braces because the statement
Greg Spencer (Chromium)
2011/12/09 18:51:38
Done.
| |
| 404 | |
| 405 // As far as I can tell, there is no API for PKCS11 that allows one | |
| 406 // to get the valid public key (that has a valid PKCS11 slot and id) | |
| 407 // associated with a certificate. So, instead, I extract the public | |
| 408 // key using CERT_ExtractPublicKey (which returns a key that has no | |
| 409 // slot or pkcs11 id set), and then we iterate through all of the | |
| 410 // existing public keys (which do have this information), and look | |
| 411 // for one that has the same DER encoding as the public key | |
| 412 // extracted from this certificate. I then set that key's nickname | |
| 413 // to the given label. | |
| 414 SECKEYPublicKey* public_key = CERT_ExtractPublicKey(cert->os_cert_handle()); | |
| 415 if (!public_key) | |
| 416 return false; | |
| 417 | |
| 418 SECKEYPublicKeyList* pubkey_list = | |
| 419 PK11_ListPublicKeysInSlot(cert->os_cert_handle()->slot, NULL); | |
| 420 | |
| 421 // If there are no public keys, that's OK. | |
| 422 if (pubkey_list) { | |
| 423 for (SECKEYPublicKeyListNode* node = PUBKEY_LIST_HEAD(pubkey_list); | |
| 424 !PUBKEY_LIST_END(node, pubkey_list); | |
| 425 node = PUBKEY_LIST_NEXT(node)) { | |
| 426 SECItem* der_encoded = PK11_DEREncodePublicKey(node->key); | |
| 427 if (SECITEM_CompareItem( | |
| 428 der_encoded, | |
| 429 &cert->os_cert_handle()->derPublicKey) == SECEqual) | |
| 430 PK11_SetPublicKeyNickname(node->key, label.c_str()); | |
| 431 SECITEM_FreeItem(der_encoded, PR_TRUE); | |
| 432 } | |
| 433 SECKEY_DestroyPublicKeyList(pubkey_list); | |
| 434 } | |
| 435 SECKEY_DestroyPublicKey(public_key); | |
| 436 | |
| 437 // Now set the nickname on the private key (if there is one) | |
| 438 SECKEYPrivateKey* private_key = | |
| 439 PK11_FindPrivateKeyFromCert(cert->os_cert_handle()->slot, | |
| 440 cert->os_cert_handle(), | |
| 441 NULL); | |
| 442 if (private_key) { | |
| 443 PK11_SetPrivateKeyNickname(private_key, label.c_str()); | |
| 444 SECKEY_DestroyPrivateKey(private_key); | |
| 445 } | |
| 446 return true; | |
| 447 } | |
| 448 | |
| 449 std::string GetLabel(const X509Certificate* cert) { | |
| 450 std::string result; | |
| 451 // This doesn't work on production NSS yet (it's not implemented), but | |
| 452 // ChromeOS has a patched version that it will work on. | |
| 453 SECItem sec_label; | |
| 454 SECStatus srv = PK11_ReadRawAttribute(PK11_TypeCert, | |
| 455 cert->os_cert_handle(), | |
| 456 CKA_LABEL, | |
| 457 &sec_label); | |
|
wtc
2011/12/08 00:07:43
Why don't you just use the 'nickname' field of the
Greg Spencer (Chromium)
2011/12/09 18:51:38
For server and CA certs, this works because I can
| |
| 458 if (srv != SECSuccess) { | |
| 459 return result; | |
| 460 } | |
| 461 | |
| 462 result = std::string(reinterpret_cast<char *>(sec_label.data), sec_label.len); | |
| 463 PORT_Free(sec_label.data); | |
| 464 return result; | |
| 465 } | |
| 466 #endif // OS_CHROMEOS | |
| 467 | |
| 468 | |
| 316 } // namespace x509_util | 469 } // namespace x509_util |
| 317 | 470 |
| 318 } // namespace net | 471 } // namespace net |
| OLD | NEW |