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.
5 #include "net/base/cert_verify_proc_nss.h"
17 #include "base/logging.h"
18 #include "crypto/nss_util.h"
19 #include "crypto/scoped_nss_types.h"
20 #include "crypto/sha2.h"
21 #include "net/base/asn1_util.h"
22 #include "net/base/cert_status_flags.h"
23 #include "net/base/cert_verifier.h"
24 #include "net/base/cert_verify_result.h"
25 #include "net/base/crl_set.h"
26 #include "net/base/ev_root_ca_metadata.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/x509_certificate.h"
29 #include "net/base/x509_util_nss.h"
32 #include <CommonCrypto/CommonDigest.h>
33 #include "net/base/x509_util_ios.h"
34 #endif // defined(OS_IOS)
36 #define NSS_VERSION_NUM (NSS_VMAJOR * 10000 + NSS_VMINOR * 100 + NSS_VPATCH)
37 #if NSS_VERSION_NUM < 31305
38 // Added in NSS 3.13.5.
39 #define SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED -8016
46 typedef scoped_ptr_malloc
<
47 CERTCertificatePolicies
,
48 crypto::NSSDestroyer
<CERTCertificatePolicies
,
49 CERT_DestroyCertificatePoliciesExtension
> >
50 ScopedCERTCertificatePolicies
;
52 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam
53 // array that cvout points to. cvout must be initialized as passed to
54 // CERT_PKIXVerifyCert, so that the array must be terminated with
56 // When it goes out of scope, it destroys values of cert_po_trustAnchor
57 // and cert_po_certList types, but doesn't release the array itself.
58 class ScopedCERTValOutParam
{
60 explicit ScopedCERTValOutParam(CERTValOutParam
* cvout
)
63 ~ScopedCERTValOutParam() {
66 for (CERTValOutParam
*p
= cvout_
; p
->type
!= cert_po_end
; p
++) {
68 case cert_po_trustAnchor
:
69 if (p
->value
.pointer
.cert
) {
70 CERT_DestroyCertificate(p
->value
.pointer
.cert
);
71 p
->value
.pointer
.cert
= NULL
;
74 case cert_po_certList
:
75 if (p
->value
.pointer
.chain
) {
76 CERT_DestroyCertList(p
->value
.pointer
.chain
);
77 p
->value
.pointer
.chain
= NULL
;
87 CERTValOutParam
* cvout_
;
89 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam
);
92 // Map PORT_GetError() return values to our network error codes.
93 int MapSecurityError(int err
) {
95 case PR_DIRECTORY_LOOKUP_ERROR
: // DNS lookup error.
96 return ERR_NAME_NOT_RESOLVED
;
97 case SEC_ERROR_INVALID_ARGS
:
98 return ERR_INVALID_ARGUMENT
;
99 case SSL_ERROR_BAD_CERT_DOMAIN
:
100 return ERR_CERT_COMMON_NAME_INVALID
;
101 case SEC_ERROR_INVALID_TIME
:
102 case SEC_ERROR_EXPIRED_CERTIFICATE
:
103 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
:
104 return ERR_CERT_DATE_INVALID
;
105 case SEC_ERROR_UNKNOWN_ISSUER
:
106 case SEC_ERROR_UNTRUSTED_ISSUER
:
107 case SEC_ERROR_CA_CERT_INVALID
:
108 return ERR_CERT_AUTHORITY_INVALID
;
109 // TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
110 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
:
111 case SEC_ERROR_OCSP_SERVER_ERROR
:
112 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION
;
113 case SEC_ERROR_REVOKED_CERTIFICATE
:
114 case SEC_ERROR_UNTRUSTED_CERT
: // Treat as revoked.
115 return ERR_CERT_REVOKED
;
116 case SEC_ERROR_BAD_DER
:
117 case SEC_ERROR_BAD_SIGNATURE
:
118 case SEC_ERROR_CERT_NOT_VALID
:
119 // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
120 case SEC_ERROR_CERT_USAGES_INVALID
:
121 case SEC_ERROR_INADEQUATE_KEY_USAGE
: // Key usage.
122 case SEC_ERROR_INADEQUATE_CERT_TYPE
: // Extended key usage and whether
123 // the certificate is a CA.
124 case SEC_ERROR_POLICY_VALIDATION_FAILED
:
125 case SEC_ERROR_CERT_NOT_IN_NAME_SPACE
:
126 case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID
:
127 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
:
128 case SEC_ERROR_EXTENSION_VALUE_INVALID
:
129 return ERR_CERT_INVALID
;
130 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
:
131 return ERR_CERT_WEAK_SIGNATURE_ALGORITHM
;
133 LOG(WARNING
) << "Unknown error " << err
<< " mapped to net::ERR_FAILED";
138 // Map PORT_GetError() return values to our cert status flags.
139 CertStatus
MapCertErrorToCertStatus(int err
) {
140 int net_error
= MapSecurityError(err
);
141 return MapNetErrorToCertStatus(net_error
);
144 // Saves some information about the certificate chain cert_list in
145 // *verify_result. The caller MUST initialize *verify_result before calling
147 // Note that cert_list[0] is the end entity certificate.
148 void GetCertChainInfo(CERTCertList
* cert_list
,
149 CERTCertificate
* root_cert
,
150 CertVerifyResult
* verify_result
) {
151 // NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the
152 // NSS version currently in use:
153 // 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*)
154 // 2. use ident libnss3.so* for the library's version
157 CERTCertificate
* verified_cert
= NULL
;
158 std::vector
<CERTCertificate
*> verified_chain
;
160 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
161 !CERT_LIST_END(node
, cert_list
);
162 node
= CERT_LIST_NEXT(node
), ++i
) {
164 verified_cert
= node
->cert
;
166 // Because of an NSS bug, CERT_PKIXVerifyCert may chain a self-signed
167 // certificate of a root CA to another certificate of the same root CA
168 // key. Detect that error and ignore the root CA certificate.
169 // See https://bugzilla.mozilla.org/show_bug.cgi?id=721288.
170 if (node
->cert
->isRoot
) {
171 // NOTE: isRoot doesn't mean the certificate is a trust anchor. It
172 // means the certificate is self-signed. Here we assume isRoot only
173 // implies the certificate is self-issued.
174 CERTCertListNode
* next_node
= CERT_LIST_NEXT(node
);
175 CERTCertificate
* next_cert
;
176 if (!CERT_LIST_END(next_node
, cert_list
)) {
177 next_cert
= next_node
->cert
;
179 next_cert
= root_cert
;
181 // Test that |node->cert| is actually a self-signed certificate
182 // whose key is equal to |next_cert|, and not a self-issued
183 // certificate signed by another key of the same CA.
184 if (next_cert
&& SECITEM_ItemsAreEqual(&node
->cert
->derPublicKey
,
185 &next_cert
->derPublicKey
)) {
189 verified_chain
.push_back(node
->cert
);
192 SECAlgorithmID
& signature
= node
->cert
->signature
;
193 SECOidTag oid_tag
= SECOID_FindOIDTag(&signature
.algorithm
);
195 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
:
196 verify_result
->has_md5
= true;
198 verify_result
->has_md5_ca
= true;
200 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION
:
201 verify_result
->has_md2
= true;
203 verify_result
->has_md2_ca
= true;
205 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION
:
206 verify_result
->has_md4
= true;
214 verified_chain
.push_back(root_cert
);
216 verify_result
->verified_cert
=
217 x509_util_ios::CreateCertFromNSSHandles(verified_cert
, verified_chain
);
219 verify_result
->verified_cert
=
220 X509Certificate::CreateFromHandle(verified_cert
, verified_chain
);
221 #endif // defined(OS_IOS)
224 // IsKnownRoot returns true if the given certificate is one that we believe
225 // is a standard (as opposed to user-installed) root.
226 bool IsKnownRoot(CERTCertificate
* root
) {
227 if (!root
|| !root
->slot
)
230 // This magic name is taken from
231 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
232 return 0 == strcmp(PK11_GetSlotName(root
->slot
),
233 "NSS Builtin Objects");
242 // CheckRevocationWithCRLSet attempts to check each element of |cert_list|
243 // against |crl_set|. It returns:
244 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
245 // kCRLSetError: if an error occurs in processing.
246 // kCRLSetOk: if no element in the chain is known to have been revoked.
247 CRLSetResult
CheckRevocationWithCRLSet(CERTCertList
* cert_list
,
248 CERTCertificate
* root
,
250 std::vector
<CERTCertificate
*> certs
;
253 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
254 !CERT_LIST_END(node
, cert_list
);
255 node
= CERT_LIST_NEXT(node
)) {
256 certs
.push_back(node
->cert
);
260 certs
.push_back(root
);
262 // We iterate from the root certificate down to the leaf, keeping track of
263 // the issuer's SPKI at each step.
264 std::string issuer_spki_hash
;
265 for (std::vector
<CERTCertificate
*>::reverse_iterator i
= certs
.rbegin();
266 i
!= certs
.rend(); ++i
) {
267 CERTCertificate
* cert
= *i
;
269 base::StringPiece
der(reinterpret_cast<char*>(cert
->derCert
.data
),
272 base::StringPiece spki
;
273 if (!asn1::ExtractSPKIFromDERCert(der
, &spki
)) {
277 const std::string spki_hash
= crypto::SHA256HashString(spki
);
279 base::StringPiece serial_number
= base::StringPiece(
280 reinterpret_cast<char*>(cert
->serialNumber
.data
),
281 cert
->serialNumber
.len
);
283 CRLSet::Result result
= crl_set
->CheckSPKI(spki_hash
);
285 if (result
!= CRLSet::REVOKED
&& !issuer_spki_hash
.empty())
286 result
= crl_set
->CheckSerial(serial_number
, issuer_spki_hash
);
288 issuer_spki_hash
= spki_hash
;
291 case CRLSet::REVOKED
:
292 return kCRLSetRevoked
;
293 case CRLSet::UNKNOWN
:
305 // Forward declarations.
306 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
307 CERTCertificate
* cert_handle
, int num_policy_oids
,
308 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
309 CERTValOutParam
* cvout
);
310 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
);
312 // Call CERT_PKIXVerifyCert for the cert_handle.
313 // Verification results are stored in an array of CERTValOutParam.
314 // If policy_oids is not NULL and num_policy_oids is positive, policies
316 // Caller must initialize cvout before calling this function.
317 SECStatus
PKIXVerifyCert(CERTCertificate
* cert_handle
,
318 bool check_revocation
,
319 bool cert_io_enabled
,
320 const SECOidTag
* policy_oids
,
322 CERTValOutParam
* cvout
) {
323 bool use_crl
= check_revocation
;
324 bool use_ocsp
= check_revocation
;
326 // These CAs have multiple keys, which trigger two bugs in NSS's CRL code.
327 // 1. NSS may use one key to verify a CRL signed with another key,
328 // incorrectly concluding that the CRL's signature is invalid.
329 // Hopefully this bug will be fixed in NSS 3.12.9.
330 // 2. NSS considers all certificates issued by the CA as revoked when it
331 // receives a CRL with an invalid signature. This overly strict policy
332 // has been relaxed in NSS 3.12.7. See
333 // https://bugzilla.mozilla.org/show_bug.cgi?id=562542.
334 // So we have to turn off CRL checking for these CAs. See
335 // http://crbug.com/55695.
336 static const char* const kMultipleKeyCA
[] = {
337 "CN=Microsoft Secure Server Authority,"
338 "DC=redmond,DC=corp,DC=microsoft,DC=com",
339 "CN=Microsoft Secure Server Authority",
342 if (!NSS_VersionCheck("3.12.7")) {
343 for (size_t i
= 0; i
< arraysize(kMultipleKeyCA
); ++i
) {
344 if (strcmp(cert_handle
->issuerName
, kMultipleKeyCA
[i
]) == 0) {
351 PRUint64 revocation_method_flags
=
352 CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD
|
353 CERT_REV_M_ALLOW_NETWORK_FETCHING
|
354 CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
|
355 CERT_REV_M_IGNORE_MISSING_FRESH_INFO
|
356 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
;
357 PRUint64 revocation_method_independent_flags
=
358 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
;
359 if (check_revocation
&& policy_oids
&& num_policy_oids
> 0) {
360 // EV verification requires revocation checking. Consider the certificate
361 // revoked if we don't have revocation info.
362 // TODO(wtc): Add a bool parameter to expressly specify we're doing EV
363 // verification or we want strict revocation flags.
364 revocation_method_flags
|= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
;
365 revocation_method_independent_flags
|=
366 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
;
368 revocation_method_flags
|= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
;
369 revocation_method_independent_flags
|=
370 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT
;
372 PRUint64 method_flags
[2];
373 method_flags
[cert_revocation_method_crl
] = revocation_method_flags
;
374 method_flags
[cert_revocation_method_ocsp
] = revocation_method_flags
;
377 method_flags
[cert_revocation_method_crl
] |=
378 CERT_REV_M_TEST_USING_THIS_METHOD
;
381 method_flags
[cert_revocation_method_ocsp
] |=
382 CERT_REV_M_TEST_USING_THIS_METHOD
;
385 CERTRevocationMethodIndex preferred_revocation_methods
[1];
387 preferred_revocation_methods
[0] = cert_revocation_method_ocsp
;
389 preferred_revocation_methods
[0] = cert_revocation_method_crl
;
392 CERTRevocationFlags revocation_flags
;
393 revocation_flags
.leafTests
.number_of_defined_methods
=
394 arraysize(method_flags
);
395 revocation_flags
.leafTests
.cert_rev_flags_per_method
= method_flags
;
396 revocation_flags
.leafTests
.number_of_preferred_methods
=
397 arraysize(preferred_revocation_methods
);
398 revocation_flags
.leafTests
.preferred_methods
= preferred_revocation_methods
;
399 revocation_flags
.leafTests
.cert_rev_method_independent_flags
=
400 revocation_method_independent_flags
;
402 revocation_flags
.chainTests
.number_of_defined_methods
=
403 arraysize(method_flags
);
404 revocation_flags
.chainTests
.cert_rev_flags_per_method
= method_flags
;
405 revocation_flags
.chainTests
.number_of_preferred_methods
=
406 arraysize(preferred_revocation_methods
);
407 revocation_flags
.chainTests
.preferred_methods
= preferred_revocation_methods
;
408 revocation_flags
.chainTests
.cert_rev_method_independent_flags
=
409 revocation_method_independent_flags
;
411 std::vector
<CERTValInParam
> cvin
;
413 CERTValInParam in_param
;
414 // No need to set cert_pi_trustAnchors here.
415 in_param
.type
= cert_pi_revocationFlags
;
416 in_param
.value
.pointer
.revocation
= &revocation_flags
;
417 cvin
.push_back(in_param
);
418 if (policy_oids
&& num_policy_oids
> 0) {
419 in_param
.type
= cert_pi_policyOID
;
420 in_param
.value
.arraySize
= num_policy_oids
;
421 in_param
.value
.array
.oids
= policy_oids
;
422 cvin
.push_back(in_param
);
424 in_param
.type
= cert_pi_end
;
425 cvin
.push_back(in_param
);
427 SECStatus rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
428 &cvin
[0], cvout
, NULL
);
429 if (rv
!= SECSuccess
) {
430 rv
= RetryPKIXVerifyCertWithWorkarounds(cert_handle
, num_policy_oids
,
431 cert_io_enabled
, &cvin
, cvout
);
436 // PKIXVerifyCert calls this function to work around some bugs in
437 // CERT_PKIXVerifyCert. All the arguments of this function are either the
438 // arguments or local variables of PKIXVerifyCert.
439 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
440 CERTCertificate
* cert_handle
, int num_policy_oids
,
441 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
442 CERTValOutParam
* cvout
) {
443 // We call this function when the first CERT_PKIXVerifyCert call in
444 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure.
445 SECStatus rv
= SECFailure
;
446 int nss_error
= PORT_GetError();
447 CERTValInParam in_param
;
449 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
450 // CA certificate, so we retry with cert_pi_useAIACertFetch.
451 // cert_pi_useAIACertFetch has several bugs in its error handling and
452 // error reporting (NSS bug 528743), so we don't use it by default.
453 // Note: When building a certificate chain, CERT_PKIXVerifyCert may
454 // incorrectly pick a CA certificate with the same subject name as the
455 // missing intermediate CA certificate, and fail with the
456 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
457 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
458 if (cert_io_enabled
&&
459 (nss_error
== SEC_ERROR_UNKNOWN_ISSUER
||
460 nss_error
== SEC_ERROR_BAD_SIGNATURE
)) {
461 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
463 in_param
.type
= cert_pi_useAIACertFetch
;
464 in_param
.value
.scalar
.b
= PR_TRUE
;
465 cvin
->push_back(in_param
);
466 in_param
.type
= cert_pi_end
;
467 cvin
->push_back(in_param
);
468 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
469 &(*cvin
)[0], cvout
, NULL
);
470 if (rv
== SECSuccess
)
472 int new_nss_error
= PORT_GetError();
473 if (new_nss_error
== SEC_ERROR_INVALID_ARGS
||
474 new_nss_error
== SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE
||
475 new_nss_error
== SEC_ERROR_BAD_INFO_ACCESS_LOCATION
||
476 new_nss_error
== SEC_ERROR_BAD_HTTP_RESPONSE
||
477 new_nss_error
== SEC_ERROR_BAD_LDAP_RESPONSE
||
478 !IS_SEC_ERROR(new_nss_error
)) {
479 // Use the original error code because of cert_pi_useAIACertFetch's
480 // bad error reporting.
481 PORT_SetError(nss_error
);
484 nss_error
= new_nss_error
;
487 // If an intermediate CA certificate has requireExplicitPolicy in its
488 // policyConstraints extension, CERT_PKIXVerifyCert fails with
489 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
490 // certificate policy (NSS bug 552775). So we retry with the certificate
491 // policy found in the server certificate.
492 if (nss_error
== SEC_ERROR_POLICY_VALIDATION_FAILED
&&
493 num_policy_oids
== 0) {
494 SECOidTag policy
= GetFirstCertPolicy(cert_handle
);
495 if (policy
!= SEC_OID_UNKNOWN
) {
496 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
498 in_param
.type
= cert_pi_policyOID
;
499 in_param
.value
.arraySize
= 1;
500 in_param
.value
.array
.oids
= &policy
;
501 cvin
->push_back(in_param
);
502 in_param
.type
= cert_pi_end
;
503 cvin
->push_back(in_param
);
504 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
505 &(*cvin
)[0], cvout
, NULL
);
506 if (rv
!= SECSuccess
) {
507 // Use the original error code.
508 PORT_SetError(nss_error
);
516 // Decodes the certificatePolicies extension of the certificate. Returns
517 // NULL if the certificate doesn't have the extension or the extension can't
518 // be decoded. The returned value must be freed with a
519 // CERT_DestroyCertificatePoliciesExtension call.
520 CERTCertificatePolicies
* DecodeCertPolicies(
521 CERTCertificate
* cert_handle
) {
523 SECStatus rv
= CERT_FindCertExtension(cert_handle
,
524 SEC_OID_X509_CERTIFICATE_POLICIES
,
526 if (rv
!= SECSuccess
)
528 CERTCertificatePolicies
* policies
=
529 CERT_DecodeCertificatePoliciesExtension(&policy_ext
);
530 SECITEM_FreeItem(&policy_ext
, PR_FALSE
);
534 // Returns the OID tag for the first certificate policy in the certificate's
535 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate
536 // has no certificate policy.
537 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
) {
538 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
540 return SEC_OID_UNKNOWN
;
542 CERTPolicyInfo
* policy_info
= policies
->policyInfos
[0];
544 return SEC_OID_UNKNOWN
;
545 if (policy_info
->oid
!= SEC_OID_UNKNOWN
)
546 return policy_info
->oid
;
548 // The certificate policy is unknown to NSS. We need to create a dynamic
549 // OID tag for the policy.
551 od
.oid
.len
= policy_info
->policyID
.len
;
552 od
.oid
.data
= policy_info
->policyID
.data
;
553 od
.offset
= SEC_OID_UNKNOWN
;
554 // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
555 // default description here. The description doesn't need to be unique for
557 od
.desc
= "a certificate policy";
558 od
.mechanism
= CKM_INVALID_MECHANISM
;
559 od
.supportedExtension
= INVALID_CERT_EXTENSION
;
560 return SECOID_AddEntry(&od
);
563 HashValue
CertPublicKeyHashSHA1(CERTCertificate
* cert
) {
564 HashValue
hash(HASH_VALUE_SHA1
);
566 CC_SHA1(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
568 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA1
, hash
.data(),
569 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
570 DCHECK_EQ(SECSuccess
, rv
);
575 HashValue
CertPublicKeyHashSHA256(CERTCertificate
* cert
) {
576 HashValue
hash(HASH_VALUE_SHA256
);
578 CC_SHA256(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
580 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA256
, hash
.data(),
581 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
582 DCHECK_EQ(rv
, SECSuccess
);
587 void AppendPublicKeyHashes(CERTCertList
* cert_list
,
588 CERTCertificate
* root_cert
,
589 HashValueVector
* hashes
) {
590 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
591 !CERT_LIST_END(node
, cert_list
);
592 node
= CERT_LIST_NEXT(node
)) {
593 hashes
->push_back(CertPublicKeyHashSHA1(node
->cert
));
594 hashes
->push_back(CertPublicKeyHashSHA256(node
->cert
));
597 hashes
->push_back(CertPublicKeyHashSHA1(root_cert
));
598 hashes
->push_back(CertPublicKeyHashSHA256(root_cert
));
602 // Returns true if |cert_handle| contains a policy OID that is an EV policy
603 // OID according to |metadata|, storing the resulting policy OID in
604 // |*ev_policy_oid|. A true return is not sufficient to establish that a
605 // certificate is EV, but a false return is sufficient to establish the
606 // certificate cannot be EV.
607 bool IsEVCandidate(EVRootCAMetadata
* metadata
,
608 CERTCertificate
* cert_handle
,
609 SECOidTag
* ev_policy_oid
) {
611 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
615 CERTPolicyInfo
** policy_infos
= policies
->policyInfos
;
616 while (*policy_infos
!= NULL
) {
617 CERTPolicyInfo
* policy_info
= *policy_infos
++;
618 // If the Policy OID is unknown, that implicitly means it has not been
619 // registered as an EV policy.
620 if (policy_info
->oid
== SEC_OID_UNKNOWN
)
622 if (metadata
->IsEVPolicyOID(policy_info
->oid
)) {
623 *ev_policy_oid
= policy_info
->oid
;
631 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp
632 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate.
633 // TODO(wtc): A possible optimization is that we get the trust anchor from
634 // the first PKIXVerifyCert call. We look up the EV policy for the trust
635 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV.
636 // Otherwise, we pass just that EV policy (as opposed to all the EV policies)
637 // to the second PKIXVerifyCert call.
638 bool VerifyEV(CERTCertificate
* cert_handle
,
641 EVRootCAMetadata
* metadata
,
642 SECOidTag ev_policy_oid
) {
643 CERTValOutParam cvout
[3];
645 cvout
[cvout_index
].type
= cert_po_certList
;
646 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
647 int cvout_cert_list_index
= cvout_index
;
649 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
650 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
651 int cvout_trust_anchor_index
= cvout_index
;
653 cvout
[cvout_index
].type
= cert_po_end
;
654 ScopedCERTValOutParam
scoped_cvout(cvout
);
656 bool rev_checking_enabled
=
657 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED
) ||
658 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY
);
660 SECStatus status
= PKIXVerifyCert(
662 rev_checking_enabled
,
663 flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
,
667 if (status
!= SECSuccess
)
670 CERTCertificate
* root_ca
=
671 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
;
675 // This second PKIXVerifyCert call could have found a different certification
676 // path and one or more of the certificates on this new path, that weren't on
677 // the old path, might have been revoked.
679 CRLSetResult crl_set_result
= CheckRevocationWithCRLSet(
680 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
681 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
683 if (crl_set_result
== kCRLSetRevoked
)
688 SHA1HashValue fingerprint
= x509_util_ios::CalculateFingerprintNSS(root_ca
);
690 SHA1HashValue fingerprint
=
691 X509Certificate::CalculateFingerprint(root_ca
);
693 return metadata
->HasEVPolicyOID(fingerprint
, ev_policy_oid
);
698 CertVerifyProcNSS::CertVerifyProcNSS() {}
700 CertVerifyProcNSS::~CertVerifyProcNSS() {}
702 int CertVerifyProcNSS::VerifyInternal(X509Certificate
* cert
,
703 const std::string
& hostname
,
706 CertVerifyResult
* verify_result
) {
708 // For iOS, the entire chain must be loaded into NSS's in-memory certificate
710 x509_util_ios::NSSCertChain
scoped_chain(cert
);
711 CERTCertificate
* cert_handle
= scoped_chain
.cert_handle();
713 CERTCertificate
* cert_handle
= cert
->os_cert_handle();
714 #endif // defined(OS_IOS)
716 // Make sure that the hostname matches with the common name of the cert.
717 SECStatus status
= CERT_VerifyCertName(cert_handle
, hostname
.c_str());
718 if (status
!= SECSuccess
)
719 verify_result
->cert_status
|= CERT_STATUS_COMMON_NAME_INVALID
;
721 // Make sure that the cert is valid now.
722 SECCertTimeValidity validity
= CERT_CheckCertValidTimes(
723 cert_handle
, PR_Now(), PR_TRUE
);
724 if (validity
!= secCertTimeValid
)
725 verify_result
->cert_status
|= CERT_STATUS_DATE_INVALID
;
727 CERTValOutParam cvout
[3];
729 cvout
[cvout_index
].type
= cert_po_certList
;
730 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
731 int cvout_cert_list_index
= cvout_index
;
733 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
734 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
735 int cvout_trust_anchor_index
= cvout_index
;
737 cvout
[cvout_index
].type
= cert_po_end
;
738 ScopedCERTValOutParam
scoped_cvout(cvout
);
740 EVRootCAMetadata
* metadata
= EVRootCAMetadata::GetInstance();
741 SECOidTag ev_policy_oid
= SEC_OID_UNKNOWN
;
742 bool is_ev_candidate
=
743 (flags
& CertVerifier::VERIFY_EV_CERT
) &&
744 IsEVCandidate(metadata
, cert_handle
, &ev_policy_oid
);
745 bool cert_io_enabled
= flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
;
746 bool check_revocation
=
748 ((flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED
) ||
749 ((flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY
) &&
751 if (check_revocation
)
752 verify_result
->cert_status
|= CERT_STATUS_REV_CHECKING_ENABLED
;
754 status
= PKIXVerifyCert(cert_handle
, check_revocation
, cert_io_enabled
,
757 if (status
== SECSuccess
) {
758 AppendPublicKeyHashes(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
759 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
760 &verify_result
->public_key_hashes
);
762 verify_result
->is_issued_by_known_root
=
763 IsKnownRoot(cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
);
765 GetCertChainInfo(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
766 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
771 CRLSetResult crl_set_result
= CheckRevocationWithCRLSet(
772 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
773 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
775 if (crl_set_result
== kCRLSetRevoked
) {
776 PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE
);
781 if (status
!= SECSuccess
) {
782 int err
= PORT_GetError();
783 LOG(ERROR
) << "CERT_PKIXVerifyCert for " << hostname
784 << " failed err=" << err
;
785 // CERT_PKIXVerifyCert rerports the wrong error code for
786 // expired certificates (NSS bug 491174)
787 if (err
== SEC_ERROR_CERT_NOT_VALID
&&
788 (verify_result
->cert_status
& CERT_STATUS_DATE_INVALID
))
789 err
= SEC_ERROR_EXPIRED_CERTIFICATE
;
790 CertStatus cert_status
= MapCertErrorToCertStatus(err
);
792 verify_result
->cert_status
|= cert_status
;
793 return MapCertStatusToNetError(verify_result
->cert_status
);
795 // |err| is not a certificate error.
796 return MapSecurityError(err
);
799 if (IsCertStatusError(verify_result
->cert_status
))
800 return MapCertStatusToNetError(verify_result
->cert_status
);
802 if ((flags
& CertVerifier::VERIFY_EV_CERT
) && is_ev_candidate
&&
803 VerifyEV(cert_handle
, flags
, crl_set
, metadata
, ev_policy_oid
)) {
804 verify_result
->cert_status
|= CERT_STATUS_IS_EV
;