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/cert/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/net_errors.h"
22 #include "net/cert/asn1_util.h"
23 #include "net/cert/cert_status_flags.h"
24 #include "net/cert/cert_verifier.h"
25 #include "net/cert/cert_verify_result.h"
26 #include "net/cert/crl_set.h"
27 #include "net/cert/ev_root_ca_metadata.h"
28 #include "net/cert/x509_certificate.h"
29 #include "net/cert/x509_util_nss.h"
32 #include <CommonCrypto/CommonDigest.h>
33 #include "net/cert/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
42 #if NSS_VERSION_NUM < 31402
43 // Added in NSS 3.14.2.
44 #define cert_pi_useOnlyTrustAnchors static_cast<CERTValParamInType>(14)
51 typedef scoped_ptr_malloc
<
52 CERTCertificatePolicies
,
53 crypto::NSSDestroyer
<CERTCertificatePolicies
,
54 CERT_DestroyCertificatePoliciesExtension
> >
55 ScopedCERTCertificatePolicies
;
57 typedef scoped_ptr_malloc
<
59 crypto::NSSDestroyer
<CERTCertList
, CERT_DestroyCertList
> >
62 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam
63 // array that cvout points to. cvout must be initialized as passed to
64 // CERT_PKIXVerifyCert, so that the array must be terminated with
66 // When it goes out of scope, it destroys values of cert_po_trustAnchor
67 // and cert_po_certList types, but doesn't release the array itself.
68 class ScopedCERTValOutParam
{
70 explicit ScopedCERTValOutParam(CERTValOutParam
* cvout
)
73 ~ScopedCERTValOutParam() {
76 for (CERTValOutParam
*p
= cvout_
; p
->type
!= cert_po_end
; p
++) {
78 case cert_po_trustAnchor
:
79 if (p
->value
.pointer
.cert
) {
80 CERT_DestroyCertificate(p
->value
.pointer
.cert
);
81 p
->value
.pointer
.cert
= NULL
;
84 case cert_po_certList
:
85 if (p
->value
.pointer
.chain
) {
86 CERT_DestroyCertList(p
->value
.pointer
.chain
);
87 p
->value
.pointer
.chain
= NULL
;
97 CERTValOutParam
* cvout_
;
99 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam
);
102 // Map PORT_GetError() return values to our network error codes.
103 int MapSecurityError(int err
) {
105 case PR_DIRECTORY_LOOKUP_ERROR
: // DNS lookup error.
106 return ERR_NAME_NOT_RESOLVED
;
107 case SEC_ERROR_INVALID_ARGS
:
108 return ERR_INVALID_ARGUMENT
;
109 case SSL_ERROR_BAD_CERT_DOMAIN
:
110 return ERR_CERT_COMMON_NAME_INVALID
;
111 case SEC_ERROR_INVALID_TIME
:
112 case SEC_ERROR_EXPIRED_CERTIFICATE
:
113 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
:
114 return ERR_CERT_DATE_INVALID
;
115 case SEC_ERROR_UNKNOWN_ISSUER
:
116 case SEC_ERROR_UNTRUSTED_ISSUER
:
117 case SEC_ERROR_CA_CERT_INVALID
:
118 return ERR_CERT_AUTHORITY_INVALID
;
119 // TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
120 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
:
121 case SEC_ERROR_OCSP_SERVER_ERROR
:
122 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION
;
123 case SEC_ERROR_REVOKED_CERTIFICATE
:
124 case SEC_ERROR_UNTRUSTED_CERT
: // Treat as revoked.
125 return ERR_CERT_REVOKED
;
126 case SEC_ERROR_BAD_DER
:
127 case SEC_ERROR_BAD_SIGNATURE
:
128 case SEC_ERROR_CERT_NOT_VALID
:
129 // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
130 case SEC_ERROR_CERT_USAGES_INVALID
:
131 case SEC_ERROR_INADEQUATE_KEY_USAGE
: // Key usage.
132 case SEC_ERROR_INADEQUATE_CERT_TYPE
: // Extended key usage and whether
133 // the certificate is a CA.
134 case SEC_ERROR_POLICY_VALIDATION_FAILED
:
135 case SEC_ERROR_CERT_NOT_IN_NAME_SPACE
:
136 case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID
:
137 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
:
138 case SEC_ERROR_EXTENSION_VALUE_INVALID
:
139 return ERR_CERT_INVALID
;
140 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
:
141 return ERR_CERT_WEAK_SIGNATURE_ALGORITHM
;
143 LOG(WARNING
) << "Unknown error " << err
<< " mapped to net::ERR_FAILED";
148 // Map PORT_GetError() return values to our cert status flags.
149 CertStatus
MapCertErrorToCertStatus(int err
) {
150 int net_error
= MapSecurityError(err
);
151 return MapNetErrorToCertStatus(net_error
);
154 // Saves some information about the certificate chain cert_list in
155 // *verify_result. The caller MUST initialize *verify_result before calling
157 // Note that cert_list[0] is the end entity certificate.
158 void GetCertChainInfo(CERTCertList
* cert_list
,
159 CERTCertificate
* root_cert
,
160 CertVerifyResult
* verify_result
) {
161 // NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the
162 // NSS version currently in use:
163 // 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*)
164 // 2. use ident libnss3.so* for the library's version
167 CERTCertificate
* verified_cert
= NULL
;
168 std::vector
<CERTCertificate
*> verified_chain
;
170 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
171 !CERT_LIST_END(node
, cert_list
);
172 node
= CERT_LIST_NEXT(node
), ++i
) {
174 verified_cert
= node
->cert
;
176 // Because of an NSS bug, CERT_PKIXVerifyCert may chain a self-signed
177 // certificate of a root CA to another certificate of the same root CA
178 // key. Detect that error and ignore the root CA certificate.
179 // See https://bugzilla.mozilla.org/show_bug.cgi?id=721288.
180 if (node
->cert
->isRoot
) {
181 // NOTE: isRoot doesn't mean the certificate is a trust anchor. It
182 // means the certificate is self-signed. Here we assume isRoot only
183 // implies the certificate is self-issued.
184 CERTCertListNode
* next_node
= CERT_LIST_NEXT(node
);
185 CERTCertificate
* next_cert
;
186 if (!CERT_LIST_END(next_node
, cert_list
)) {
187 next_cert
= next_node
->cert
;
189 next_cert
= root_cert
;
191 // Test that |node->cert| is actually a self-signed certificate
192 // whose key is equal to |next_cert|, and not a self-issued
193 // certificate signed by another key of the same CA.
194 if (next_cert
&& SECITEM_ItemsAreEqual(&node
->cert
->derPublicKey
,
195 &next_cert
->derPublicKey
)) {
199 verified_chain
.push_back(node
->cert
);
202 SECAlgorithmID
& signature
= node
->cert
->signature
;
203 SECOidTag oid_tag
= SECOID_FindOIDTag(&signature
.algorithm
);
205 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
:
206 verify_result
->has_md5
= true;
208 verify_result
->has_md5_ca
= true;
210 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION
:
211 verify_result
->has_md2
= true;
213 verify_result
->has_md2_ca
= true;
215 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION
:
216 verify_result
->has_md4
= true;
224 verified_chain
.push_back(root_cert
);
226 verify_result
->verified_cert
=
227 x509_util_ios::CreateCertFromNSSHandles(verified_cert
, verified_chain
);
229 verify_result
->verified_cert
=
230 X509Certificate::CreateFromHandle(verified_cert
, verified_chain
);
231 #endif // defined(OS_IOS)
234 // IsKnownRoot returns true if the given certificate is one that we believe
235 // is a standard (as opposed to user-installed) root.
236 bool IsKnownRoot(CERTCertificate
* root
) {
237 if (!root
|| !root
->slot
)
240 // This magic name is taken from
241 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
242 return 0 == strcmp(PK11_GetSlotName(root
->slot
),
243 "NSS Builtin Objects");
246 // Returns true if the given certificate is one of the additional trust anchors.
247 bool IsAdditionalTrustAnchor(CERTCertList
* additional_trust_anchors
,
248 CERTCertificate
* root
) {
249 if (!additional_trust_anchors
|| !root
)
251 for (CERTCertListNode
* node
= CERT_LIST_HEAD(additional_trust_anchors
);
252 !CERT_LIST_END(node
, additional_trust_anchors
);
253 node
= CERT_LIST_NEXT(node
)) {
254 if (CERT_CompareCerts(node
->cert
, root
))
266 // CheckRevocationWithCRLSet attempts to check each element of |cert_list|
267 // against |crl_set|. It returns:
268 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
269 // kCRLSetError: if an error occurs in processing.
270 // kCRLSetOk: if no element in the chain is known to have been revoked.
271 CRLSetResult
CheckRevocationWithCRLSet(CERTCertList
* cert_list
,
272 CERTCertificate
* root
,
274 std::vector
<CERTCertificate
*> certs
;
277 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
278 !CERT_LIST_END(node
, cert_list
);
279 node
= CERT_LIST_NEXT(node
)) {
280 certs
.push_back(node
->cert
);
284 certs
.push_back(root
);
286 // We iterate from the root certificate down to the leaf, keeping track of
287 // the issuer's SPKI at each step.
288 std::string issuer_spki_hash
;
289 for (std::vector
<CERTCertificate
*>::reverse_iterator i
= certs
.rbegin();
290 i
!= certs
.rend(); ++i
) {
291 CERTCertificate
* cert
= *i
;
293 base::StringPiece
der(reinterpret_cast<char*>(cert
->derCert
.data
),
296 base::StringPiece spki
;
297 if (!asn1::ExtractSPKIFromDERCert(der
, &spki
)) {
301 const std::string spki_hash
= crypto::SHA256HashString(spki
);
303 base::StringPiece serial_number
= base::StringPiece(
304 reinterpret_cast<char*>(cert
->serialNumber
.data
),
305 cert
->serialNumber
.len
);
307 CRLSet::Result result
= crl_set
->CheckSPKI(spki_hash
);
309 if (result
!= CRLSet::REVOKED
&& !issuer_spki_hash
.empty())
310 result
= crl_set
->CheckSerial(serial_number
, issuer_spki_hash
);
312 issuer_spki_hash
= spki_hash
;
315 case CRLSet::REVOKED
:
316 return kCRLSetRevoked
;
317 case CRLSet::UNKNOWN
:
329 // Forward declarations.
330 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
331 CERTCertificate
* cert_handle
, int num_policy_oids
,
332 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
333 CERTValOutParam
* cvout
);
334 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
);
336 // Call CERT_PKIXVerifyCert for the cert_handle.
337 // Verification results are stored in an array of CERTValOutParam.
338 // If policy_oids is not NULL and num_policy_oids is positive, policies
340 // additional_trust_anchors is an optional list of certificates that can be
341 // trusted as anchors when building a certificate chain.
342 // Caller must initialize cvout before calling this function.
343 SECStatus
PKIXVerifyCert(CERTCertificate
* cert_handle
,
344 bool check_revocation
,
345 bool cert_io_enabled
,
346 const SECOidTag
* policy_oids
,
348 CERTCertList
* additional_trust_anchors
,
349 CERTValOutParam
* cvout
) {
350 bool use_crl
= check_revocation
;
351 bool use_ocsp
= check_revocation
;
353 // These CAs have multiple keys, which trigger two bugs in NSS's CRL code.
354 // 1. NSS may use one key to verify a CRL signed with another key,
355 // incorrectly concluding that the CRL's signature is invalid.
356 // Hopefully this bug will be fixed in NSS 3.12.9.
357 // 2. NSS considers all certificates issued by the CA as revoked when it
358 // receives a CRL with an invalid signature. This overly strict policy
359 // has been relaxed in NSS 3.12.7. See
360 // https://bugzilla.mozilla.org/show_bug.cgi?id=562542.
361 // So we have to turn off CRL checking for these CAs. See
362 // http://crbug.com/55695.
363 static const char* const kMultipleKeyCA
[] = {
364 "CN=Microsoft Secure Server Authority,"
365 "DC=redmond,DC=corp,DC=microsoft,DC=com",
366 "CN=Microsoft Secure Server Authority",
369 if (!NSS_VersionCheck("3.12.7")) {
370 for (size_t i
= 0; i
< arraysize(kMultipleKeyCA
); ++i
) {
371 if (strcmp(cert_handle
->issuerName
, kMultipleKeyCA
[i
]) == 0) {
378 PRUint64 revocation_method_flags
=
379 CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD
|
380 CERT_REV_M_ALLOW_NETWORK_FETCHING
|
381 CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
|
382 CERT_REV_M_IGNORE_MISSING_FRESH_INFO
|
383 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
;
384 PRUint64 revocation_method_independent_flags
=
385 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
;
386 if (check_revocation
&& policy_oids
&& num_policy_oids
> 0) {
387 // EV verification requires revocation checking. Consider the certificate
388 // revoked if we don't have revocation info.
389 // TODO(wtc): Add a bool parameter to expressly specify we're doing EV
390 // verification or we want strict revocation flags.
391 revocation_method_flags
|= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
;
392 revocation_method_independent_flags
|=
393 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
;
395 revocation_method_flags
|= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
;
396 revocation_method_independent_flags
|=
397 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT
;
399 PRUint64 method_flags
[2];
400 method_flags
[cert_revocation_method_crl
] = revocation_method_flags
;
401 method_flags
[cert_revocation_method_ocsp
] = revocation_method_flags
;
404 method_flags
[cert_revocation_method_crl
] |=
405 CERT_REV_M_TEST_USING_THIS_METHOD
;
408 method_flags
[cert_revocation_method_ocsp
] |=
409 CERT_REV_M_TEST_USING_THIS_METHOD
;
412 CERTRevocationMethodIndex preferred_revocation_methods
[1];
414 preferred_revocation_methods
[0] = cert_revocation_method_ocsp
;
416 preferred_revocation_methods
[0] = cert_revocation_method_crl
;
419 CERTRevocationFlags revocation_flags
;
420 revocation_flags
.leafTests
.number_of_defined_methods
=
421 arraysize(method_flags
);
422 revocation_flags
.leafTests
.cert_rev_flags_per_method
= method_flags
;
423 revocation_flags
.leafTests
.number_of_preferred_methods
=
424 arraysize(preferred_revocation_methods
);
425 revocation_flags
.leafTests
.preferred_methods
= preferred_revocation_methods
;
426 revocation_flags
.leafTests
.cert_rev_method_independent_flags
=
427 revocation_method_independent_flags
;
429 revocation_flags
.chainTests
.number_of_defined_methods
=
430 arraysize(method_flags
);
431 revocation_flags
.chainTests
.cert_rev_flags_per_method
= method_flags
;
432 revocation_flags
.chainTests
.number_of_preferred_methods
=
433 arraysize(preferred_revocation_methods
);
434 revocation_flags
.chainTests
.preferred_methods
= preferred_revocation_methods
;
435 revocation_flags
.chainTests
.cert_rev_method_independent_flags
=
436 revocation_method_independent_flags
;
439 std::vector
<CERTValInParam
> cvin
;
441 CERTValInParam in_param
;
442 in_param
.type
= cert_pi_revocationFlags
;
443 in_param
.value
.pointer
.revocation
= &revocation_flags
;
444 cvin
.push_back(in_param
);
445 if (policy_oids
&& num_policy_oids
> 0) {
446 in_param
.type
= cert_pi_policyOID
;
447 in_param
.value
.arraySize
= num_policy_oids
;
448 in_param
.value
.array
.oids
= policy_oids
;
449 cvin
.push_back(in_param
);
451 if (additional_trust_anchors
) {
452 in_param
.type
= cert_pi_trustAnchors
;
453 in_param
.value
.pointer
.chain
= additional_trust_anchors
;
454 cvin
.push_back(in_param
);
455 in_param
.type
= cert_pi_useOnlyTrustAnchors
;
456 in_param
.value
.scalar
.b
= PR_FALSE
;
457 cvin
.push_back(in_param
);
459 in_param
.type
= cert_pi_end
;
460 cvin
.push_back(in_param
);
462 SECStatus rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
463 &cvin
[0], cvout
, NULL
);
464 if (rv
!= SECSuccess
) {
465 rv
= RetryPKIXVerifyCertWithWorkarounds(cert_handle
, num_policy_oids
,
466 cert_io_enabled
, &cvin
, cvout
);
471 // PKIXVerifyCert calls this function to work around some bugs in
472 // CERT_PKIXVerifyCert. All the arguments of this function are either the
473 // arguments or local variables of PKIXVerifyCert.
474 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
475 CERTCertificate
* cert_handle
, int num_policy_oids
,
476 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
477 CERTValOutParam
* cvout
) {
478 // We call this function when the first CERT_PKIXVerifyCert call in
479 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure.
480 SECStatus rv
= SECFailure
;
481 int nss_error
= PORT_GetError();
482 CERTValInParam in_param
;
484 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
485 // CA certificate, so we retry with cert_pi_useAIACertFetch.
486 // cert_pi_useAIACertFetch has several bugs in its error handling and
487 // error reporting (NSS bug 528743), so we don't use it by default.
488 // Note: When building a certificate chain, CERT_PKIXVerifyCert may
489 // incorrectly pick a CA certificate with the same subject name as the
490 // missing intermediate CA certificate, and fail with the
491 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
492 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
493 if (cert_io_enabled
&&
494 (nss_error
== SEC_ERROR_UNKNOWN_ISSUER
||
495 nss_error
== SEC_ERROR_BAD_SIGNATURE
)) {
496 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
498 in_param
.type
= cert_pi_useAIACertFetch
;
499 in_param
.value
.scalar
.b
= PR_TRUE
;
500 cvin
->push_back(in_param
);
501 in_param
.type
= cert_pi_end
;
502 cvin
->push_back(in_param
);
503 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
504 &(*cvin
)[0], cvout
, NULL
);
505 if (rv
== SECSuccess
)
507 int new_nss_error
= PORT_GetError();
508 if (new_nss_error
== SEC_ERROR_INVALID_ARGS
||
509 new_nss_error
== SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE
||
510 new_nss_error
== SEC_ERROR_BAD_INFO_ACCESS_LOCATION
||
511 new_nss_error
== SEC_ERROR_BAD_HTTP_RESPONSE
||
512 new_nss_error
== SEC_ERROR_BAD_LDAP_RESPONSE
||
513 !IS_SEC_ERROR(new_nss_error
)) {
514 // Use the original error code because of cert_pi_useAIACertFetch's
515 // bad error reporting.
516 PORT_SetError(nss_error
);
519 nss_error
= new_nss_error
;
522 // If an intermediate CA certificate has requireExplicitPolicy in its
523 // policyConstraints extension, CERT_PKIXVerifyCert fails with
524 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
525 // certificate policy (NSS bug 552775). So we retry with the certificate
526 // policy found in the server certificate.
527 if (nss_error
== SEC_ERROR_POLICY_VALIDATION_FAILED
&&
528 num_policy_oids
== 0) {
529 SECOidTag policy
= GetFirstCertPolicy(cert_handle
);
530 if (policy
!= SEC_OID_UNKNOWN
) {
531 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
533 in_param
.type
= cert_pi_policyOID
;
534 in_param
.value
.arraySize
= 1;
535 in_param
.value
.array
.oids
= &policy
;
536 cvin
->push_back(in_param
);
537 in_param
.type
= cert_pi_end
;
538 cvin
->push_back(in_param
);
539 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
540 &(*cvin
)[0], cvout
, NULL
);
541 if (rv
!= SECSuccess
) {
542 // Use the original error code.
543 PORT_SetError(nss_error
);
551 // Decodes the certificatePolicies extension of the certificate. Returns
552 // NULL if the certificate doesn't have the extension or the extension can't
553 // be decoded. The returned value must be freed with a
554 // CERT_DestroyCertificatePoliciesExtension call.
555 CERTCertificatePolicies
* DecodeCertPolicies(
556 CERTCertificate
* cert_handle
) {
558 SECStatus rv
= CERT_FindCertExtension(cert_handle
,
559 SEC_OID_X509_CERTIFICATE_POLICIES
,
561 if (rv
!= SECSuccess
)
563 CERTCertificatePolicies
* policies
=
564 CERT_DecodeCertificatePoliciesExtension(&policy_ext
);
565 SECITEM_FreeItem(&policy_ext
, PR_FALSE
);
569 // Returns the OID tag for the first certificate policy in the certificate's
570 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate
571 // has no certificate policy.
572 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
) {
573 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
575 return SEC_OID_UNKNOWN
;
577 CERTPolicyInfo
* policy_info
= policies
->policyInfos
[0];
579 return SEC_OID_UNKNOWN
;
580 if (policy_info
->oid
!= SEC_OID_UNKNOWN
)
581 return policy_info
->oid
;
583 // The certificate policy is unknown to NSS. We need to create a dynamic
584 // OID tag for the policy.
586 od
.oid
.len
= policy_info
->policyID
.len
;
587 od
.oid
.data
= policy_info
->policyID
.data
;
588 od
.offset
= SEC_OID_UNKNOWN
;
589 // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
590 // default description here. The description doesn't need to be unique for
592 od
.desc
= "a certificate policy";
593 od
.mechanism
= CKM_INVALID_MECHANISM
;
594 od
.supportedExtension
= INVALID_CERT_EXTENSION
;
595 return SECOID_AddEntry(&od
);
598 HashValue
CertPublicKeyHashSHA1(CERTCertificate
* cert
) {
599 HashValue
hash(HASH_VALUE_SHA1
);
601 CC_SHA1(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
603 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA1
, hash
.data(),
604 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
605 DCHECK_EQ(SECSuccess
, rv
);
610 HashValue
CertPublicKeyHashSHA256(CERTCertificate
* cert
) {
611 HashValue
hash(HASH_VALUE_SHA256
);
613 CC_SHA256(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
615 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA256
, hash
.data(),
616 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
617 DCHECK_EQ(rv
, SECSuccess
);
622 void AppendPublicKeyHashes(CERTCertList
* cert_list
,
623 CERTCertificate
* root_cert
,
624 HashValueVector
* hashes
) {
625 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
626 !CERT_LIST_END(node
, cert_list
);
627 node
= CERT_LIST_NEXT(node
)) {
628 hashes
->push_back(CertPublicKeyHashSHA1(node
->cert
));
629 hashes
->push_back(CertPublicKeyHashSHA256(node
->cert
));
632 hashes
->push_back(CertPublicKeyHashSHA1(root_cert
));
633 hashes
->push_back(CertPublicKeyHashSHA256(root_cert
));
637 // Returns true if |cert_handle| contains a policy OID that is an EV policy
638 // OID according to |metadata|, storing the resulting policy OID in
639 // |*ev_policy_oid|. A true return is not sufficient to establish that a
640 // certificate is EV, but a false return is sufficient to establish the
641 // certificate cannot be EV.
642 bool IsEVCandidate(EVRootCAMetadata
* metadata
,
643 CERTCertificate
* cert_handle
,
644 SECOidTag
* ev_policy_oid
) {
646 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
650 CERTPolicyInfo
** policy_infos
= policies
->policyInfos
;
651 while (*policy_infos
!= NULL
) {
652 CERTPolicyInfo
* policy_info
= *policy_infos
++;
653 // If the Policy OID is unknown, that implicitly means it has not been
654 // registered as an EV policy.
655 if (policy_info
->oid
== SEC_OID_UNKNOWN
)
657 if (metadata
->IsEVPolicyOID(policy_info
->oid
)) {
658 *ev_policy_oid
= policy_info
->oid
;
666 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp
667 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate.
668 // TODO(wtc): A possible optimization is that we get the trust anchor from
669 // the first PKIXVerifyCert call. We look up the EV policy for the trust
670 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV.
671 // Otherwise, we pass just that EV policy (as opposed to all the EV policies)
672 // to the second PKIXVerifyCert call.
673 bool VerifyEV(CERTCertificate
* cert_handle
,
676 EVRootCAMetadata
* metadata
,
677 SECOidTag ev_policy_oid
,
678 CERTCertList
* additional_trust_anchors
) {
679 CERTValOutParam cvout
[3];
681 cvout
[cvout_index
].type
= cert_po_certList
;
682 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
683 int cvout_cert_list_index
= cvout_index
;
685 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
686 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
687 int cvout_trust_anchor_index
= cvout_index
;
689 cvout
[cvout_index
].type
= cert_po_end
;
690 ScopedCERTValOutParam
scoped_cvout(cvout
);
692 bool rev_checking_enabled
=
693 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED
) ||
694 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY
);
696 SECStatus status
= PKIXVerifyCert(
698 rev_checking_enabled
,
699 flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
,
702 additional_trust_anchors
,
704 if (status
!= SECSuccess
)
707 CERTCertificate
* root_ca
=
708 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
;
712 // This second PKIXVerifyCert call could have found a different certification
713 // path and one or more of the certificates on this new path, that weren't on
714 // the old path, might have been revoked.
716 CRLSetResult crl_set_result
= CheckRevocationWithCRLSet(
717 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
718 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
720 if (crl_set_result
== kCRLSetRevoked
)
725 SHA1HashValue fingerprint
= x509_util_ios::CalculateFingerprintNSS(root_ca
);
727 SHA1HashValue fingerprint
=
728 X509Certificate::CalculateFingerprint(root_ca
);
730 return metadata
->HasEVPolicyOID(fingerprint
, ev_policy_oid
);
733 CERTCertList
* CertificateListToCERTCertList(const CertificateList
& list
) {
734 CERTCertList
* result
= CERT_NewCertList();
735 for (size_t i
= 0; i
< list
.size(); ++i
) {
737 // X509Certificate::os_cert_handle() on iOS is a SecCertificateRef; convert
738 // it to an NSS CERTCertificate.
739 CERTCertificate
* cert
= x509_util_ios::CreateNSSCertHandleFromOSHandle(
740 list
[i
]->os_cert_handle());
742 CERTCertificate
* cert
= list
[i
]->os_cert_handle();
744 CERT_AddCertToListTail(result
, CERT_DupCertificate(cert
));
751 CertVerifyProcNSS::CertVerifyProcNSS() {}
753 CertVerifyProcNSS::~CertVerifyProcNSS() {}
755 bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
756 // This requires APIs introduced in 3.14.2.
757 return NSS_VersionCheck("3.14.2");
760 int CertVerifyProcNSS::VerifyInternal(
761 X509Certificate
* cert
,
762 const std::string
& hostname
,
765 const CertificateList
& additional_trust_anchors
,
766 CertVerifyResult
* verify_result
) {
768 // For iOS, the entire chain must be loaded into NSS's in-memory certificate
770 x509_util_ios::NSSCertChain
scoped_chain(cert
);
771 CERTCertificate
* cert_handle
= scoped_chain
.cert_handle();
773 CERTCertificate
* cert_handle
= cert
->os_cert_handle();
774 #endif // defined(OS_IOS)
776 // Make sure that the hostname matches with the common name of the cert.
777 SECStatus status
= CERT_VerifyCertName(cert_handle
, hostname
.c_str());
778 if (status
!= SECSuccess
)
779 verify_result
->cert_status
|= CERT_STATUS_COMMON_NAME_INVALID
;
781 // Make sure that the cert is valid now.
782 SECCertTimeValidity validity
= CERT_CheckCertValidTimes(
783 cert_handle
, PR_Now(), PR_TRUE
);
784 if (validity
!= secCertTimeValid
)
785 verify_result
->cert_status
|= CERT_STATUS_DATE_INVALID
;
787 CERTValOutParam cvout
[3];
789 cvout
[cvout_index
].type
= cert_po_certList
;
790 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
791 int cvout_cert_list_index
= cvout_index
;
793 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
794 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
795 int cvout_trust_anchor_index
= cvout_index
;
797 cvout
[cvout_index
].type
= cert_po_end
;
798 ScopedCERTValOutParam
scoped_cvout(cvout
);
800 EVRootCAMetadata
* metadata
= EVRootCAMetadata::GetInstance();
801 SECOidTag ev_policy_oid
= SEC_OID_UNKNOWN
;
802 bool is_ev_candidate
=
803 (flags
& CertVerifier::VERIFY_EV_CERT
) &&
804 IsEVCandidate(metadata
, cert_handle
, &ev_policy_oid
);
805 bool cert_io_enabled
= flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
;
806 bool check_revocation
=
808 ((flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED
) ||
809 ((flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY
) &&
811 if (check_revocation
)
812 verify_result
->cert_status
|= CERT_STATUS_REV_CHECKING_ENABLED
;
814 ScopedCERTCertList trust_anchors
;
815 if (SupportsAdditionalTrustAnchors() && !additional_trust_anchors
.empty()) {
817 CertificateListToCERTCertList(additional_trust_anchors
));
820 status
= PKIXVerifyCert(cert_handle
, check_revocation
, cert_io_enabled
,
821 NULL
, 0, trust_anchors
.get(), cvout
);
823 if (status
== SECSuccess
) {
824 AppendPublicKeyHashes(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
825 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
826 &verify_result
->public_key_hashes
);
828 verify_result
->is_issued_by_known_root
=
829 IsKnownRoot(cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
);
830 verify_result
->is_issued_by_additional_trust_anchor
=
831 IsAdditionalTrustAnchor(
833 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
);
835 GetCertChainInfo(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
836 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
841 CRLSetResult crl_set_result
= CheckRevocationWithCRLSet(
842 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
843 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
845 if (crl_set_result
== kCRLSetRevoked
) {
846 PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE
);
851 if (status
!= SECSuccess
) {
852 int err
= PORT_GetError();
853 LOG(ERROR
) << "CERT_PKIXVerifyCert for " << hostname
854 << " failed err=" << err
;
855 // CERT_PKIXVerifyCert rerports the wrong error code for
856 // expired certificates (NSS bug 491174)
857 if (err
== SEC_ERROR_CERT_NOT_VALID
&&
858 (verify_result
->cert_status
& CERT_STATUS_DATE_INVALID
))
859 err
= SEC_ERROR_EXPIRED_CERTIFICATE
;
860 CertStatus cert_status
= MapCertErrorToCertStatus(err
);
862 verify_result
->cert_status
|= cert_status
;
863 return MapCertStatusToNetError(verify_result
->cert_status
);
865 // |err| is not a certificate error.
866 return MapSecurityError(err
);
869 if (IsCertStatusError(verify_result
->cert_status
))
870 return MapCertStatusToNetError(verify_result
->cert_status
);
872 if ((flags
& CertVerifier::VERIFY_EV_CERT
) && is_ev_candidate
&&
873 VerifyEV(cert_handle
, flags
, crl_set
, metadata
, ev_policy_oid
,
874 trust_anchors
.get())) {
875 verify_result
->cert_status
|= CERT_STATUS_IS_EV
;