2011-04-19 09:57:58 -04:00
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
2018-12-04 22:23:22 -05:00
"crypto"
2025-09-11 16:27:04 -04:00
"crypto/dsa"
2018-12-04 22:23:22 -05:00
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
2011-10-14 15:06:54 -04:00
"crypto/x509/pkix"
2022-05-02 12:00:36 -07:00
"encoding/asn1"
2011-04-19 09:57:58 -04:00
"encoding/pem"
2011-11-01 22:04:37 -04:00
"errors"
2016-08-28 14:11:31 -05:00
"fmt"
2021-12-08 16:47:56 -05:00
"internal/testenv"
2018-12-04 22:23:22 -05:00
"math/big"
2024-11-16 11:17:54 -08:00
"os"
2023-12-12 09:28:03 -08:00
"os/exec"
2012-03-07 13:12:35 -05:00
"runtime"
2024-05-22 13:38:40 -07:00
"slices"
2023-12-12 09:28:03 -08:00
"strconv"
2011-04-19 09:57:58 -04:00
"strings"
"testing"
2011-11-30 12:01:46 -05:00
"time"
2011-04-19 09:57:58 -04:00
)
type verifyTest struct {
2020-06-18 22:45:52 -04:00
name string
leaf string
intermediates [ ] string
roots [ ] string
currentTime int64
dnsName string
systemSkip bool
systemLax bool
keyUsages [ ] ExtKeyUsage
errorCallback func ( * testing . T , error )
2011-04-19 09:57:58 -04:00
expectedChains [ ] [ ] string
}
var verifyTests = [ ] verifyTest {
2013-01-21 11:25:28 -05:00
{
2020-06-18 22:45:52 -04:00
name : "Valid" ,
2011-04-19 09:57:58 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2011-04-19 09:57:58 -04:00
dnsName : "www.google.com" ,
2023-05-17 20:48:28 +00:00
expectedChains : [ ] [ ] string {
{ "www.google.com" , "GTS CA 1C3" , "GTS Root R1" } ,
} ,
} ,
{
name : "Valid (fqdn)" ,
leaf : googleLeaf ,
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
dnsName : "www.google.com." ,
2011-04-19 09:57:58 -04:00
expectedChains : [ ] [ ] string {
2023-02-28 13:23:43 -08:00
{ "www.google.com" , "GTS CA 1C3" , "GTS Root R1" } ,
2011-04-19 09:57:58 -04:00
} ,
} ,
2012-01-31 11:00:16 -05:00
{
2020-06-18 22:45:52 -04:00
name : "MixedCase" ,
2012-01-31 11:00:16 -05:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2012-01-31 11:00:16 -05:00
dnsName : "WwW.GooGLE.coM" ,
expectedChains : [ ] [ ] string {
2023-02-28 13:23:43 -08:00
{ "www.google.com" , "GTS CA 1C3" , "GTS Root R1" } ,
2012-01-31 11:00:16 -05:00
} ,
} ,
2011-04-19 09:57:58 -04:00
{
2020-06-18 22:45:52 -04:00
name : "HostnameMismatch" ,
2011-04-19 09:57:58 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2011-04-19 09:57:58 -04:00
dnsName : "www.example.com" ,
2018-07-11 15:59:56 -04:00
errorCallback : expectHostnameError ( "certificate is valid for" ) ,
} ,
{
2020-06-18 22:45:52 -04:00
name : "IPMissing" ,
2018-07-11 15:59:56 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2018-07-11 15:59:56 -04:00
dnsName : "1.2.3.4" ,
errorCallback : expectHostnameError ( "doesn't contain any IP SANs" ) ,
2011-04-19 09:57:58 -04:00
} ,
{
2020-06-18 22:45:52 -04:00
name : "Expired" ,
2011-04-19 09:57:58 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
2011-04-19 09:57:58 -04:00
currentTime : 1 ,
dnsName : "www.example.com" ,
errorCallback : expectExpired ,
} ,
{
2020-06-18 22:45:52 -04:00
name : "MissingIntermediate" ,
2011-04-19 09:57:58 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2011-04-19 09:57:58 -04:00
dnsName : "www.google.com" ,
2012-03-07 13:12:35 -05:00
// Skip when using systemVerify, since Windows
// *will* find the missing intermediate cert.
systemSkip : true ,
2011-04-19 09:57:58 -04:00
errorCallback : expectAuthorityUnknown ,
} ,
{
2020-06-18 22:45:52 -04:00
name : "RootInIntermediates" ,
2011-04-19 09:57:58 -04:00
leaf : googleLeaf ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsRoot , gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2011-04-19 09:57:58 -04:00
dnsName : "www.google.com" ,
expectedChains : [ ] [ ] string {
2023-02-28 13:23:43 -08:00
{ "www.google.com" , "GTS CA 1C3" , "GTS Root R1" } ,
2011-04-19 09:57:58 -04:00
} ,
2014-03-28 10:36:52 -04:00
// CAPI doesn't build the chain with the duplicated GeoTrust
2020-06-18 22:45:52 -04:00
// entry so the results don't match.
systemLax : true ,
} ,
2013-05-20 14:20:26 -04:00
{
2020-06-18 22:45:52 -04:00
name : "InvalidHash" ,
2013-05-20 14:20:26 -04:00
leaf : googleLeafWithInvalidHash ,
2023-02-28 13:23:43 -08:00
intermediates : [ ] string { gtsIntermediate } ,
roots : [ ] string { gtsRoot } ,
currentTime : 1677615892 ,
2013-05-20 14:20:26 -04:00
dnsName : "www.google.com" ,
// The specific error message may not occur when using system
// verification.
2020-06-18 22:45:52 -04:00
systemLax : true ,
2013-05-20 14:20:26 -04:00
errorCallback : expectHashError ,
} ,
2020-06-18 22:45:52 -04:00
// EKULeaf tests use an unconstrained chain leading to a leaf certificate
// with an E-mail Protection EKU but not a Server Auth one, checking that
// the EKUs on the leaf are enforced.
2012-06-20 16:18:56 -04:00
{
2020-06-18 22:45:52 -04:00
name : "EKULeaf" ,
leaf : smimeLeaf ,
intermediates : [ ] string { smimeIntermediate } ,
roots : [ ] string { smimeRoot } ,
currentTime : 1594673418 ,
2012-06-20 16:18:56 -04:00
errorCallback : expectUsageError ,
} ,
{
2020-06-18 22:45:52 -04:00
name : "EKULeafExplicit" ,
leaf : smimeLeaf ,
intermediates : [ ] string { smimeIntermediate } ,
roots : [ ] string { smimeRoot } ,
currentTime : 1594673418 ,
keyUsages : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
2012-06-20 16:18:56 -04:00
errorCallback : expectUsageError ,
} ,
{
2020-06-18 22:45:52 -04:00
name : "EKULeafValid" ,
leaf : smimeLeaf ,
intermediates : [ ] string { smimeIntermediate } ,
roots : [ ] string { smimeRoot } ,
currentTime : 1594673418 ,
keyUsages : [ ] ExtKeyUsage { ExtKeyUsageEmailProtection } ,
2012-06-20 16:18:56 -04:00
expectedChains : [ ] [ ] string {
2020-06-18 22:45:52 -04:00
{ "CORPORATIVO FICTICIO ACTIVO" , "EAEko Herri Administrazioen CA - CA AAPP Vascas (2)" , "IZENPE S.A." } ,
2012-06-20 16:18:56 -04:00
} ,
} ,
2013-10-21 19:01:24 -04:00
{
// Check that a name constrained intermediate works even when
// it lists multiple constraints.
2020-06-18 22:45:52 -04:00
name : "MultipleConstraints" ,
2013-10-21 19:01:24 -04:00
leaf : nameConstraintsLeaf ,
intermediates : [ ] string { nameConstraintsIntermediate1 , nameConstraintsIntermediate2 } ,
roots : [ ] string { globalSignRoot } ,
2024-11-19 11:34:19 -08:00
currentTime : 1524771953 ,
dnsName : "udctest.ads.vt.edu" ,
2013-10-21 19:01:24 -04:00
expectedChains : [ ] [ ] string {
{
2024-11-19 11:34:19 -08:00
"udctest.ads.vt.edu" ,
2013-10-21 19:01:24 -04:00
"Virginia Tech Global Qualified Server CA" ,
2024-11-19 11:34:19 -08:00
"Trusted Root CA SHA256 G2" ,
"GlobalSign" ,
2013-10-21 19:01:24 -04:00
} ,
} ,
} ,
2014-04-14 12:12:06 -07:00
{
// Check that SHA-384 intermediates (which are popping up)
// work.
2020-06-18 22:45:52 -04:00
name : "SHA-384" ,
2023-02-28 13:23:43 -08:00
leaf : trustAsiaLeaf ,
intermediates : [ ] string { trustAsiaSHA384Intermediate } ,
roots : [ ] string { digicertRoot } ,
currentTime : 1558051200 ,
dnsName : "tm.cn" ,
2014-04-14 12:12:06 -07:00
2020-06-18 22:45:52 -04:00
// CryptoAPI can find alternative validation paths.
systemLax : true ,
2015-07-22 13:11:54 -04:00
2014-04-14 12:12:06 -07:00
expectedChains : [ ] [ ] string {
{
2023-02-28 13:23:43 -08:00
"tm.cn" ,
"TrustAsia ECC OV TLS Pro CA" ,
"DigiCert Global Root CA" ,
2014-04-14 12:12:06 -07:00
} ,
} ,
} ,
2016-08-18 16:44:08 -07:00
{
// Putting a certificate as a root directly should work as a
// way of saying “exactly this”.
2020-06-18 22:45:52 -04:00
name : "LeafInRoots" ,
2016-08-18 16:44:08 -07:00
leaf : selfSigned ,
roots : [ ] string { selfSigned } ,
currentTime : 1471624472 ,
dnsName : "foo.example" ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2016-08-18 16:44:08 -07:00
expectedChains : [ ] [ ] string {
{ "Acme Co" } ,
} ,
} ,
{
// Putting a certificate as a root directly should not skip
// other checks however.
2020-06-18 22:45:52 -04:00
name : "LeafInRootsInvalid" ,
2016-08-18 16:44:08 -07:00
leaf : selfSigned ,
roots : [ ] string { selfSigned } ,
currentTime : 1471624472 ,
dnsName : "notfoo.example" ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2016-08-18 16:44:08 -07:00
2018-07-11 15:59:56 -04:00
errorCallback : expectHostnameError ( "certificate is valid for" ) ,
2016-08-18 16:44:08 -07:00
} ,
2016-12-14 14:10:26 -08:00
{
// An X.509 v1 certificate should not be accepted as an
// intermediate.
2020-06-18 22:45:52 -04:00
name : "X509v1Intermediate" ,
2016-12-14 14:10:26 -08:00
leaf : x509v1TestLeaf ,
intermediates : [ ] string { x509v1TestIntermediate } ,
roots : [ ] string { x509v1TestRoot } ,
currentTime : 1481753183 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2016-12-14 14:10:26 -08:00
errorCallback : expectNotAuthorizedError ,
} ,
2017-02-09 15:57:53 -08:00
{
2020-06-18 22:45:52 -04:00
name : "IgnoreCNWithSANs" ,
2017-02-09 15:57:53 -08:00
leaf : ignoreCNWithSANLeaf ,
dnsName : "foo.example.com" ,
roots : [ ] string { ignoreCNWithSANRoot } ,
currentTime : 1486684488 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2017-02-09 15:57:53 -08:00
2018-07-11 15:59:56 -04:00
errorCallback : expectHostnameError ( "certificate is not valid for any names" ) ,
2017-02-09 15:57:53 -08:00
} ,
2017-02-08 16:27:00 -08:00
{
// Test that excluded names are respected.
2020-06-18 22:45:52 -04:00
name : "ExcludedNames" ,
2017-02-08 16:27:00 -08:00
leaf : excludedNamesLeaf ,
dnsName : "bender.local" ,
intermediates : [ ] string { excludedNamesIntermediate } ,
roots : [ ] string { excludedNamesRoot } ,
currentTime : 1486684488 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2017-02-08 16:27:00 -08:00
errorCallback : expectNameConstraintsError ,
} ,
2017-10-06 12:46:22 -07:00
{
// Test that unknown critical extensions in a leaf cause a
// verify error.
2020-06-18 22:45:52 -04:00
name : "CriticalExtLeaf" ,
2017-10-06 12:46:22 -07:00
leaf : criticalExtLeafWithExt ,
intermediates : [ ] string { criticalExtIntermediate } ,
roots : [ ] string { criticalExtRoot } ,
currentTime : 1486684488 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2017-10-06 12:46:22 -07:00
errorCallback : expectUnhandledCriticalExtension ,
} ,
{
// Test that unknown critical extensions in an intermediate
// cause a verify error.
2020-06-18 22:45:52 -04:00
name : "CriticalExtIntermediate" ,
2017-10-06 12:46:22 -07:00
leaf : criticalExtLeaf ,
intermediates : [ ] string { criticalExtIntermediateWithExt } ,
roots : [ ] string { criticalExtRoot } ,
currentTime : 1486684488 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2017-10-06 12:46:22 -07:00
errorCallback : expectUnhandledCriticalExtension ,
} ,
2018-07-11 15:59:56 -04:00
{
2020-06-18 22:45:52 -04:00
name : "ValidCN" ,
2018-07-11 15:59:56 -04:00
leaf : validCNWithoutSAN ,
dnsName : "foo.example.com" ,
roots : [ ] string { invalidCNRoot } ,
currentTime : 1540000000 ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2018-07-11 15:59:56 -04:00
2020-04-30 20:43:59 -04:00
errorCallback : expectHostnameError ( "certificate relies on legacy Common Name field" ) ,
2018-07-12 19:19:45 -04:00
} ,
2019-02-04 18:08:43 -05:00
{
// A certificate with an AKID should still chain to a parent without SKID.
// See Issue 30079.
2020-06-18 22:45:52 -04:00
name : "AKIDNoSKID" ,
2019-02-04 18:08:43 -05:00
leaf : leafWithAKID ,
roots : [ ] string { rootWithoutSKID } ,
currentTime : 1550000000 ,
dnsName : "example" ,
2020-06-18 22:45:52 -04:00
systemSkip : true , // does not chain to a system root
2019-02-04 18:08:43 -05:00
expectedChains : [ ] [ ] string {
{ "Acme LLC" , "Acme Co" } ,
} ,
} ,
crypto/x509: prioritize potential parents in chain building
When building a x509 chain the algorithm currently looks for parents
that have a subject key identifier (SKID) that matches the child
authority key identifier (AKID), if it is present, and returns all
matches. If the child doesn't have an AKID, or there are no parents
with matching SKID it will instead return all parents that have a
subject DN matching the child's issuer DN. Prioritizing AKID/SKID
matches over issuer/subject matches means that later in buildChains we
have to throw away any pairs where these DNs do not match. This also
prevents validation when a child has a SKID with two possible parents,
one with matching AKID but mismatching subject DN, and one with a
matching subject but missing AKID. In this case the former will be
chosen and the latter ignored, meaning a valid chain cannot be built.
This change alters how possible parents are chosen. Instead of doing a
two step search it instead only consults the CertPool.byName subject DN
map, avoiding issues where possible parents may be shadowed by parents
that have SKID but bad subject DNs. Additionally it orders the list of
possible parents by the likelihood that they are in fact a match. This
ordering follows this pattern:
* AKID and SKID match
* AKID present, SKID missing / AKID missing, SKID present
* AKID and SKID don't match
In an ideal world this should save a handful of cycles when there are
multiple possible matching parents by prioritizing parents that have
the highest likelihood. This does diverge from past behavior in that
it also means there are cases where _more_ parents will be considered
than in the past. Another version of this change could just retain the
past behavior, and only consider parents where both the subject and
issuer DNs match, and if both parent and child have SKID and AKID also
compare those, without any prioritization of the candidate parents.
This change removes an existing test case as it assumes that the
CertPool will return a possible candidate where the issuer/subject DNs
do not match.
Fixes #30079
Change-Id: I629f579cabb0b3d0c8cae5ad0429cc5a536b3e58
Reviewed-on: https://go-review.googlesource.com/c/go/+/232993
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2020-05-08 15:57:25 -07:00
{
2023-09-22 23:27:51 +00:00
// When there are two parents, one with an incorrect subject but matching SKID
crypto/x509: prioritize potential parents in chain building
When building a x509 chain the algorithm currently looks for parents
that have a subject key identifier (SKID) that matches the child
authority key identifier (AKID), if it is present, and returns all
matches. If the child doesn't have an AKID, or there are no parents
with matching SKID it will instead return all parents that have a
subject DN matching the child's issuer DN. Prioritizing AKID/SKID
matches over issuer/subject matches means that later in buildChains we
have to throw away any pairs where these DNs do not match. This also
prevents validation when a child has a SKID with two possible parents,
one with matching AKID but mismatching subject DN, and one with a
matching subject but missing AKID. In this case the former will be
chosen and the latter ignored, meaning a valid chain cannot be built.
This change alters how possible parents are chosen. Instead of doing a
two step search it instead only consults the CertPool.byName subject DN
map, avoiding issues where possible parents may be shadowed by parents
that have SKID but bad subject DNs. Additionally it orders the list of
possible parents by the likelihood that they are in fact a match. This
ordering follows this pattern:
* AKID and SKID match
* AKID present, SKID missing / AKID missing, SKID present
* AKID and SKID don't match
In an ideal world this should save a handful of cycles when there are
multiple possible matching parents by prioritizing parents that have
the highest likelihood. This does diverge from past behavior in that
it also means there are cases where _more_ parents will be considered
than in the past. Another version of this change could just retain the
past behavior, and only consider parents where both the subject and
issuer DNs match, and if both parent and child have SKID and AKID also
compare those, without any prioritization of the candidate parents.
This change removes an existing test case as it assumes that the
CertPool will return a possible candidate where the issuer/subject DNs
do not match.
Fixes #30079
Change-Id: I629f579cabb0b3d0c8cae5ad0429cc5a536b3e58
Reviewed-on: https://go-review.googlesource.com/c/go/+/232993
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2020-05-08 15:57:25 -07:00
// and one with a correct subject but missing SKID, the latter should be
// considered as a possible parent.
leaf : leafMatchingAKIDMatchingIssuer ,
roots : [ ] string { rootMatchingSKIDMismatchingSubject , rootMismatchingSKIDMatchingSubject } ,
currentTime : 1550000000 ,
dnsName : "example" ,
systemSkip : true ,
expectedChains : [ ] [ ] string {
{ "Leaf" , "Root B" } ,
} ,
} ,
2011-04-19 09:57:58 -04:00
}
2020-06-18 22:45:52 -04:00
func expectHostnameError ( msg string ) func ( * testing . T , error ) {
return func ( t * testing . T , err error ) {
2018-07-11 15:59:56 -04:00
if _ , ok := err . ( HostnameError ) ; ! ok {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not a HostnameError: %v" , err )
2018-07-11 15:59:56 -04:00
}
if ! strings . Contains ( err . Error ( ) , msg ) {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "HostnameError did not contain %q: %v" , msg , err )
2018-07-11 15:59:56 -04:00
}
2011-04-19 09:57:58 -04:00
}
}
2020-06-18 22:45:52 -04:00
func expectExpired ( t * testing . T , err error ) {
2011-04-19 09:57:58 -04:00
if inval , ok := err . ( CertificateInvalidError ) ; ! ok || inval . Reason != Expired {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not Expired: %v" , err )
2011-04-19 09:57:58 -04:00
}
}
2020-06-18 22:45:52 -04:00
func expectUsageError ( t * testing . T , err error ) {
2012-06-20 16:18:56 -04:00
if inval , ok := err . ( CertificateInvalidError ) ; ! ok || inval . Reason != IncompatibleUsage {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not IncompatibleUsage: %v" , err )
2012-06-20 16:18:56 -04:00
}
}
2020-06-18 22:45:52 -04:00
func expectAuthorityUnknown ( t * testing . T , err error ) {
2016-11-03 09:39:32 -04:00
e , ok := err . ( UnknownAuthorityError )
if ! ok {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not UnknownAuthorityError: %v" , err )
2011-04-19 09:57:58 -04:00
}
2016-11-03 09:39:32 -04:00
if e . Cert == nil {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was UnknownAuthorityError, but missing Cert: %v" , err )
2013-01-21 11:25:28 -05:00
}
}
2020-06-18 22:45:52 -04:00
func expectHashError ( t * testing . T , err error ) {
2013-05-20 14:20:26 -04:00
if err == nil {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "no error resulted from invalid hash" )
2013-05-20 14:20:26 -04:00
}
if expected := "algorithm unimplemented" ; ! strings . Contains ( err . Error ( ) , expected ) {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error resulting from invalid hash didn't contain '%s', rather it was: %v" , expected , err )
2013-05-20 14:20:26 -04:00
}
}
2020-06-18 22:45:52 -04:00
func expectNameConstraintsError ( t * testing . T , err error ) {
2017-02-08 16:27:00 -08:00
if inval , ok := err . ( CertificateInvalidError ) ; ! ok || inval . Reason != CANotAuthorizedForThisName {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not a CANotAuthorizedForThisName: %v" , err )
2017-02-08 16:27:00 -08:00
}
}
2020-06-18 22:45:52 -04:00
func expectNotAuthorizedError ( t * testing . T , err error ) {
2016-12-14 14:10:26 -08:00
if inval , ok := err . ( CertificateInvalidError ) ; ! ok || inval . Reason != NotAuthorizedToSign {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not a NotAuthorizedToSign: %v" , err )
2016-12-14 14:10:26 -08:00
}
}
2020-06-18 22:45:52 -04:00
func expectUnhandledCriticalExtension ( t * testing . T , err error ) {
2017-10-06 12:46:22 -07:00
if _ , ok := err . ( UnhandledCriticalExtension ) ; ! ok {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "error was not an UnhandledCriticalExtension: %v" , err )
2017-10-06 12:46:22 -07:00
}
}
2011-11-01 22:04:37 -04:00
func certificateFromPEM ( pemBytes string ) ( * Certificate , error ) {
2011-04-19 09:57:58 -04:00
block , _ := pem . Decode ( [ ] byte ( pemBytes ) )
if block == nil {
2011-11-01 22:04:37 -04:00
return nil , errors . New ( "failed to decode PEM" )
2011-04-19 09:57:58 -04:00
}
return ParseCertificate ( block . Bytes )
}
2020-06-18 22:45:52 -04:00
func testVerify ( t * testing . T , test verifyTest , useSystemRoots bool ) {
opts := VerifyOptions {
Intermediates : NewCertPool ( ) ,
DNSName : test . dnsName ,
CurrentTime : time . Unix ( test . currentTime , 0 ) ,
KeyUsages : test . keyUsages ,
}
2011-04-19 09:57:58 -04:00
2020-06-18 22:45:52 -04:00
if ! useSystemRoots {
opts . Roots = NewCertPool ( )
for j , root := range test . roots {
ok := opts . Roots . AppendCertsFromPEM ( [ ] byte ( root ) )
2011-04-19 09:57:58 -04:00
if ! ok {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "failed to parse root #%d" , j )
2011-04-19 09:57:58 -04:00
}
}
2020-06-18 22:45:52 -04:00
}
2011-04-19 09:57:58 -04:00
2020-06-18 22:45:52 -04:00
for j , intermediate := range test . intermediates {
ok := opts . Intermediates . AppendCertsFromPEM ( [ ] byte ( intermediate ) )
if ! ok {
t . Fatalf ( "failed to parse intermediate #%d" , j )
2013-01-21 11:25:28 -05:00
}
2020-06-18 22:45:52 -04:00
}
2013-01-21 11:25:28 -05:00
2020-06-18 22:45:52 -04:00
leaf , err := certificateFromPEM ( test . leaf )
if err != nil {
t . Fatalf ( "failed to parse leaf: %v" , err )
}
2011-04-19 09:57:58 -04:00
2020-06-18 22:45:52 -04:00
chains , err := leaf . Verify ( opts )
2013-01-21 11:25:28 -05:00
2020-06-18 22:45:52 -04:00
if test . errorCallback == nil && err != nil {
2021-12-08 16:47:56 -05:00
if runtime . GOOS == "windows" && strings . HasSuffix ( testenv . Builder ( ) , "-2008" ) && err . Error ( ) == "x509: certificate signed by unknown authority" {
testenv . SkipFlaky ( t , 19564 )
}
2020-06-18 22:45:52 -04:00
t . Fatalf ( "unexpected error: %v" , err )
}
if test . errorCallback != nil {
if useSystemRoots && test . systemLax {
if err == nil {
t . Fatalf ( "expected error" )
2011-04-19 09:57:58 -04:00
}
2020-06-18 22:45:52 -04:00
} else {
test . errorCallback ( t , err )
2011-04-19 09:57:58 -04:00
}
2020-06-18 22:45:52 -04:00
}
2011-04-19 09:57:58 -04:00
2020-10-14 01:05:43 +02:00
doesMatch := func ( expectedChain [ ] string , chain [ ] * Certificate ) bool {
if len ( chain ) != len ( expectedChain ) {
return false
}
for k , cert := range chain {
if ! strings . Contains ( nameToKey ( & cert . Subject ) , expectedChain [ k ] ) {
return false
}
}
return true
2020-06-18 22:45:52 -04:00
}
2011-04-19 09:57:58 -04:00
2023-06-21 14:43:05 -07:00
// Every expected chain should match one (or more) returned chain. We tolerate multiple
// matches, as due to root store semantics it is plausible that (at least on the system
// verifiers) multiple identical (looking) chains may be returned when two roots with the
// same subject are present.
2020-10-14 01:05:43 +02:00
for _ , expectedChain := range test . expectedChains {
2023-06-21 14:43:05 -07:00
var match bool
2020-10-14 01:05:43 +02:00
for _ , chain := range chains {
if doesMatch ( expectedChain , chain ) {
2023-06-21 14:43:05 -07:00
match = true
break
2020-10-14 01:05:43 +02:00
}
}
2023-06-21 14:43:05 -07:00
if ! match {
t . Errorf ( "No match found for %v" , expectedChain )
2020-10-14 01:05:43 +02:00
}
}
// Every returned chain should match 1 expected chain (or <2 if testing against the system)
for _ , chain := range chains {
nMatched := 0
for _ , expectedChain := range test . expectedChains {
if doesMatch ( expectedChain , chain ) {
nMatched ++
2020-06-18 22:45:52 -04:00
}
2020-10-14 01:05:43 +02:00
}
// Allow additional unknown chains if systemLax is set
if nMatched == 0 && test . systemLax == false || nMatched > 1 {
t . Errorf ( "Got %v matches for chain %v" , nMatched , chainToDebugString ( chain ) )
for _ , expectedChain := range test . expectedChains {
if doesMatch ( expectedChain , chain ) {
t . Errorf ( "\t matched %v" , expectedChain )
2011-04-19 09:57:58 -04:00
}
}
}
}
}
2012-03-07 13:12:35 -05:00
func TestGoVerify ( t * testing . T ) {
2020-06-18 22:45:52 -04:00
for _ , test := range verifyTests {
t . Run ( test . name , func ( t * testing . T ) {
testVerify ( t , test , false )
} )
}
2012-03-07 13:12:35 -05:00
}
func TestSystemVerify ( t * testing . T ) {
if runtime . GOOS != "windows" {
2013-01-24 17:32:10 +11:00
t . Skipf ( "skipping verify test using system APIs on %q" , runtime . GOOS )
2012-03-07 13:12:35 -05:00
}
2020-06-18 22:45:52 -04:00
for _ , test := range verifyTests {
t . Run ( test . name , func ( t * testing . T ) {
if test . systemSkip {
t . SkipNow ( )
}
testVerify ( t , test , true )
} )
}
2012-03-07 13:12:35 -05:00
}
2011-04-19 09:57:58 -04:00
func chainToDebugString ( chain [ ] * Certificate ) string {
var chainStr string
for _ , cert := range chain {
if len ( chainStr ) > 0 {
chainStr += " -> "
}
chainStr += nameToKey ( & cert . Subject )
}
return chainStr
}
2011-10-14 15:06:54 -04:00
func nameToKey ( name * pkix . Name ) string {
return strings . Join ( name . Country , "," ) + "/" + strings . Join ( name . Organization , "," ) + "/" + strings . Join ( name . OrganizationalUnit , "," ) + "/" + name . CommonName
}
2023-02-28 13:23:43 -08:00
const gtsIntermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIFljCCA36gAwIBAgINAgO8U1lrNMcY9QFQZjANBgkqhkiG9w0BAQsFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw
MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFDMzCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAPWI3 + dijB43 + DdCkH9sh9D7ZYIl / ejLa6T / belaI + KZ9hzp
kgOZE3wJCor6QtZeViSqejOEH9Hpabu5dOxXTGZok3c3VVP + ORBNtzS7XyV3NzsX
lOo85Z3VvMO0Q + sup0fvsEQRY9i0QYXdQTBIkxu / t / bgRQIh4JZCF8 / ZK2VWNAcm
BA2o / X3KLu / qSHw3TT8An4Pf73WELnlXXPxXbhqW //yMmqaZviXZf5YsBvcRKgKA
gOtjGDxQSYflispfGStZloEAoPtR28p3CwvJlk / vcEnHXG0g / Zm0tOLKLnf9LdwL
tmsTDIwZKxeWmLnwi / agJ7u2441Rj72ux5uxiZ0CAwEAAaOCAYAwggF8MA4GA1Ud
DwEB / wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T
AQH / BAgwBgEB / wIBADAdBgNVHQ4EFgQUinR / r4XN7pXNPZzQ4kYU83E1HScwHwYD
VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG
CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw
AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt
MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsMFcG
A1UdIARQME4wOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br
aS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATAIBgZngQwBAgIwDQYJKoZIhvcN
AQELBQADggIBAIl9rCBcDDy + mqhXlRu0rvqrpXJxtDaV / d9AEQNMwkYUuxQkq / BQ
cSLbrcRuf8 / xam / IgxvYzolfh2yHuKkMo5uhYpSTld9brmYZCwKWnvy15xBpPnrL
RklfRuFBsdeYTWU0AIAaP0 + fbH9JAIFTQaSSIYKCGvGjRFsqUBITTcFTNvNCCK9U
+ o53UxtkOCcXCb1YyRt8OS1b887U7ZfbFAO / CVMkH8IMBHmYJvJh8VNS / UKMG2Yr
PxWhu //2m+OBmgEGcYk1KCTd4b3rGS3hSMs9WYNRtHTGnXzGsYZbr8w0xNPM1IER
lQCh9BIiAfq0g3GvjLeMcySsN1PCAJA / Ef5c7TaUEDu9Ka7ixzpiO2xj2YC / WXGs
Yye5TBeg2vZzFb8q3o / zpWwygTMD0IZRcZk0upONXbVRWPeyk + gB9lm + cZv9TSjO
z23HFtz30dZGm6fKa + l3D / 2 gthsjgx0QGtkJAITgRNOidSOzNIb2ILCkXhAd4FJG
AJ2xDx8hcFH1mt0G / FX0Kw4zd8NLQsLxdxP8c4CU6x + 7 Nz / OAipmsHMdMqUybDKw
juDEI / 9 bfU1lcKwrmz3O2 + BtjjKAvpafkmO8l7tdufThcV4q5O8DIrGKZTqPwJNl
1 IXNDw9bg1kWRxYtnCQ6yICmJhSFm / Y3m6xv + cXDBlHz4n / FsRC6UfTd
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2011-04-19 09:57:58 -04:00
2023-02-28 13:23:43 -08:00
const gtsRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIFVzCCAz + gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L + UPreVp0A8of2C + X0yBoJx9vaMf / vo
27 xqLpeXo4xL + Sv2sfnOhB2x + cWX3u + 58 qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
Cl7raKb0xlpHDU0QM + NOsROjyBhsS + z8CZDfnWQpJSMHobTSPS5g4M / SCYe7zUjw
TcLCeoiKu7rPWRnWr4 + wB7CeMfGCwcDfLqZtbBkOtdh + JhpFAz2weaSUKK0Pfybl
qAj + lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9 + aCEI3oncKKiPo4Zor8
Y / kB + Xj9e1x3 + naH + uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf / VjsPOS + C12LOORc92
wO1AK / 1 TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
aDPvOmbsB4om3xPXV2V4J95eSRQAogB / mqghtqmxlbCluQ0WEdrHbEg8QOB + DVrN
VjzRlwW5y0vtOUucxD / SVRNuJLDWcfr0wbrM7Rv1 / oFB2ACYPTrIrnqYNxgFlQID
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH / BAUwAwEB / zAdBgNVHQ4E
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ + qQibb
C5u + / x6Wki4 + omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq + bXmYOfg6LEe
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps + GlQebtuy
h6f88 / qBVRRiClmpIgUxPoLW7ttXNLwzldMXG + gnoot7TiYaelpkttGsN / H9oPM4
7 HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo + 9 K4l / 3 wV3s6MJT / KYnAK9y8J
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp + 0 KueIHoI17eko8cdLiA6Ef
MgfdG + RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv / bal8xa5meLMFrUKTX5hgUvYU /
Z6tGn6D / Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
6 u9AWpQKXCBfTkBdYiJ23 //OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
0E6 yove + 7 u7Y / 9 waLd64NnHi / Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
2 tIMPNuzjsmhDYAPexZ3FL //2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
bP6MvPJwNQzcmRk13NfIRmPVNnGuV / u3gm3c
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2011-04-19 09:57:58 -04:00
const googleLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2023-02-28 13:23:43 -08:00
MIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
QzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw
ODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S + lwoCMT5geP0u9pWjk6a
wdB6i3kO + UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC / nRcZU49LX5 + o
55 kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr
N775eYbixEULvyVLq5BLbCOpPo8n0 / MTjQ32ku1jQq3GIYMJC / Rf2VW5doF6t9zs
KleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g / 9 VPJtRf4l1Q
WRPDC + 6 JUqzXCxAGmIRGZ7TNMxPMBW / 7 DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw
DgYDVR0PAQH / BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB / wQC
MAAwHQYDVR0OBBYEFBnboj3lf9 + Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0
f6 + Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb
aHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v
cGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n
b29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD
VR0fBDUwMzAxoC + gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow
TjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4
4 FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo + S
3 T8PP0 / cvokhUlumRE3GRWGL4WRMLpcCIQDY + bwK384mZxyXGZ5lwNRTAPNzT8Fx
1 + //nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB
hXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR + M0UuJ2zOqbxIRum7Sn9RMPOBGMC
IQD1 / BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQsF
AAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b + hj4O + Rn7BXTTmaYX3M6p
MsfRH4YVJJMB / dc3PROR2VtnKFC6gAZX + RKM6nXnZhIlOdmQnonS1ecOL19PliUd
VXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE + XXiOZhcZuMYyWmoEKJQ / XlSga
zWfTn1IcKhA3IC7A1n / 5 bkkWD1Xi1mdWFQ6DQDMp //667zz7pKOgFMlB93aPDjvI
c78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ / KQ1yb7LPUOR6wXXWrG1cLnNP
i8eNLnKL9PXQ + 5 SwJFCzfEhcIZuhzg ==
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2011-04-19 09:57:58 -04:00
2013-05-20 14:20:26 -04:00
// googleLeafWithInvalidHash is the same as googleLeaf, but the signature
// algorithm in the certificate contains a nonsense OID.
const googleLeafWithInvalidHash = ` -- -- - BEGIN CERTIFICATE -- -- -
2023-02-28 13:23:43 -08:00
MIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQ4FADBG
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
QzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw
ODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S + lwoCMT5geP0u9pWjk6a
wdB6i3kO + UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC / nRcZU49LX5 + o
55 kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr
N775eYbixEULvyVLq5BLbCOpPo8n0 / MTjQ32ku1jQq3GIYMJC / Rf2VW5doF6t9zs
KleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g / 9 VPJtRf4l1Q
WRPDC + 6 JUqzXCxAGmIRGZ7TNMxPMBW / 7 DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw
DgYDVR0PAQH / BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB / wQC
MAAwHQYDVR0OBBYEFBnboj3lf9 + Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0
f6 + Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb
aHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v
cGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n
b29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD
VR0fBDUwMzAxoC + gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow
TjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4
4 FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo + S
3 T8PP0 / cvokhUlumRE3GRWGL4WRMLpcCIQDY + bwK384mZxyXGZ5lwNRTAPNzT8Fx
1 + //nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB
hXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR + M0UuJ2zOqbxIRum7Sn9RMPOBGMC
IQD1 / BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQ4F
AAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b + hj4O + Rn7BXTTmaYX3M6p
MsfRH4YVJJMB / dc3PROR2VtnKFC6gAZX + RKM6nXnZhIlOdmQnonS1ecOL19PliUd
VXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE + XXiOZhcZuMYyWmoEKJQ / XlSga
zWfTn1IcKhA3IC7A1n / 5 bkkWD1Xi1mdWFQ6DQDMp //667zz7pKOgFMlB93aPDjvI
c78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ / KQ1yb7LPUOR6wXXWrG1cLnNP
i8eNLnKL9PXQ + 5 SwJFCzfEhcIZuhzg ==
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2013-05-20 14:20:26 -04:00
2012-06-20 16:18:56 -04:00
const smimeLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2020-06-18 22:45:52 -04:00
MIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB
nTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB
WlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB
MTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB
QVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC
AQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh
dGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv
bmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl
a28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW
TyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE
DAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb + v / L774jMH8valX7RPH8cl2Lb
SiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X / g66 / E / + q2hUJlfJtqVDJFwtFYV4u2S
yzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9 + DK8Tm4cMF6i8l
+ AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK
0 DZ93 / f0G / 3 + vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr / O / WWgbB / BqhR
qjJQ7Ui16cuDldXaWQ / rkMzsxmsAox0UF + zdQNvXUQIDAQABo4IDBDCCAwAwgccG
A1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j
b22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S
TWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh
IGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H
YXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH /
BAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC
AjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK
90 clh / + 8 taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB
AQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh
Y29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG + Wml1cnRhZ2lyaWEgRXVza2Fs
IEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl
ZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy
ZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh
ZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI
KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g
K4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ
KoZIhvcNAQELBQADggIBAIy5PQ + UZlCRq6ig43vpHwlwuD9daAYeejV0Q + ZbgWAE
GtO0kT / ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4 +
ZoXAg / QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt
BGI7L1MnGJBv8RfP / TL67aJ5bgq2ri4S8vGHtXSjcZ0 + rCEOLJtmDNMnTZxancg3
/ H5edeNd + n6Z48LO + JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL
i31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV / gcIw
bCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS
5 IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds + W9k1S2CNTx
d0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw
mYust + gxVtOZs1gk2MUCgJ1V1X + g7r / Cg7viIn6TLkLrpS1kS1hvMqkl9M + 7 XqPo
Qd95nJKOkusQpy99X4dF / lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F +
2012-06-20 16:18:56 -04:00
-- -- - END CERTIFICATE -- -- - `
const smimeIntermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
2020-06-18 22:45:52 -04:00
MIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL
MAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu
cGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV
BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0
YWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE
AwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj
YXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h
rR5T4xuV / usKDoMIasaiKvfLhbwxaNtTt + a7W / 6 wV5bv3svQFIy3sUXjjdzV1nG2
To2wo / YSPQiOt8exWvOapvL21ogiof + kelWnXFjWaKJI / vThHYLgIYEMj / y4HdtU
ojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD
PY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr
PGqkg2y1Q3gT6b4537jz + StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn
soShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a + hx8FM
8 IBpGf / I94pU6HzGXqCyc1d46drJgDY9mXa + 6 YDAJFl3xeXOOW2iGCfwXqhiCrKL
MYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn + Utqp8vd2r7ShfQJz01KtM4hgKdgSg
jtW + shkVVN5ng / fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM / 7 S1HxlT61cdQ7Nnk
3 LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm + Vb5LSThdHC8bxxwyTb
KsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w
gbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO
MUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0
b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk
aXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP
BgNVHRMBAf8EBTADAQH / MA4GA1UdDwEB / wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl
h / + 8 taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc / ksbHmvVV0bAUw
OgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl
bnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v
b2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny
bC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM
3 DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3 / fKCS
M539qH0M + gsySNie + iKlbSZJUyE635T1tKw + G7bDUapjlH1xyv55NC5I6wCXGC6E
3 TEP5B / E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny / N / V1enaTCpRl1qH1OyL
xUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P / y6JAtN1GqT4
VF / + vMygx22n0DufGepBwTQz6 / rr1ulSZ + eMnuJiTXgh / BzQnkUsXTb8mHII25iR
0 oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK
b6mzE1BN2ug + ZaX8wLA5IMPFaf0jKhb / Cxu8INsxjt00brsErCc9ip1VNaH0M4bi
1 tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU / U38BxW3uyJMY60zwHvS
FlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid + TUnfyxbJ9cttSgzaF3hP / N4zgMEM
5 tikXUskeckt8LUK96EH0QyssavAMECUEb / xrupyRdYWwjQGvNLq6T5 + fViDGyOw
k + lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7
-- -- - END CERTIFICATE -- -- - `
const smimeRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7 / hy / WL1xnmfTANBgkqhkiG9w0BAQsFADA4
MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
scIbRTJxldn + EFvMr + eleQGPicPK8lVx93e + d5TzcqQsRNiekpsUOqHnJJAKClaO
xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
LmYRY2xU + zydcsC8Lv / Ct90NduM61 / e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
yCV8wXDbO / QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB + eAJRE1NZMDhDVqHIrytG6P +
JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy + E60Q
rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU + mNFctKy6lvROUbQc / hhqfK0GqfvEyN
BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD + veIR8GdwYDsMnvmfzAuU8L
hij + 0 rnq49qlw0dpEuDb8PYZi + 17 cNcC1u2HGCgsBCRMd + RIihrGO5rUD8r6ddIB
QFqNeb + Lz0vPqhbBleStTIo + F5HUsWLlguWABKQDfo2 / 2 n + iD5dPDNMN + 9 fR5XJ +
HMh3 / 1 uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw + SVpFTlBFIFMuQS4gLSBDSUYg
QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH / BAUwAwEB / zAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg / YsfqikuFgba56
awmqxinuaElnMIAkejEWOVt + 8 Rwu3WwJrfIxwYJOubv5vr8qhT / AQKM6WfxZSzwo
JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
LDXWrzY0tM07 + DKo7 + N4ifuNRSzanLh + QBxh5z6ikixL8s36mLYp //Pye6kfLqCT
VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1 / OpaFs4R1 + 7 vUIgtYf8 /
QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH / pR9hNiTrdZoQ0iy2 + tzJOeRf1SktoA +
naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
QyYBNWNgVYkDOnXYukrZVP / u3oDYLdE41V4tC5h9Pmzb / CaIxw ==
2012-06-20 16:18:56 -04:00
-- -- - END CERTIFICATE -- -- - `
2013-02-09 13:20:25 -05:00
2013-10-21 19:01:24 -04:00
var nameConstraintsLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2024-11-19 11:34:19 -08:00
MIIG + jCCBOKgAwIBAgIQWj9gbtPPkZs65N6TKyutRjANBgkqhkiG9w0BAQsFADCB
yzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYDVQQHEwpCbGFj
a3NidXJnMSMwIQYDVQQLExpHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTE8MDoG
A1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBTdGF0ZSBV
bml2ZXJzaXR5MTEwLwYDVQQDEyhWaXJnaW5pYSBUZWNoIEdsb2JhbCBRdWFsaWZp
ZWQgU2VydmVyIENBMB4XDTE4MDQyNjE5NDU1M1oXDTE5MTIxMDAwMDAwMFowgZAx
CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tz
YnVyZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFu
ZCBTdGF0ZSBVbml2ZXJzaXR5MRswGQYDVQQDExJ1ZGN0ZXN0LmFkcy52dC5lZHUw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcoVBeV3AzdSGMzRWH0tuM
VluEj + sq4r9PuLDBAdgjjHi4ED8npT2 / fgOalswInXspRvFS + pkEwTrmeZ7HPzRJ
HUE5YlX5Nc6WI8ZXPVg5E6GyoMy6gNlALwqsIvDCvqxBMc39oG6yOuGmQXdF6s0N
BJMrXc4aPz60s4QMWNO2OHL0pmnZqE1TxYRBHUY / dk3cfsIepIDDuSxRsNE / P / MI
pxm / uVOyiLEnPmOMsL430SZ7nC8PxUMqya9ok6Zaf7k54g7JJXDjE96VMCjMszIv
Ud9qe1PbokTOxlG / 4 QW7Qm0dPbiJhTUuoBzVAxzlOOkSFdXqSYKjC9tFcbr8y + pT
AgMBAAGjggIRMIICDTCBtgYIKwYBBQUHAQEEgakwgaYwXwYIKwYBBQUHMAKGU2h0
dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9iYWxxdWFsaWZpZWRzZXJ2ZXIvY2FjZXJ0
L2dsb2JhbHF1YWxpZmllZHNlcnZlcl9zaGEyNTYuY3J0MEMGCCsGAQUFBzABhjdo
dHRwOi8vdnRjYS5wa2kudnQuZWR1OjgwODAvZWpiY2EvcHVibGljd2ViL3N0YXR1
cy9vY3NwMB0GA1UdDgQWBBSzDLXee0wbgXpVQxvBQCophQDZbTAMBgNVHRMBAf8E
AjAAMB8GA1UdIwQYMBaAFLxiYCfV4zVIF + lLq0Vq0Miod3GMMGoGA1UdIARjMGEw
DgYMKwYBBAG0aAUCAgIBMA4GDCsGAQQBtGgFAgIBATA / BgwrBgEEAbRoBQICAwEw
LzAtBggrBgEFBQcCARYhaHR0cDovL3d3dy5wa2kudnQuZWR1L2dsb2JhbC9jcHMv
MEoGA1UdHwRDMEEwP6A9oDuGOWh0dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9iYWxx
dWFsaWZpZWRzZXJ2ZXIvY3JsL2NhY3JsLmNybDAOBgNVHQ8BAf8EBAMCBeAwHQYD
VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdEQQWMBSCEnVkY3Rlc3Qu
YWRzLnZ0LmVkdTANBgkqhkiG9w0BAQsFAAOCAgEAD79kuyZbwQJCSBOVq9lA0lj4
juHM7RMBfp2GuWvhk5F90OMKQCNdITva3oq4uQzt013TtwposYXq / d0Jobk6RHxj
OJzRZVvEPsXLvKm8oLhz7 / qgI8gcVeJFR9WgdNhjN1upn ++ EnABHUdDR77fgixuH
FFwNC0WSZ6G0 + WgYV7MKD4jYWh1DXEaJtQCN763IaWGxgvQaLUwS423xgwsx + 8 rw
hCRYns5u8myTbUlEu2b + GYimiogtDFMT01A7y88vKl9g + 3 bx42dJHQNNmSzmYPfs
IljtQbVwJIyNL / rwjoz7BTk8e9WY0qUK7ZYh + oGK8kla8yfPKtkvOJV29KdFKzTm
42 kNm6cH + U5gGwEEg + Xj66Q2yFH5J9kAoBazTepgQ / 13 wwTY0mU9PtKVBtMH5Y / u
MoNVZz6p7vWWRrY5qSXIaW9qyF3bZnmPEHHYTplWsyAyh8blGlqPnpayDflPiQF /
9 y37kax5yhT0zPZW1ZwIZ5hDTO7pu5i83bYh3pzhvJNHtv74Nn / SX1dTZrWBi / HG
OSWK3CLz8qAEBe72XGoBjBzuk9VQxg6k52qjxCyYf7CBSQpTZhsNMf0gzu + JNATc
b + XaOqJT6uI / RfqAJVe16ZeXZIFZgQlzIwRS9vobq9fqTIpH / QxqgXROGqAlbBVp
/ ByH6FEe6 + oH1UCklhg =
2013-10-21 19:01:24 -04:00
-- -- - END CERTIFICATE -- -- - `
var nameConstraintsIntermediate1 = ` -- -- - BEGIN CERTIFICATE -- -- -
2024-11-19 11:34:19 -08:00
MIIHVTCCBj2gAwIBAgINAecHzcaPEeFvu7X4TTANBgkqhkiG9w0BAQsFADBjMQsw
CQYDVQQGEwJCRTEVMBMGA1UECxMMVHJ1c3RlZCBSb290MRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMSIwIAYDVQQDExlUcnVzdGVkIFJvb3QgQ0EgU0hBMjU2IEcy
MB4XDTE3MTIwNjAwMDAwMFoXDTIyMTIwNjAwMDAwMFowgcsxCzAJBgNVBAYTAlVT
MREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEjMCEGA1UE
CxMaR2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1Zpcmdpbmlh
IFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTExMC8G
A1UEAxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALgIZhEaptBWADBqdJ45ueFG
zMXaGHnzNxoxR1fQIaaRQNdCg4cw3A4dWKMeEgYLtsp65ai3Xfw62Qaus0 + KJ3Rh
gV + rihqK81NUzkls78fJlADVDI4fCTlothsrE1CTOMiy97jKHai5mVTiWxmcxpmj
v7fm5Nhc + uHgh2hIz6npryq495mD51ZrUTIaqAQN6Pw / VHfAmR524vgriTOjtp1t
4 lA9pXGWjF / vkhAKFFheOQSQ00rngo2wHgCqMla64UTN0oz70AsCYNZ3jDLx0kOP
0 YmMR3Ih91VA63kLqPXA0R6yxmmhhxLZ5bcyAy1SLjr1N302MIxLM / pSy6aquEnb
ELhzqyp9yGgRyGJay96QH7c4RJY6gtcoPDbldDcHI9nXngdAL4DrZkJ9OkDkJLyq
G66WZTF5q4EIs6yMdrywz0x7QP + OXPJrjYpbeFs6tGZCFnWPFfmHCRJF8 / unofYr
heq + 9 J7Jx3U55S / k57NXbAM1RAJOuMTlfn9Etf9Dpoac9poI4Liav6rBoUQk3N3J
WqnVHNx / NdCyJ1 / 6 UbKMJUZsStAVglsi6lVPo289HHOE4f7iwl3SyekizVOp01wU
in3ycnbZB / rXmZbwapSxTTSBf0EIOr9i4EGfnnhCAVA9U5uLrI5OEB69IY8PNX00
71 s3Z2a2fio5c8m3JkdrAgMBAAGjggKdMIICmTAOBgNVHQ8BAf8EBAMCAQYwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB / wQIMAYBAf8CAQAw
HQYDVR0OBBYEFLxiYCfV4zVIF + lLq0Vq0Miod3GMMB8GA1UdIwQYMBaAFMhjmwhp
VMKYyNnN4zO3UF74yQGbMIGNBggrBgEFBQcBAQSBgDB + MDcGCCsGAQUFBzABhito
dHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vdHJ1c3Ryb290c2hhMmcyMEMGCCsG
AQUFBzAChjdodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC90cnVz
dHJvb3RzaGEyZzIuY3J0MIHyBgNVHR4EgeowgeeggbIwCIEGdnQuZWR1MAmCB2Jl
di5uZXQwCoIIdmNvbS5lZHUwCIIGdnQuZWR1MAyCCnZ0Y2dpdC5jb20wd6R1MHMx
CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tz
YnVyZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFu
ZCBTdGF0ZSBVbml2ZXJzaXR5oTAwCocIAAAAAAAAAAAwIocgAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2Ny
bC5nbG9iYWxzaWduLmNvbS9ncy90cnVzdHJvb3RzaGEyZzIuY3JsMEwGA1UdIARF
MEMwQQYJKwYBBAGgMgE8MDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh
bHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQArHocpEKTv
DW1Hw0USj60KN96aLJXTLm05s0LbjloeTePtDFtuisrbE85A0IhCwxdIl / VsQMZB
7 mQZBEmLzR + NK1 / Luvs7C6WTmkqrE8H7D73dSOab5fMZIXS91V / aEtEQGpJMhwi1
svd9TiiQrVkagrraeRWmTTz9BtUA3CeujuW2tShxF1ew4Q4prYw97EsE4HnKDJtu
RtyTqKsuh / rRvKMmgUdEPZbVI23yzUKhi / mTbyml / 35 x / f6f5p7OYIKcQ / 34 sts8
xoW9dfkWBQKAXCstXat3WJVilGXBFub6GoVZdnxTDipyMZhUT / vzXq2bPphjcdR5
YGbmwyYmChfa
2013-10-21 19:01:24 -04:00
-- -- - END CERTIFICATE -- -- - `
var nameConstraintsIntermediate2 = ` -- -- - BEGIN CERTIFICATE -- -- -
2024-11-19 11:34:19 -08:00
MIIEXDCCA0SgAwIBAgILBAAAAAABNumCOV0wDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIwNDI1MTEwMDAwWhcNMjcwNDI1
MTEwMDAwWjBjMQswCQYDVQQGEwJCRTEVMBMGA1UECxMMVHJ1c3RlZCBSb290MRkw
FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSIwIAYDVQQDExlUcnVzdGVkIFJvb3Qg
Q0EgU0hBMjU2IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz80 +
/ Q2PAhLuYwe04YTLBLGKr1 / JScHtDvAY5E94GjGxCbSR1 / 1 VhL880UPJyN85tddO
oxZPgtIyZixDvvK + CgpT5webyBBbqK / ap7aoByghAJ7X520XZMRwKA6cEWa6tjCL
WH1zscxQxGzgtV50rn2ux2SapoCPxMpM4 + tpEVwWJf3KP3NT + jd9GRaXWgNei5JK
Quo9l + cZkSeuoWijvaer5hcLCufPywMMQd0r6XXIM / l7g9DjMaE24d + fa2bWxQXC
8 WT / PZ + D1KUEkdtn / ixADqsoiIibGn7M84EE9 / NLjbzPrwROlBUJFz6cuw + II0rZ
8 OFFeZ / OkHHYZq2h9wIDAQABo4IBJjCCASIwDgYDVR0PAQH / BAQDAgEGMA8GA1Ud
EwEB / wQFMAMBAf8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0
dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMB0GA1UdDgQWBBTI
Y5sIaVTCmMjZzeMzt1Be + MkBmzA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vY3Js
Lmdsb2JhbHNpZ24ubmV0L3Jvb3QtcjMuY3JsMD4GCCsGAQUFBwEBBDIwMDAuBggr
BgEFBQcwAYYiaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3Jvb3RyMzAfBgNV
HSMEGDAWgBSP8Et / qC5FJK5NUPpjmove4t0bvDANBgkqhkiG9w0BAQsFAAOCAQEA
XzbLwBjJiY6j3WEcxD3eVnsIY4pY3bl6660tgpxCuLVx4o1xyiVkS / BcQFD7GIoX
FBRrf5HibO1uSEOw0QZoRwlsio1VPg1PRaccG5C1sB51l / TL1XH5zldZBCnRYrrF
qCPorxi0xoRogj8kqkS2xyzYLElhx9X7jIzfZ8dC4mgOeoCtVvwM9xvmef3n6Vyb
7 / hl3w / zWwKxWyKJNaF7tScD5nvtLUzyBpr ++ aztiyJ1WliWcS6W + V2gKg9rxEC /
rc2yJS70DvfkPiEnBJ2x2AHZV3yKTALUqurkV705JledqUT9I5frAwYNXZ8pNzde
n + DIcSIo7yKy6MX9czbFWQ ==
2013-10-21 19:01:24 -04:00
-- -- - END CERTIFICATE -- -- - `
var globalSignRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2024-11-19 11:34:19 -08:00
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie / QV2EcWtiHL8
RgJDx7KKnQRfJMsuS + FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e + pZo34knlTifBtc + ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO / bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c + 9 C7v / U9AOEGM + iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk + QLjJg6VfLuQSSaGjlOCZgdbKfd / + RFO + uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH / BAQDAgEGMA8GA1UdEwEB / wQFMAMBAf8wHQYDVR0OBBYEFI / wS3 + o
LkUkrk1Q + mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr + yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q / c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj + 9 xTaGdWPoO4zzUhw8lo / s7awlOqzJCK
6 fBdRoyV3XpYKBovHd7NADdBj + 1 EbddTKJd + 82 cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws / zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9 + E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
2013-10-21 19:01:24 -04:00
-- -- - END CERTIFICATE -- -- - `
2014-04-14 12:12:06 -07:00
2023-02-28 13:23:43 -08:00
const digicertRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9 w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U / dDxGkAV53ijSLdhwZAAIEJzs4bg7 / fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV / Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43 C / dxC //AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv + zbMUZBfHWymeMr / y7vrTC0LUq7dBMtoM1O / 4
gdW7jVg / tRvoSSiicNoxBN33shbyTApOB6jtSj1etX + jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH / BAUwAwEB / zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK + t1EnE9SsPTfrgT1eXkIoyQY / Esr
hMAtudXH / vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp / 2 PV5Adg
06 O / nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp + dWOIrWcBAI + 0 tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU + Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp + uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4 =
2014-04-14 12:12:06 -07:00
-- -- - END CERTIFICATE -- -- - `
2023-02-28 13:23:43 -08:00
const trustAsiaSHA384Intermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
MIID9zCCAt + gAwIBAgIQC965p4OR4AKrGlsyW0XrDzANBgkqhkiG9w0BAQwFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xODA0MjcxMjQyNTlaFw0yODA0MjcxMjQyNTlaMFoxCzAJBgNVBAYTAkNO
MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQD
ExtUcnVzdEFzaWEgRUNDIE9WIFRMUyBQcm8gQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAQPIUn75M5BCQLKoPsSU2KTr3mDMh13usnAQ38XfKOzjXiyQ + W0inA7meYR
xS + XMQgvnbCigEsKj3ErPIzO68uC9V / KdqMaXWBJp85Ws9A4KL92NB4Okbn5dp6v
Qzy08PajggFeMIIBWjAdBgNVHQ4EFgQULdRyBx6HyIH / + LOvuexyH5p / 3 PwwHwYD
VR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH / BAQDAgGGMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH / AgEA
MDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AuZGlnaWNl
cnQtY24uY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwuZGlnaWNlcnQt
Y24uY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBWBgNVHSAETzBNMDcGCWCG
SAGG / WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v
Q1BTMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQEMBQADggEBACVRufYd
j81xUqngFCO + Pk8EYXie0pxHKsBZnOPygAyXKx + awUasKBAnHjmhoFPXaDGAP2oV
OeZTWgwnURVr6wUCuTkz2 / 8 Tgl1egC7OrVcHSa0fIIhaVo9 / zRA / hr31xMG7LFBk
GNd7jd06Up4f / UOGbcJsqJexc5QRcUeSwe1MiUDcTNiyCjZk74QCPdcfdFYM4xsa
SlUpboB5vyT7jFePZ2v95CKjcr0EhiQ0gwxpdgoipZdfYTiMFGxCLsk6v8pUv7Tq
PT / qadOGyC + PfLuZh1PtLp20mF06K + MzheCiv + w1NT5ofhmcObvukc68wvbvRFL6
rRzZxAYN36q1SX8 =
2014-04-14 12:12:06 -07:00
-- -- - END CERTIFICATE -- -- - `
2023-02-28 13:23:43 -08:00
const trustAsiaLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIEwTCCBEegAwIBAgIQBOjomZfHfhgz2bVYZVuf2DAKBggqhkjOPQQDAzBaMQsw
CQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5j
LjEkMCIGA1UEAxMbVHJ1c3RBc2lhIEVDQyBPViBUTFMgUHJvIENBMB4XDTE5MDUx
NzAwMDAwMFoXDTIwMDcyODEyMDAwMFowgY0xCzAJBgNVBAYTAkNOMRIwEAYDVQQI
DAnnpo / lu7rnnIExEjAQBgNVBAcMCeWOpumXqOW4gjEqMCgGA1UECgwh5Y6m6Zeo
5 Y + B546W5Y + B56eR5oqA5pyJ6ZmQ5YWs5Y + 4 MRgwFgYDVQQLDA / nn6Xor4bkuqfm
nYPpg6gxEDAOBgNVBAMMByoudG0uY24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AARx / MDQ0oGnCLagQIzjIz57iqFYFmz4 / W6gaU6N + GHBkzyvQU8aX02QkdlTTNYL
TCoGFJxHB0XlZVSxrqoIPlNKo4ICuTCCArUwHwYDVR0jBBgwFoAULdRyBx6HyIH /
+ LOvuexyH5p / 3 PwwHQYDVR0OBBYEFGTyf5adc5smW8NvDZyummJwZRLEMBkGA1Ud
EQQSMBCCByoudG0uY26CBXRtLmNuMA4GA1UdDwEB / wQEAwIHgDAdBgNVHSUEFjAU
BggrBgEFBQcDAQYIKwYBBQUHAwIwRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2Ny
bC5kaWdpY2VydC1jbi5jb20vVHJ1c3RBc2lhRUNDT1ZUTFNQcm9DQS5jcmwwTAYD
VR0gBEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cu
ZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBAgIwfgYIKwYBBQUHAQEEcjBwMCcGCCsG
AQUFBzABhhtodHRwOi8vb2NzcC5kaWdpY2VydC1jbi5jb20wRQYIKwYBBQUHMAKG
OWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LWNuLmNvbS9UcnVzdEFzaWFFQ0NPVlRM
U1Byb0NBLmNydDAMBgNVHRMBAf8EAjAAMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDv
AHUA7ku9t3XOYLrhQmkfq + GeZqMPfl + wctiDAMR7iXqo / csAAAFqxGMTnwAABAMA
RjBEAiAz13zKEoyqd4e / 96 SK / fxfjl7uR + xhfoDZeyA1BvtfOwIgTY + 8 nJMGekv8
leIVdW6AGh7oqH31CIGTAbNJJWzaSFYAdgCHdb / nWXz4jEOZX73zbv9WjUdWNv9K
tWDBtOr / XqCDDwAAAWrEYxTCAAAEAwBHMEUCIQDlWm7 + limbRiurcqUwXav3NSmx
x / aMnolLbh6 + f + b1XAIgQfinHwLw6pDr4R9UkndUsX8QFF4GXS3 / IwRR8HCp + pIw
CgYIKoZIzj0EAwMDaAAwZQIwHg8JmjRtcq + OgV0vVmdVBPqehi1sQJ9PZ + 51 CG + Z
0 GOu + 2 HwS / fyLRViwSc / MZoVAjEA7NgbgpPN4OIsZn2XjMGxemtVxGFS6ZR + 1364
EEeHB9vhZAEjQSePAfjR9aAGhXRa
2014-04-14 13:23:58 -07:00
-- -- - END CERTIFICATE -- -- - `
2016-08-18 16:44:08 -07:00
const selfSigned = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIC / DCCAeSgAwIBAgIRAK0SWRVmi67xU3z0gkgY + PkwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTkxNjMzNDdaFw0xNzA4MTkxNjMz
NDdaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDWkm1kdCwxyKEt6OTmZitkmLGH8cQu9z7rUdrhW8lWNm4kh2SuaUWP
pscBjda5iqg51aoKuWJR2rw6ElDne + X5eit2FT8zJgAU8v39lMFjbaVZfS9TFOYF
w0Tk0Luo / PyKJpZnwhsP ++ iiGQiteJbndy8aLKmJ2MpLfpDGIgxEIyNb5dgoDi0D
WReDCpE6K9WDYqvKVGnQ2Jvqqra6Gfx0tFkuqJxQuqA8aUOlPHcCH4KBZdNEoXdY
YL3E4dCAh0YiDs80wNZx4cHqEM3L8gTEFqW2Tn1TSuPZO6gjJ9QPsuUZVjaMZuuO
NVxqLGujZkDzARhC3fBpptMuaAfi20 + BAgMBAAGjTTBLMA4GA1UdDwEB / wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBYGA1UdEQQPMA2C
C2Zvby5leGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQBPvvfnDhsHWt + / cfwdAVim
4 EDn + hYOMkTQwU0pouYIvY8QXYkZ8MBxpBtBMK4JhFU + ewSWoBAEH2dCCvx / BDxN
UGTSJHMbsvJHcFvdmsvvRxOqQ / cJz7behx0cfoeHMwcs0 / vWv8ms5wHesb5Ek7L0
pl01FCBGTcncVqr6RK1r4fTpeCCfRIERD + YRJz8TtPH6ydesfLL8jIV40H8NiDfG
vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63 + CFty4da3r5lRezeedCE
cw3ESZzThBwWqvPOtJdpXdm + r57pDW8qD + / 0 lY8wfImMNkQAyCUCLg / 1 Lxt / hrBj
-- -- - END CERTIFICATE -- -- - `
2016-08-28 14:11:31 -05:00
2020-06-18 22:45:52 -04:00
const issuerSubjectMatchRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2016-05-30 00:41:03 -07:00
MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1
MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1
siSSOO4mYgLHlPE + oXdqwI / VImi2XeJM2uCFETXCknJJjYG0iJdrt / yyRFvZTQZw
+ QzGj + mz36NqhGxDWb6dstB2m8PX + plZw7jl81MDvUnWs8yiQ / 6 twgu5AbhWKZQD
JKcNKCEpqa6UW0r5nwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB / wQFMAMBAf8wGQYDVR0OBBIE
EEA31wH7QC + 4 HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAb4TfSeCZ1HFmHTKG
VsvqWmsOAGrRWm4fBiMH / 8 vRGnTkJEMLqiqgc3Ulgry / P6n4SIis7TqUOw3TiMhn
RGEz33Fsxa / tFoy / gvlJu + MqB1M2NyV33pGkdwl / b7KRWMQFieqO + uE7Ge / 49 pS3
eyfm5ITdK / WT9TzYhsU4AVZcn20 =
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const issuerSubjectMatchLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2016-05-30 00:41:03 -07:00
MIICODCCAaGgAwIBAgIJAOjwnT / iW + qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV
BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y
NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA20Z9ky4SJwZIvAYoIat + xLaiXf4e
UkWIejZHpQgNkkJbwoHAvpd5mED7T20U / SsTi8KlLmfY1Ame1iI4t0oLdHMrwjTx
0 ZPlltl0e / NYn2xhPMCwQdTZKyskI3dbHDu9dV3OIFTPoWOHHR4kxPMdGlCLqrYU
Q + 2 Xp3Vi9BTIUtcCAwEAAaN3MHUwDgYDVR0PAQH / BAQDAgWgMB0GA1UdJQQWMBQG
CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBCfkRYf
Q0M + SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC + 4 HH5UBCeMWQEwDQYJKoZI
hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I
ynqjmdqDCyK + TwI1kU5dXDe / iSJYfTB20i / QoO53nnfA1hnr7KBjNWqAm4AagN5k
vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ =
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2016-05-30 00:41:03 -07:00
2020-06-18 22:45:52 -04:00
const x509v1TestRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2016-12-14 14:10:26 -08:00
MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAwMDAwMFoXDTI1
MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENB
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1
siSSOO4mYgLHlPE + oXdqwI / VImi2XeJM2uCFETXCknJJjYG0iJdrt / yyRFvZTQZw
+ QzGj + mz36NqhGxDWb6dstB2m8PX + plZw7jl81MDvUnWs8yiQ / 6 twgu5AbhWKZQD
JKcNKCEpqa6UW0r5nwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB / wQFMAMBAf8wGQYDVR0OBBIE
EEA31wH7QC + 4 HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAcIwqeNUpQr9cOcYm
YjpGpYkQ6b248xijCK7zI + lOeWN89zfSXn1AvfsC9pSdTMeDklWktbF / Ad0IN8Md
h2NtN34ard0hEfHc8qW8mkXdsysVmq6cPvFYaHz + dBtkHuHDoy8YQnC0zdN / WyYB
/ 1 JmacUUofl + HusHuLkDxmadogI =
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const x509v1TestIntermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
2016-12-14 14:10:26 -08:00
MIIByjCCATMCCQCCdEMsT8ykqTANBgkqhkiG9w0BAQsFADAjMQ8wDQYDVQQKEwZH
b2xhbmcxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAwMDAwWhcNMjUwMTAx
MDAwMDAwWjAwMQ8wDQYDVQQKEwZHb2xhbmcxHTAbBgNVBAMTFFguNTA5djEgaW50
ZXJtZWRpYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJ2QyniAOT + 5 YL
jeinEBJr3NsC / Q2QJ / VKmgvp + xRxuKTHJiVmxVijmp0vWg8AWfkmuE4p3hXQbbqM
k5yxrk1n60ONhim2L4VXriEvCE7X2OXhTmBls5Ufr7aqIgPMikwjScCXwz8E8qI8
UxyAhnjeJwMYBU8TuwBImSd4LBHoQQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAIab
DRG6FbF9kL9jb / TDHkbVBk + sl / Pxi4 / XjuFyIALlARgAkeZcPmL5tNW1ImHkwsHR
zWE77kJDibzd141u21ZbLsKvEdUJXjla43bdyMmEqf5VGpC3D4sFt3QVH7lGeRur
x5Wlq1u3YDL / j6s1nU2dQ3ySB / oP7J + vQ9V4QeM +
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const x509v1TestLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2016-12-14 14:10:26 -08:00
MIICMzCCAZygAwIBAgIJAPo99mqJJrpJMA0GCSqGSIb3DQEBCwUAMDAxDzANBgNV
BAoTBkdvbGFuZzEdMBsGA1UEAxMUWC41MDl2MSBpbnRlcm1lZGlhdGUwHhcNMTUw
MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjArMQ8wDQYDVQQKEwZHb2xhbmcxGDAW
BgNVBAMTD2Zvby5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEApUh60Z + a5 / oKJxG //Dn8CihSo2CJHNIIO3zEJZ1EeNSMZCynaIR6D3IPZEIR
+ RG2oGt + f5EEukAPYxwasp6VeZEezoQWJ + 97 nPCT6DpwLlWp3i2MF8piK2R9vxkG
Z5n0 + HzYk1VM8epIrZFUXSMGTX8w1y041PX / yYLxbdEifdcCAwEAAaNaMFgwDgYD
VR0PAQH / BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
HRMBAf8EAjAAMBkGA1UdDgQSBBBFozXe0SnzAmjy + 1 U6M / cvMA0GCSqGSIb3DQEB
CwUAA4GBADYzYUvaToO / ucBskPdqXV16AaakIhhSENswYVSl97 / sODaxsjishKq9
5 R7siu + JnIFotA7IbBe633p75xEnLN88X626N / XRFG9iScLzpj0o0PWXBUiB + fxL
/ jt8qszOXCv2vYdUTPNuPqufXLWMoirpuXrr1liJDmedCcAHepY /
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const ignoreCNWithSANRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2017-02-09 15:57:53 -08:00
MIIDPzCCAiegAwIBAgIIJkzCwkNrPHMwDQYJKoZIhvcNAQELBQAwMDEQMA4GA1UE
ChMHVEVTVElORzEcMBoGA1UEAxMTKipUZXN0aW5nKiogUm9vdCBDQTAeFw0xNTAx
MDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMDAxEDAOBgNVBAoTB1RFU1RJTkcxHDAa
BgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC4YAf5YqlXGcikvbMWtVrNICt + V / NNWljwfvSKdg4Inm7k6BwW
P6y4Y + n4qSYIWNU4iRkdpajufzctxQCO6ty13iw3qVktzcC5XBIiS6ymiRhhDgnY
VQqyakVGw9MxrPwdRZVlssUv3Hmy6tU + v5Ok31SLY5z3wKgYWvSyYs0b8bKNU8kf
2 FmSHnBN16lxGdjhe3ji58F / zFMr0ds + HakrLIvVdFcQFAnQopM8FTHpoWNNzGU3
KaiO0jBbMFkd6uVjVnuRJ + xjuiqi / NWwiwQA + CEr9HKzGkxOF8nAsHamdmO1wW + w
OsCrC0qWQ / f5NTOVATTJe0vj88OMTvo3071VAgMBAAGjXTBbMA4GA1UdDwEB / wQE
AwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH / BAUw
AwEB / zAZBgNVHQ4EEgQQQDfXAftAL7gcflQEJ4xZATANBgkqhkiG9w0BAQsFAAOC
AQEAGOn3XjxHyHbXLKrRmpwV447B7iNBXR5VlhwOgt1kWaHDL2 + 8 f / 9 / h0HMkB6j
fC + / yyuYVqYuOeavqMGVrh33D2ODuTQcFlOx5lXukP46j3j + Lm0jjZ1qNX7vlP8I
VlUXERhbelkw8O4oikakwIY9GE8syuSgYf + VeBW / lvuAZQrdnPfabxe05Tre6RXy
nJHMB1q07YHpbwIkcV / lfCE9pig2nPXTLwYZz9cl46Ul5RCpPUi + IKURo3x8y0FU
aSLjI / Ya0zwUARMmyZ3RRGCyhIarPb20mKSaMf1 / Nb23pS3k1QgmZhk5pAnXYsWu
BJ6bvwEAasFiLGP6Zbdmxb2hIA ==
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const ignoreCNWithSANLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2017-02-09 15:57:53 -08:00
MIIDaTCCAlGgAwIBAgIJAONakvRTxgJhMA0GCSqGSIb3DQEBCwUAMDAxEDAOBgNV
BAoTB1RFU1RJTkcxHDAaBgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwHhcNMTUw
MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAsMRAwDgYDVQQKEwdURVNUSU5HMRgw
FgYDVQQDEw9mb28uZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDBqskp89V / JMIBBqcauKSOVLcMyIE / t0jgSWVrsI4sksBTabLsfMdS
ui2n + dHQ1dRBuw3o4g4fPrWwS3nMnV3pZUHEn2TPi5N1xkjTaxObXgKIY2GKmFP3
rJ9vYqHT6mT4K93kCHoRcmJWWySc7S3JAOhTcdB4G + tIdQJN63E + XRYQQfNrn5HZ
hxQoOzaguHFx + ZGSD4Ntk6BSZz5NfjqCYqYxe + iCpTpEEYhIpi8joSPSmkTMTxBW
S1W2gXbYNQ9KjNkGM6FnQsUJrSPMrWs4v3UB / U88N5LkZeF41SqD9ySFGwbGajFV
nyzj12 + 4 K4D8BLhlOc0Eo / F / 8 GwOwvmxAgMBAAGjgYkwgYYwDgYDVR0PAQH / BAQD
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
MBkGA1UdDgQSBBCjeab27q + 5 pV43jBGANOJ1MBsGA1UdIwQUMBKAEEA31wH7QC + 4
HH5UBCeMWQEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGZfZ
ErTVxxpIg64s22mQpXSk / 72 THVQsfsKHzlXmztM0CJzH8ccoN67ZqKxJCfdiE / FI
Emb6BVV4cGPeIKpcxaM2dwX / Y + Y0JaxpQJvqLxs + EByRL0gPP3shgg86WWCjYLxv
AgOn862d / JXGDrC9vIlQ / DDQcyL5g0JV5UjG2G9TUigbnrXxBw7BoWK6wmoSaHnR
sZKEHSs3RUJvm7qqpA9Yfzm9jg + i9j32zh1xFacghAOmFRFXa9eCVeigZ / KK2mEY
j2kBQyvnyKsXHLAKUoUOpd6t / 1 PHrfXnGj + HmzZNloJ / BZ1kiWb4eLvMljoLGkZn
xZbqP3Krgjj4XNaXjg ==
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const excludedNamesLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
2017-02-08 16:27:00 -08:00
MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
ICgzNzM0NTE1NTYyODA2Mzk3KTEhMB8GA1UEAwwYSW50ZXJtZWRpYXRlIENBIGZv
ciAzMzkyMB4XDTE3MDIwODIxMTUwNFoXDTE4MDIwODIwMjQ1OFowgZAxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlMb3MgR2F0b3Mx
FDASBgNVBAoMC05ldGZsaXggSW5jMS0wKwYDVQQLDCRQbGF0Zm9ybSBTZWN1cml0
eSAoMzczNDUxNTc0ODUwMjY5NikxEzARBgNVBAMMCjE3Mi4xNi4wLjEwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ0oP1bMv6bOeqcKbzinnGpNOpenhA
zdFFsgea62znWsH3Wg4 + 1 Md8uPCqlaQIsaJQKZHc50eKD3bg0Io7c6kxHkBQr1b8
Q7cGeK3CjdqG3NwS / aizzrLKOwL693hFwwy7JY7GGCvogbhyQRKn6iV0U9zMm7bu
/ 9 pQVV / wx8u01u2uAlLttjyQ5LJkxo5t8cATFVqxdN5J9eY //VSDiTwXnlpQITBP
/ Ow + zYuZ3kFlzH3CtCOhOEvNG3Ar1NvP3Icq35PlHV + Eki4otnKfixwByoiGpqCB
UEIY04VrZJjwBxk08y / 3 jY2B3VLYGgi + rryyCxIqkB7UpSNPMMWSG4UpAgMBAAGj
LzAtMAwGA1UdEwEB / wQCMAAwHQYDVR0RBBYwFIIMYmVuZGVyLmxvY2FshwSsEAAB
MA0GCSqGSIb3DQEBCwUAA4IBAQCLW3JO8L7LKByjzj2RciPjCGH5XF87Wd20gYLq
sNKcFwCIeyZhnQy5aZ164a5G9AIk2HLvH6HevBFPhA9Ivmyv / wYEfnPd1VcFkpgP
hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV + Rc9JUUYM9TIeERkpl0rskcO1YGewkYt
qKlWE + 0 S16 + pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO + P + yXVw8Q +
VQDnMHUe5PbZd1 / + 1 KKVs1K / CkBCtoHNHp1d / JT + 2 zUQJphwja9CcgfFdVhSnHL4
oEEOFtqVMIuQfR2isi08qW / JGOHc4sFoLYB8hvdaxKWSE19A
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
2017-02-08 16:27:00 -08:00
2020-06-18 22:45:52 -04:00
const excludedNamesIntermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
2017-02-08 16:27:00 -08:00
MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBmb3IgMzM5
MjAeFw0xNzAyMDgyMTE1MDRaFw0xODAyMDgyMDI0NThaMIGeMQswCQYDVQQGEwJV
UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJTG9zIEdhdG9zMRQwEgYD
VQQKDAtOZXRmbGl4IEluYzEtMCsGA1UECwwkUGxhdGZvcm0gU2VjdXJpdHkgKDM3
MzQ1MTU1NjI4MDYzOTcpMSEwHwYDVQQDDBhJbnRlcm1lZGlhdGUgQ0EgZm9yIDMz
OTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCOyEs6tJ / t9emQTvlx
3 FS7uJSou5rKkuqVxZdIuYQ + B2ZviBYUnMRT9bXDB0nsVdKZdp0hdchdiwNXDG / I
CiWu48jkcv / BdynVyayOT + 0 pOJSYLaPYpzBx1Pb9M5651ct9GSbj6Tz0ChVonoIE
1 AIZ0kkebucZRRFHd0xbAKVRKyUzPN6HJ7WfgyauUp7RmlC35wTmrmARrFohQLlL
7 oICy + hIQePMy9x1LSFTbPxZ5AUUXVC3eUACU3vLClF / Xs8XGHebZpUXCdMQjOGS
nq1eFguFHR1poSB8uSmmLqm4vqUH9CDhEgiBAC8yekJ8 //kZQ7lUEqZj3YxVbk+Y
E4H5AgMBAAGjEzARMA8GA1UdEwEB / wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
ADxrnmNX5gWChgX9K5fYwhFDj5ofxZXAKVQk + WjmkwMcmCx3dtWSm ++ Wdksj / ZlA
V1cLW3ohWv1 / OAZuOlw7sLf98aJpX + UUmIYYQxDubq + 4 / q7VA7HzEf2k / i / oN1NI
JgtrhpPcZ / LMO6k7DYx0qlfYq8pTSfd6MI4LnWKgLc + JSPJJjmvspgio2ZFcnYr7
A264BwLo6v1Mos1o1JUvFFcp4GANlw0XFiWh7JXYRl8WmS5DoouUC + aNJ3lmyF6z
LbIjZCSfgZnk / LK1KU1j91FI2bc2ULYZvAC1PAg8 / zvIgxn6YM2Q7ZsdEgWw0FpS
zMBX1 / lk4wkFckeUIlkD55Y =
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const excludedNamesRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2017-02-08 16:27:00 -08:00
MIIEGTCCAwGgAwIBAgIHDUSFpInn / zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
ICgzNzMxNTA5NDM3NDYyNDg1KTEmMCQGA1UEAwwdTmFtZSBDb25zdHJhaW50cyBU
ZXN0IFJvb3QgQ0EwHhcNMTcwMjA4MjExNTA0WhcNMTgwMjA4MjAyNDU4WjCBmTEL
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBH
YXRvczEUMBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNl
Y3VyaXR5ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBm
b3IgMzM5MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJymcnX29ekc
7 + MLyr8QuAzoHWznmGdDd2sITwWRjM89 / 21 cdlHCGKSpULUNdFp9HDLWvYECtxt +
8 TuzKiQz7qAerzGUT1zI5McIjHy0e / i4xIkfiBiNeTCuB / N9QRbZlcfM80ErkaA4
gCAFK8qZAcWkHIl6e + KaQFMPLKk9kckgAnVDHEJe8oLNCogCJ15558b65g05p9eb
5 Lg + E98hoPRTQaDwlz3CZPfTTA2EiEZInSi8qzodFCbTpJUVTbiVUH / JtVjlibbb
smdcx5PORK + 8 ZJkhLEh54AjaWOX4tB / 7 Tkk8stg2VBmrIARt / j4UVj7cTrIWU3bV
m8TwHJG + YgsCAwEAAaNaMFgwDwYDVR0TAQH / BAUwAwEB / zBFBgNVHR4EPjA8oBww
CocICgEAAP //AAAwDoIMYmVuZGVyLmxvY2FsoRwwCocICgEAAP//AAAwDoIMYmVu
ZGVyLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4IBAQAMjbheffPxtSKSv9NySW + 8 qmHs
n7Mb5GGyCFu + cMZSoSaabstbml + zHEFJvWz6 / 1E95 K4F8jKhAcu / CwDf4IZrSD2 +
Hee0DolVSQhZpnHgPyj7ZATz48e3aJaQPUlhCEOh0wwF4Y0N4FV0t7R6woLylYRZ
yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ / Nq7isGvknaTmcIwT
6 mOAFN0qFb4RGzfGJW7x6z7KCULS7qVDp6fU3tRoScHFEgRubks6jzQ1W5ooSm4o
+ NQCZDd5eFeU8PpNX7rgaYE4GPq + EEmLVCBYmdctr8QVdqJ //8Xu3+1phjDy
-- -- - END CERTIFICATE -- -- - `
2020-06-18 22:45:52 -04:00
const invalidCNRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
2018-07-11 15:59:56 -04:00
MIIBFjCBvgIJAIsu4r + jb70UMAoGCCqGSM49BAMCMBQxEjAQBgNVBAsMCVRlc3Qg
cm9vdDAeFw0xODA3MTExODMyMzVaFw0yODA3MDgxODMyMzVaMBQxEjAQBgNVBAsM
CVRlc3Qgcm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6oDgMg0LV6YhPj
QXaPXYCc2cIyCdqp0ROUksRz0pOLTc5iY2nraUheRUD1vRRneq7GeXOVNn7uXONg
oCGMjNwwCgYIKoZIzj0EAwIDRwAwRAIgDSiwgIn8g1lpruYH0QD1GYeoWVunfmrI
XzZZl0eW / ugCICgOfXeZ2GGy3wIC0352BaC3a8r5AAb2XSGNe + e9wNN6
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
const validCNWithoutSAN = ` -- -- - BEGIN CERTIFICATE -- -- -
2018-07-11 15:59:56 -04:00
MIIBJzCBzwIUB7q8t9mrDAL + UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG
A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow
GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd / + sr
p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR
cqUYo0sNUuoV + tCTMDJUS + 0 PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0
h7olHCpY9yMRiz0 =
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
const rootWithoutSKID = ` -- -- - BEGIN CERTIFICATE -- -- -
2019-02-04 18:08:43 -05:00
MIIBbzCCARSgAwIBAgIQeCkq3C8SOX / JM5PqYTl9cDAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE5MDIwNDIyNTYzNFoXDTI5MDIwMTIyNTYzNFow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABISm
jGlTr4dLOWT + BCTm2PzWRjk1DpLcSAh + Al8eB1Nc2eBWxYIH9qPirfatvqBOA4c5
ZwycRpFoaw6O + EmXnVujTDBKMA4GA1UdDwEB / wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH / MBIGA1UdEQQLMAmCB2V4YW1wbGUwCgYI
KoZIzj0EAwIDSQAwRgIhAMaBYWFCjTfn0MNyQ0QXvYT / iIFompkIqzw6wB7qjLrA
AiEA3sn65V7G4tsjZEOpN0Jykn9uiTjqniqn / S / qmv8gIec =
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
const leafWithAKID = ` -- -- - BEGIN CERTIFICATE -- -- -
2019-02-04 18:08:43 -05:00
MIIBjTCCATSgAwIBAgIRAPCKYvADhKLPaWOtcTu2XYwwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0xOTAyMDQyMzA2NTJaFw0yOTAyMDEyMzA2NTJa
MBMxETAPBgNVBAoTCEFjbWUgTExDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
Wk5N + / 8 X97YT6ClFNIE5 / 4 yc2YwKn921l0wrIJEcT2u + Uydm7EqtCJNtZjYMAnBd
Acp / wynpTwC6tBTsxcM0s6NqMGgwDgYDVR0PAQH / BAQDAgWgMBMGA1UdJQQMMAoG
CCsGAQUFBwMBMAwGA1UdEwEB / wQCMAAwHwYDVR0jBBgwFoAUwitfkXg0JglCjW9R
ssWvTAveakIwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNHADBEAiBk
4 LpWiWPOIl5PIhX9PDVkmjpre5oyoH / 3 aYwG8ABYuAIgCeSfbYueOOG2AdXuMqSU
ZZMqeJS7JldLx91sPUArY5A =
2020-06-18 22:45:52 -04:00
-- -- - END CERTIFICATE -- -- - `
crypto/x509: prioritize potential parents in chain building
When building a x509 chain the algorithm currently looks for parents
that have a subject key identifier (SKID) that matches the child
authority key identifier (AKID), if it is present, and returns all
matches. If the child doesn't have an AKID, or there are no parents
with matching SKID it will instead return all parents that have a
subject DN matching the child's issuer DN. Prioritizing AKID/SKID
matches over issuer/subject matches means that later in buildChains we
have to throw away any pairs where these DNs do not match. This also
prevents validation when a child has a SKID with two possible parents,
one with matching AKID but mismatching subject DN, and one with a
matching subject but missing AKID. In this case the former will be
chosen and the latter ignored, meaning a valid chain cannot be built.
This change alters how possible parents are chosen. Instead of doing a
two step search it instead only consults the CertPool.byName subject DN
map, avoiding issues where possible parents may be shadowed by parents
that have SKID but bad subject DNs. Additionally it orders the list of
possible parents by the likelihood that they are in fact a match. This
ordering follows this pattern:
* AKID and SKID match
* AKID present, SKID missing / AKID missing, SKID present
* AKID and SKID don't match
In an ideal world this should save a handful of cycles when there are
multiple possible matching parents by prioritizing parents that have
the highest likelihood. This does diverge from past behavior in that
it also means there are cases where _more_ parents will be considered
than in the past. Another version of this change could just retain the
past behavior, and only consider parents where both the subject and
issuer DNs match, and if both parent and child have SKID and AKID also
compare those, without any prioritization of the candidate parents.
This change removes an existing test case as it assumes that the
CertPool will return a possible candidate where the issuer/subject DNs
do not match.
Fixes #30079
Change-Id: I629f579cabb0b3d0c8cae5ad0429cc5a536b3e58
Reviewed-on: https://go-review.googlesource.com/c/go/+/232993
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2020-05-08 15:57:25 -07:00
const rootMatchingSKIDMismatchingSubject = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBQjCB6aADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQTAe
Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMBExDzANBgNVBAMTBlJvb3Qg
QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPK4p1uXq2aAeDtKDHIokg2rTcPM
2 gq3N9Y96wiW6 / 7 puBK1 + INEW //cO9x6FpzkcsHw/TriAqy4sck/iDAvf9WjMjAw
MA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH / BAUwAwEB / zAMBgNVHQ4EBQQDAQID
MAoGCCqGSM49BAMCA0gAMEUCIQDgtAp7iVHxMnKxZPaLQPC + Tv2r7 + DJc88k2SKH
MPs / wQIgFjjNvBoQEl7vSHTcRGCCcFMdlN4l0Dqc9YwGa9fyrQs =
-- -- - END CERTIFICATE -- -- - `
const rootMismatchingSKIDMatchingSubject = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBNDCB26ADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQjAe
Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMBExDzANBgNVBAMTBlJvb3Qg
QjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI1YRFcIlkWzm9BdEVrIsEQJ2dT6
qiW8 / WV9GoIhmDtX9SEDHospc0Cgm + TeD2QYW2iMrS5mvNe4GSw0Jezg / bOjJDAi
MA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH / BAUwAwEB / zAKBggqhkjOPQQDAgNI
ADBFAiEAukWOiuellx8bugRiwCS5XQ6IOJ1SZcjuZxj76WojwxkCIHqa71qNw8FM
DtA5yoL9M2pDFF6ovFWnaCe + KlzSwAW /
-- -- - END CERTIFICATE -- -- - `
const leafMatchingAKIDMatchingIssuer = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBNTCB26ADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQjAe
Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMA8xDTALBgNVBAMTBExlYWYw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASNWERXCJZFs5vQXRFayLBECdnU + qol
vP1lfRqCIZg7V / UhAx6LKXNAoJvk3g9kGFtojK0uZrzXuBksNCXs4P2zoyYwJDAO
BgNVHSMEBzAFgAMBAgMwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNJ
ADBGAiEAnV9XV7a4h0nfJB8pWv + pBUXRlRFA2uZz3mXEpee8NYACIQCWa + wL70GL
ePBQCV1F9sE2q4ZrnsT9TZoNrSe / bMDjzA ==
-- -- - END CERTIFICATE -- -- - `
2019-02-04 18:08:43 -05:00
2016-08-28 14:11:31 -05:00
var unknownAuthorityErrorTests = [ ] struct {
2023-03-01 08:39:04 -08:00
name string
2016-08-28 14:11:31 -05:00
cert string
expected string
} {
2023-03-01 08:39:04 -08:00
{ "self-signed, cn" , selfSignedWithCommonName , "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"test\")" } ,
{ "self-signed, no cn, org" , selfSignedNoCommonNameWithOrgName , "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"ca\")" } ,
{ "self-signed, no cn, no org" , selfSignedNoCommonNameNoOrgName , "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"serial:0\")" } ,
2016-08-28 14:11:31 -05:00
}
func TestUnknownAuthorityError ( t * testing . T ) {
for i , tt := range unknownAuthorityErrorTests {
2023-03-01 08:39:04 -08:00
t . Run ( tt . name , func ( t * testing . T ) {
der , _ := pem . Decode ( [ ] byte ( tt . cert ) )
if der == nil {
t . Fatalf ( "#%d: Unable to decode PEM block" , i )
}
c , err := ParseCertificate ( der . Bytes )
if err != nil {
t . Fatalf ( "#%d: Unable to parse certificate -> %v" , i , err )
}
uae := & UnknownAuthorityError {
Cert : c ,
hintErr : fmt . Errorf ( "empty" ) ,
hintCert : c ,
}
actual := uae . Error ( )
if actual != tt . expected {
t . Errorf ( "#%d: UnknownAuthorityError.Error() response invalid actual: %s expected: %s" , i , actual , tt . expected )
}
} )
2016-08-28 14:11:31 -05:00
}
}
2016-09-30 16:54:54 -07:00
var nameConstraintTests = [ ] struct {
constraint , domain string
2017-09-09 17:05:41 -07:00
expectError bool
2016-09-30 16:54:54 -07:00
shouldMatch bool
} {
2017-09-09 17:05:41 -07:00
{ "" , "anything.com" , false , true } ,
{ "example.com" , "example.com" , false , true } ,
{ "example.com." , "example.com" , true , false } ,
{ "example.com" , "example.com." , true , false } ,
{ "example.com" , "ExAmPle.coM" , false , true } ,
{ "example.com" , "exampl1.com" , false , false } ,
{ "example.com" , "www.ExAmPle.coM" , false , true } ,
{ "example.com" , "sub.www.ExAmPle.coM" , false , true } ,
{ "example.com" , "notexample.com" , false , false } ,
{ ".example.com" , "example.com" , false , false } ,
{ ".example.com" , "www.example.com" , false , true } ,
{ ".example.com" , "www..example.com" , true , false } ,
2016-09-30 16:54:54 -07:00
}
func TestNameConstraints ( t * testing . T ) {
for i , test := range nameConstraintTests {
2025-10-09 13:35:24 -07:00
result , err := matchDomainConstraint ( test . domain , test . constraint , map [ string ] [ ] string { } , map [ string ] [ ] string { } )
2017-09-09 17:05:41 -07:00
if err != nil && ! test . expectError {
t . Errorf ( "unexpected error for test #%d: domain=%s, constraint=%s, err=%s" , i , test . domain , test . constraint , err )
continue
}
if err == nil && test . expectError {
t . Errorf ( "unexpected success for test #%d: domain=%s, constraint=%s" , i , test . domain , test . constraint )
continue
}
2016-09-30 16:54:54 -07:00
if result != test . shouldMatch {
t . Errorf ( "unexpected result for test #%d: domain=%s, constraint=%s, result=%t" , i , test . domain , test . constraint , result )
}
}
}
2016-08-28 14:11:31 -05:00
const selfSignedWithCommonName = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDCjCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
MAkGA1UEAxMCY2EwHhcNMTYwODI4MTcwOTE4WhcNMjEwODI3MTcwOTE4WjAcMQsw
CQYDVQQKEwJjYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAOH55PfRsbvmcabfLLko1w / yuapY / hk13Cgmc3WE / Z1ZStxGiVxY
gQVH9n4W / TbUsrep / TmcC4MV7xEm5252ArcgaH6BeQ4QOTFj / 6 Jx0RT7U / ix + 79 x
8 RRysf7OlzNpGIctwZEM7i / G + 0 ZfqX9ULxL / EW9tppSxMX1jlXZQarnU7BERL5cH
+ G2jcbU9H28FXYishqpVYE9L7xrXMm61BAwvGKB0jcVW6JdhoAOSfQbbgp7JjIlq
czXqUsv1UdORO / horIoJptynTvuARjZzyWatya6as7wyOgEBllE6BjPK9zpn + lp3
tQ8dwKVqm / qBPhIrVqYG / Ec7pIv8mJfYabMCAwEAAaNZMFcwDgYDVR0PAQH / BAQD
AgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAA
MAoGA1UdDgQDBAEAMAwGA1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAAAM
XMFphzq4S5FBcRdB2fRrmcoz + jEROBWvIH / 1 QUJeBEBz3ZqBaJYfBtQTvqCA5Rjw
dxyIwVd1W3q3aSulM0tO62UCU6L6YeeY / eq8FmpD7nMJo7kCrXUUAMjxbYvS3zkT
v / NErK6SgWnkQiPJBZNX1Q9 + aSbLT / sbaCTdbWqcGNRuLGJkmqfIyoxRt0Hhpqsx
jP5cBaVl50t4qoCuVIE9cOucnxYXnI7X5HpXWvu8Pfxo4SwVjb1az8Fk5s8ZnxGe
fPB6Q3L / pKBe0SEe5GywpwtokPLB3lAygcuHbxp / 1 FlQ1NQZqq + vgXRIla26bNJf
IuYkJwt6w + LH / 9 HZgf8 =
-- -- - END CERTIFICATE -- -- - `
const selfSignedNoCommonNameWithOrgName = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIC + zCCAeOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxMzQ4WhcNMjEwODI3MTgxMzQ4WjANMQsw
CQYDVQQKEwJjYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5EjrUa
7 EtOMxWiIgTzp2FlQvncPsG329O3l3uNGnbigb8TmNMw2M8UhoDjd84pnU5RAfqd
8 t5TJyw / ybnIKBN131Q2xX + gPQ0dFyMvcO + i1CUgCxmYZomKVA2MXO1RD1hLTYGS
gOVjc3no3MBwd8uVQp0NStqJ1QvLtNG4Uy + B28qe + ZFGGbjGqx8 / CU4A8Szlpf7 /
xAZR8w5qFUUlpA2LQYeHHJ5fQVXw7kyL1diNrKNi0G3qcY0IrBh ++ hT + hnEEXyXu
g8a0Ux18hoE8D6rAr34rCZl6AWfqW5wjwm + N5Ns2ugr9U4N8uCKJYMPHb2CtdubU
46 IzVucpTfGLdaMCAwEAAaNZMFcwDgYDVR0PAQH / BAQDAgOoMB0GA1UdJQQWMBQG
CCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGA1UdDgQDBAEAMAwG
A1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAEn5SgVpJ3zjsdzPqK7Qd / sB
bYd1qtPHlrszjhbHBg35C6mDgKhcv4o6N + fuC + FojZb8lIxWzJtvT9pQbfy / V6u3
wOb816Hm71uiP89sioIOKCvSAstj / p9doKDOUaKOcZBTw0PS2m9eja8bnleZzBvK
rD8cNkHf74v98KvBhcwBlDifVzmkWzMG6TL1EkRXUyLKiWgoTUFSkCDV927oXXMR
DKnszq + AVw + K8hbeV2A7GqT7YfeqOAvSbatTDnDtKOPmlCnQui8A149VgZzXv7eU
29 ssJSqjUPyp58dlV6ZuynxPho1QVZUOQgnJToXIQ3 / 5 vIvJRXy52GJCs4 / Gh / w =
-- -- - END CERTIFICATE -- -- - `
const selfSignedNoCommonNameNoOrgName = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIC7jCCAdagAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxOTQ1WhcNMjEwODI3MTgxOTQ1WjAAMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp3E + Jl6DpgzogHUW / i / AAcCM
fnNJLOamNVKFGmmxhb4XTHxRaWoTzrlsyzIMS0WzivvJeZVe6mWbvuP2kZanKgIz
35 YXRTR9HbqkNTMuvnpUESzWxbGWE2jmt2 + a / Jnz89FS4WIYRhF7nI2z8PvZOfrI
2 gETTT2tEpoF2S4soaYfm0DBeT8K0 / rogAaf + oeUS6V + v3miRcAooJgpNJGu9kqm
S0xKPn1RCFVjpiRd6YNS0xZirjYQIBMFBvoSoHjaOdgJptNRBprYPOxVJ / ItzGf0
kPmzPFCx2tKfxV9HLYBPgxi + fP3IIx8aIYuJn8yReWtYEMYU11hDPeAFN5Gm + wID
AQABo1kwVzAOBgNVHQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
AQUFBwMBMAwGA1UdEwEB / wQCMAAwCgYDVR0OBAMEAQAwDAYDVR0jBAUwA4ABADAN
BgkqhkiG9w0BAQsFAAOCAQEATZVOFeiCpPM5QysToLv + 8 k7Rjoqt6L5IxMUJGEpq
4 ENmldmwkhEKr9VnYEJY3njydnnTm97d9vOfnLj9nA9wMBODeOO3KL2uJR2oDnmM
9 z1NSe2aQKnyBb ++ DM3ZdikpHn / xEpGV19pYKFQVn35x3lpPh2XijqRDO / erKemb
w67CoNRb81dy + 4 Q1lGpA8ORoLWh5fIq2t2eNGc4qB8vlTIKiESzAwu7u3sRfuWQi
4 R + gnfLd37FWflMHwztFbVTuNtPOljCX0LN7KcuoXYlr05RhQrmoN7fQHsrZMNLs
8 FVjHdKKu + uPstwd04Uy4BR / H2y1yerN9j / L6ZkMl98iiA ==
-- -- - END CERTIFICATE -- -- - `
2017-10-06 12:46:22 -07:00
const criticalExtRoot = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBqzCCAVGgAwIBAgIJAJ + mI / 85 cXApMAoGCCqGSM49BAMCMB0xDDAKBgNVBAoT
A09yZzENMAsGA1UEAxMEUm9vdDAeFw0xNTAxMDEwMDAwMDBaFw0yNTAxMDEwMDAw
MDBaMB0xDDAKBgNVBAoTA09yZzENMAsGA1UEAxMEUm9vdDBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABJGp9joiG2QSQA + 1 FczEDAsWo84rFiP3GTL + n + ugcS6TyNib
gzMsdbJgVi + a33y0SzLZxB + YvU3 / 4 KTk8yKLC + 2 jejB4MA4GA1UdDwEB / wQEAwIC
BDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH / BAUwAwEB
/ zAZBgNVHQ4EEgQQQDfXAftAL7gcflQEJ4xZATAbBgNVHSMEFDASgBBAN9cB + 0 Av
uBx + VAQnjFkBMAoGCCqGSM49BAMCA0gAMEUCIFeSV00fABFceWR52K + CfIgOHotY
FizzGiLB47hGwjMuAiEA8e0um2Kr8FPQ4wmFKaTRKHMaZizCGl3m + RG5QsE1KWo =
-- -- - END CERTIFICATE -- -- - `
const criticalExtIntermediate = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBszCCAVmgAwIBAgIJAL2kcGZKpzVqMAoGCCqGSM49BAMCMB0xDDAKBgNVBAoT
A09yZzENMAsGA1UEAxMEUm9vdDAeFw0xNTAxMDEwMDAwMDBaFw0yNTAxMDEwMDAw
MDBaMCUxDDAKBgNVBAoTA09yZzEVMBMGA1UEAxMMSW50ZXJtZWRpYXRlMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAESqVq92iPEq01cL4o99WiXDc5GZjpjNlzMS1n
rk8oHcVDp4tQRRQG3F4A6dF1rn / L923ha3b0fhDLlAvXZB + 7 EKN6MHgwDgYDVR0P
AQH / BAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMB
Af8EBTADAQH / MBkGA1UdDgQSBBCMGmiotXbbXVd7H40UsgajMBsGA1UdIwQUMBKA
EEA31wH7QC + 4 HH5UBCeMWQEwCgYIKoZIzj0EAwIDSAAwRQIhAOhhNRb6KV7h3wbE
cdap8bojzvUcPD78fbsQPCNw1jPxAiBOeAJhlTwpKn9KHpeJphYSzydj9NqcS26Y
xXbdbm27KQ ==
-- -- - END CERTIFICATE -- -- - `
const criticalExtLeafWithExt = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBxTCCAWugAwIBAgIJAJZAUtw5ccb1MAoGCCqGSM49BAMCMCUxDDAKBgNVBAoT
A09yZzEVMBMGA1UEAxMMSW50ZXJtZWRpYXRlMB4XDTE1MDEwMTAwMDAwMFoXDTI1
MDEwMTAwMDAwMFowJDEMMAoGA1UEChMDT3JnMRQwEgYDVQQDEwtleGFtcGxlLmNv
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF3ABa2 + B6gUyg6ayCaRQWYY / + No
6 PceLqEavZNUeVNuz7bS74Toy8I7R3bGMkMgbKpLSPlPTroAATvebTXoBaijgYQw
gYEwDgYDVR0PAQH / BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBBRNtBL2vq8nCV3qVp7ycxMMBsGA1Ud
IwQUMBKAEIwaaKi1dttdV3sfjRSyBqMwCgYDUQMEAQH / BAAwCgYIKoZIzj0EAwID
SAAwRQIgVjy8GBgZFiagexEuDLqtGjIRJQtBcf7lYgf6XFPH1h4CIQCT6nHhGo6E
I + crEm4P5q72AnA / Iy0m24l7OvLuXObAmg ==
-- -- - END CERTIFICATE -- -- - `
const criticalExtIntermediateWithExt = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIB2TCCAX6gAwIBAgIIQD3NrSZtcUUwCgYIKoZIzj0EAwIwHTEMMAoGA1UEChMD
T3JnMQ0wCwYDVQQDEwRSb290MB4XDTE1MDEwMTAwMDAwMFoXDTI1MDEwMTAwMDAw
MFowPTEMMAoGA1UEChMDT3JnMS0wKwYDVQQDEyRJbnRlcm1lZGlhdGUgd2l0aCBD
cml0aWNhbCBFeHRlbnNpb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQtnmzH
mcRm10bdDBnJE7xQEJ25cLCL5okuEphRR0Zneo6 + nQZikoh + UBbtt5GV3Dms7LeP
oF5HOplYDCd8wi / wo4GHMIGEMA4GA1UdDwEB / wQEAwICBDAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH / BAUwAwEB / zAZBgNVHQ4EEgQQKxdv
UuQZ6sO3XvBsxgNZ3zAbBgNVHSMEFDASgBBAN9cB + 0 AvuBx + VAQnjFkBMAoGA1ED
BAEB / wQAMAoGCCqGSM49BAMCA0kAMEYCIQCQzTPd6XKex + OAPsKT / 1 DsoMsg8vcG
c2qZ4Q0apT / kvgIhAKu2TnNQMIUdcO0BYQIl + Uhxc78dc9h4lO + YJB47pHGx
-- -- - END CERTIFICATE -- -- - `
const criticalExtLeaf = ` -- -- - BEGIN CERTIFICATE -- -- -
MIIBzzCCAXWgAwIBAgIJANoWFIlhCI9MMAoGCCqGSM49BAMCMD0xDDAKBgNVBAoT
A09yZzEtMCsGA1UEAxMkSW50ZXJtZWRpYXRlIHdpdGggQ3JpdGljYWwgRXh0ZW5z
aW9uMB4XDTE1MDEwMTAwMDAwMFoXDTI1MDEwMTAwMDAwMFowJDEMMAoGA1UEChMD
T3JnMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABG1Lfh8A0Ho2UvZN5H0 + ONil9c8jwtC0y0xIZftyQE + Fwr9XwqG3rV2g4M1h
GnJa9lV9MPHg8 + b85Hixm0ZSw7SjdzB1MA4GA1UdDwEB / wQEAwIFoDAdBgNVHSUE
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH / BAIwADAZBgNVHQ4EEgQQ
UNhY4JhezH9gQYqvDMWrWDAbBgNVHSMEFDASgBArF29S5Bnqw7de8GzGA1nfMAoG
CCqGSM49BAMCA0gAMEUCIQClA3d4tdrDu9Eb5ZBpgyC + fU1xTZB0dKQHz6M5fPZA
2 AIgN96lM + CPGicwhN24uQI6flOsO3H0TJ5lNzBYLtnQtlc =
-- -- - END CERTIFICATE -- -- - `
2018-07-11 15:59:56 -04:00
func TestValidHostname ( t * testing . T ) {
tests := [ ] struct {
2020-04-30 21:24:25 -04:00
host string
validInput , validPattern bool
2018-07-11 15:59:56 -04:00
} {
2020-04-30 21:24:25 -04:00
{ host : "example.com" , validInput : true , validPattern : true } ,
{ host : "eXample123-.com" , validInput : true , validPattern : true } ,
{ host : "-eXample123-.com" } ,
{ host : "" } ,
{ host : "." } ,
{ host : "example..com" } ,
{ host : ".example.com" } ,
{ host : "example.com." , validInput : true } ,
{ host : "*.example.com." } ,
{ host : "*.example.com" , validPattern : true } ,
{ host : "*foo.example.com" } ,
{ host : "foo.*.example.com" } ,
{ host : "exa_mple.com" , validInput : true , validPattern : true } ,
{ host : "foo,bar" } ,
2020-04-30 22:35:35 -04:00
{ host : "project-dev:us-central1:main" } ,
2018-07-11 15:59:56 -04:00
}
for _ , tt := range tests {
2020-04-30 21:24:25 -04:00
if got := validHostnamePattern ( tt . host ) ; got != tt . validPattern {
t . Errorf ( "validHostnamePattern(%q) = %v, want %v" , tt . host , got , tt . validPattern )
}
if got := validHostnameInput ( tt . host ) ; got != tt . validInput {
t . Errorf ( "validHostnameInput(%q) = %v, want %v" , tt . host , got , tt . validInput )
2018-07-11 15:59:56 -04:00
}
}
}
2018-12-04 22:23:22 -05:00
func generateCert ( cn string , isCA bool , issuer * Certificate , issuerKey crypto . PrivateKey ) ( * Certificate , crypto . PrivateKey , error ) {
priv , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
return nil , nil , err
}
serialNumberLimit := new ( big . Int ) . Lsh ( big . NewInt ( 1 ) , 128 )
serialNumber , _ := rand . Int ( rand . Reader , serialNumberLimit )
template := & Certificate {
SerialNumber : serialNumber ,
Subject : pkix . Name { CommonName : cn } ,
NotBefore : time . Now ( ) . Add ( - 1 * time . Hour ) ,
NotAfter : time . Now ( ) . Add ( 24 * time . Hour ) ,
KeyUsage : KeyUsageKeyEncipherment | KeyUsageDigitalSignature | KeyUsageCertSign ,
ExtKeyUsage : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
BasicConstraintsValid : true ,
IsCA : isCA ,
}
if issuer == nil {
issuer = template
issuerKey = priv
}
derBytes , err := CreateCertificate ( rand . Reader , template , issuer , priv . Public ( ) , issuerKey )
if err != nil {
return nil , nil , err
}
cert , err := ParseCertificate ( derBytes )
if err != nil {
return nil , nil , err
}
return cert , priv , nil
}
func TestPathologicalChain ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping generation of a long chain of certificates in short mode" )
}
// Build a chain where all intermediates share the same subject, to hit the
// path building worst behavior.
roots , intermediates := NewCertPool ( ) , NewCertPool ( )
parent , parentKey , err := generateCert ( "Root CA" , true , nil , nil )
if err != nil {
t . Fatal ( err )
}
roots . AddCert ( parent )
for i := 1 ; i < 100 ; i ++ {
parent , parentKey , err = generateCert ( "Intermediate CA" , true , parent , parentKey )
if err != nil {
t . Fatal ( err )
}
intermediates . AddCert ( parent )
}
leaf , _ , err := generateCert ( "Leaf" , false , parent , parentKey )
if err != nil {
t . Fatal ( err )
}
start := time . Now ( )
_ , err = leaf . Verify ( VerifyOptions {
Roots : roots ,
Intermediates : intermediates ,
} )
t . Logf ( "verification took %v" , time . Since ( start ) )
if err == nil || ! strings . Contains ( err . Error ( ) , "signature check attempts limit" ) {
t . Errorf ( "expected verification to fail with a signature checks limit error; got %v" , err )
}
}
func TestLongChain ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping generation of a long chain of certificates in short mode" )
}
roots , intermediates := NewCertPool ( ) , NewCertPool ( )
parent , parentKey , err := generateCert ( "Root CA" , true , nil , nil )
if err != nil {
t . Fatal ( err )
}
roots . AddCert ( parent )
for i := 1 ; i < 15 ; i ++ {
name := fmt . Sprintf ( "Intermediate CA #%d" , i )
parent , parentKey , err = generateCert ( name , true , parent , parentKey )
if err != nil {
t . Fatal ( err )
}
intermediates . AddCert ( parent )
}
leaf , _ , err := generateCert ( "Leaf" , false , parent , parentKey )
if err != nil {
t . Fatal ( err )
}
start := time . Now ( )
if _ , err := leaf . Verify ( VerifyOptions {
Roots : roots ,
Intermediates : intermediates ,
} ) ; err != nil {
t . Error ( err )
}
t . Logf ( "verification took %v" , time . Since ( start ) )
}
2020-06-18 22:45:52 -04:00
func TestSystemRootsError ( t * testing . T ) {
2021-10-01 09:14:10 -07:00
if runtime . GOOS == "windows" || runtime . GOOS == "darwin" || runtime . GOOS == "ios" {
crypto/x509: use platform verifier on darwin
When VerifyOptions.Roots is nil, default to using the platform X.509
certificate verification APIs on darwin, rather than using the Go
verifier. Since our oldest supported version of macOS is 10.12, we are
able to use the modern verification APIs, and don't need to resort to
the complex chain building trickery employed by chromium et al.
Unfortunately there is not a clean way to programmatically add test
roots to the system trust store that the builders would tolerate. The
most obvious solution, using 'security add-trusted-cert' requires human
interaction for authorization. We could also manually add anchors to
the constructed SecTrustRef, but that would require adding a whole
bunch of plumbing for test functionality, and would mean we weren't
really testing the actual non-test path. The path I've chosen here is
to just utilize existing valid, and purposefully invalid, trusted
chains, from google.com and the badssl.com test suite. This requires
external network access, but most accurately reflects real world
contexts.
This change removes the x509.SystemCertPool() functionality, which will
be ammended in a follow-up change which supports the suggested hybrid
pool approach described in #46287.
Updates #46287
Fixes #42414
Fixes #38888
Fixes #35631
Fixes #19561
Change-Id: I17f0d6c5cb3ef8a1f2731ce3296478b28d30df46
Reviewed-on: https://go-review.googlesource.com/c/go/+/353132
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
2021-09-29 11:31:01 -07:00
t . Skip ( "Windows and darwin do not use (or support) systemRoots" )
2020-06-18 22:45:52 -04:00
}
defer func ( oldSystemRoots * CertPool ) { systemRoots = oldSystemRoots } ( systemRootsPool ( ) )
opts := VerifyOptions {
Intermediates : NewCertPool ( ) ,
DNSName : "www.google.com" ,
2023-02-28 13:23:43 -08:00
CurrentTime : time . Unix ( 1677615892 , 0 ) ,
2020-06-18 22:45:52 -04:00
}
2023-02-28 13:23:43 -08:00
if ok := opts . Intermediates . AppendCertsFromPEM ( [ ] byte ( gtsIntermediate ) ) ; ! ok {
2020-06-18 22:45:52 -04:00
t . Fatalf ( "failed to parse intermediate" )
}
leaf , err := certificateFromPEM ( googleLeaf )
if err != nil {
t . Fatalf ( "failed to parse leaf: %v" , err )
}
systemRoots = nil
_ , err = leaf . Verify ( opts )
if _ , ok := err . ( SystemRootsError ) ; ! ok {
t . Errorf ( "error was not SystemRootsError: %v" , err )
}
}
2020-10-14 19:42:13 +00:00
func TestSystemRootsErrorUnwrap ( t * testing . T ) {
var err1 = errors . New ( "err1" )
err := SystemRootsError { Err : err1 }
if ! errors . Is ( err , err1 ) {
t . Error ( "errors.Is failed, wanted success" )
}
}
2022-03-17 09:39:46 -07:00
2023-12-12 09:28:03 -08:00
func macosMajorVersion ( t * testing . T ) ( int , error ) {
cmd := testenv . Command ( t , "sw_vers" , "-productVersion" )
out , err := cmd . Output ( )
if err != nil {
if ee , ok := err . ( * exec . ExitError ) ; ok && len ( ee . Stderr ) > 0 {
return 0 , fmt . Errorf ( "%v: %v\n%s" , cmd , err , ee . Stderr )
}
return 0 , fmt . Errorf ( "%v: %v" , cmd , err )
}
before , _ , ok := strings . Cut ( string ( out ) , "." )
major , err := strconv . Atoi ( before )
if ! ok || err != nil {
return 0 , fmt . Errorf ( "%v: unexpected output: %q" , cmd , out )
}
return major , nil
}
2022-03-17 09:39:46 -07:00
func TestIssue51759 ( t * testing . T ) {
2023-02-28 13:23:43 -08:00
if runtime . GOOS != "darwin" {
t . Skip ( "only affects darwin" )
}
2023-12-12 09:28:03 -08:00
testenv . MustHaveExecPath ( t , "sw_vers" )
if vers , err := macosMajorVersion ( t ) ; err != nil {
if builder := testenv . Builder ( ) ; builder != "" {
t . Fatalf ( "unable to determine macOS version: %s" , err )
} else {
t . Skip ( "unable to determine macOS version" )
}
} else if vers < 11 {
2023-03-01 08:39:04 -08:00
t . Skip ( "behavior only enforced in macOS 11 and after" )
}
2023-12-12 09:28:03 -08:00
2022-03-17 09:39:46 -07:00
// badCertData contains a cert that we parse as valid
// but that macOS SecCertificateCreateWithData rejects.
const badCertData = "0\x82\x01U0\x82\x01\a\xa0\x03\x02\x01\x02\x02\x01\x020\x05\x06\x03+ep0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260\x1e\x17\r220112235755Z\x17\r220313235755Z0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260*0\x05\x06\x03+ep\x03!\x00bA\xd8e\xadW\xcb\xefZ\x89\xb5\"\x1eR\x9d\xba\x0e:\x1042Q@\u007f\xbd\xfb{ks\x04\xd1£\x020\x000\x05\x06\x03+ep\x03A\x00[\xa7\x06y\x86(\x94\x97\x9eLwA\x00\x01x\xaa\xbc\xbd Ê]\n(΅!ف0\xf5\x9a%I\x19<\xffo\xf1\xeaaf@\xb1\xa7\xaf\xfd\xe9R\xc7\x0f\x8d&\xd5\xfc\x0f;Ϙ\x82\x84a\xbc\r"
badCert , err := ParseCertificate ( [ ] byte ( badCertData ) )
if err != nil {
t . Fatal ( err )
}
t . Run ( "leaf" , func ( t * testing . T ) {
opts := VerifyOptions { }
2023-03-01 08:39:04 -08:00
expectedErr := "invalid leaf certificate"
2022-03-17 09:39:46 -07:00
_ , err = badCert . Verify ( opts )
2023-03-01 08:39:04 -08:00
if err == nil || err . Error ( ) != expectedErr {
2023-02-28 13:23:43 -08:00
t . Fatalf ( "unexpected error: want %q, got %q" , expectedErr , err )
2022-03-17 09:39:46 -07:00
}
} )
goodCert , err := certificateFromPEM ( googleLeaf )
if err != nil {
t . Fatal ( err )
}
t . Run ( "intermediate" , func ( t * testing . T ) {
opts := VerifyOptions {
Intermediates : NewCertPool ( ) ,
}
opts . Intermediates . AddCert ( badCert )
2023-03-01 08:39:04 -08:00
expectedErr := "SecCertificateCreateWithData: invalid certificate"
2022-03-17 09:39:46 -07:00
_ , err = goodCert . Verify ( opts )
2023-03-01 08:39:04 -08:00
if err == nil || err . Error ( ) != expectedErr {
2023-02-28 13:23:43 -08:00
t . Fatalf ( "unexpected error: want %q, got %q" , expectedErr , err )
2022-03-17 09:39:46 -07:00
}
} )
}
2022-03-02 13:20:02 -08:00
type trustGraphEdge struct {
Issuer string
Subject string
Type int
MutateTemplate func ( * Certificate )
2023-08-14 10:44:29 -07:00
Constraint func ( [ ] * Certificate ) error
2022-03-02 13:20:02 -08:00
}
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
type rootDescription struct {
Subject string
MutateTemplate func ( * Certificate )
2023-08-14 10:44:29 -07:00
Constraint func ( [ ] * Certificate ) error
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
}
2022-03-02 13:20:02 -08:00
type trustGraphDescription struct {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots [ ] rootDescription
2022-03-02 13:20:02 -08:00
Leaf string
Graph [ ] trustGraphEdge
}
func genCertEdge ( t * testing . T , subject string , key crypto . Signer , mutateTmpl func ( * Certificate ) , certType int , issuer * Certificate , signer crypto . Signer ) * Certificate {
t . Helper ( )
serial , err := rand . Int ( rand . Reader , big . NewInt ( 100 ) )
if err != nil {
t . Fatalf ( "failed to generate test serial: %s" , err )
}
tmpl := & Certificate {
SerialNumber : serial ,
Subject : pkix . Name { CommonName : subject } ,
NotBefore : time . Now ( ) . Add ( - time . Hour ) ,
NotAfter : time . Now ( ) . Add ( time . Hour ) ,
}
if certType == rootCertificate || certType == intermediateCertificate {
tmpl . IsCA , tmpl . BasicConstraintsValid = true , true
tmpl . KeyUsage = KeyUsageCertSign
} else if certType == leafCertificate {
tmpl . DNSNames = [ ] string { "localhost" }
}
if mutateTmpl != nil {
mutateTmpl ( tmpl )
}
if certType == rootCertificate {
issuer = tmpl
signer = key
}
d , err := CreateCertificate ( rand . Reader , tmpl , issuer , key . Public ( ) , signer )
if err != nil {
t . Fatalf ( "failed to generate test cert: %s" , err )
}
c , err := ParseCertificate ( d )
if err != nil {
t . Fatalf ( "failed to parse test cert: %s" , err )
}
return c
}
func buildTrustGraph ( t * testing . T , d trustGraphDescription ) ( * CertPool , * CertPool , * Certificate ) {
t . Helper ( )
certs := map [ string ] * Certificate { }
keys := map [ string ] crypto . Signer { }
2023-08-14 10:44:29 -07:00
rootPool := NewCertPool ( )
2022-03-02 13:20:02 -08:00
for _ , r := range d . Roots {
k , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatalf ( "failed to generate test key: %s" , err )
}
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
root := genCertEdge ( t , r . Subject , k , r . MutateTemplate , rootCertificate , nil , nil )
2023-08-14 10:44:29 -07:00
if r . Constraint != nil {
rootPool . AddCertWithConstraint ( root , r . Constraint )
} else {
rootPool . AddCert ( root )
}
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
certs [ r . Subject ] = root
keys [ r . Subject ] = k
2022-03-02 13:20:02 -08:00
}
2023-08-14 10:44:29 -07:00
intermediatePool := NewCertPool ( )
2022-03-02 13:20:02 -08:00
var leaf * Certificate
for _ , e := range d . Graph {
issuerCert , ok := certs [ e . Issuer ]
if ! ok {
t . Fatalf ( "unknown issuer %s" , e . Issuer )
}
issuerKey , ok := keys [ e . Issuer ]
if ! ok {
t . Fatalf ( "unknown issuer %s" , e . Issuer )
}
k , ok := keys [ e . Subject ]
if ! ok {
var err error
k , err = ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatalf ( "failed to generate test key: %s" , err )
}
keys [ e . Subject ] = k
}
cert := genCertEdge ( t , e . Subject , k , e . MutateTemplate , e . Type , issuerCert , issuerKey )
certs [ e . Subject ] = cert
if e . Subject == d . Leaf {
leaf = cert
} else {
2023-08-14 10:44:29 -07:00
if e . Constraint != nil {
intermediatePool . AddCertWithConstraint ( cert , e . Constraint )
} else {
intermediatePool . AddCert ( cert )
}
2022-03-02 13:20:02 -08:00
}
}
return rootPool , intermediatePool , leaf
}
func chainsToStrings ( chains [ ] [ ] * Certificate ) [ ] string {
chainStrings := [ ] string { }
for _ , chain := range chains {
names := [ ] string { }
for _ , c := range chain {
names = append ( names , c . Subject . String ( ) )
}
chainStrings = append ( chainStrings , strings . Join ( names , " -> " ) )
}
2024-05-22 13:38:40 -07:00
slices . Sort ( chainStrings )
2022-03-02 13:20:02 -08:00
return chainStrings
}
func TestPathBuilding ( t * testing . T ) {
tests := [ ] struct {
name string
graph trustGraphDescription
expectedChains [ ] string
expectedErr string
} {
{
// Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
// certificates where the parent is the issuer and the child is the subject.) For the certificate
// C->B, use an unsupported ExtKeyUsage (in this case ExtKeyUsageCodeSigning) which invalidates
// the path Trust Anchor -> C -> B -> EE. The remaining valid paths should be:
// * Trust Anchor -> A -> B -> EE
// * Trust Anchor -> C -> A -> B -> EE
//
// +---------+
// | Trust |
// | Anchor |
// +---------+
// | |
// v v
// +---+ +---+
// | A |<-->| C |
// +---+ +---+
// | |
// | +---+ |
// +->| B |<-+
// +---+
// |
// v
// +----+
// | EE |
// +----+
name : "bad EKU" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-03-02 13:20:02 -08:00
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "root" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . ExtKeyUsage = [ ] ExtKeyUsage { ExtKeyUsageCodeSigning }
} ,
} ,
{
Issuer : "inter a" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter b" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root" ,
"CN=leaf -> CN=inter b -> CN=inter a -> CN=root" ,
} ,
} ,
{
// Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
// certificates where the parent is the issuer and the child is the subject.) For the certificate
// C->B, use a unconstrained SAN which invalidates the path Trust Anchor -> C -> B -> EE. The
// remaining valid paths should be:
// * Trust Anchor -> A -> B -> EE
// * Trust Anchor -> C -> A -> B -> EE
//
// +---------+
// | Trust |
// | Anchor |
// +---------+
// | |
// v v
// +---+ +---+
// | A |<-->| C |
// +---+ +---+
// | |
// | +---+ |
// +->| B |<-+
// +---+
// |
// v
// +----+
// | EE |
// +----+
name : "bad EKU" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-03-02 13:20:02 -08:00
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "root" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . PermittedDNSDomains = [ ] string { "good" }
t . DNSNames = [ ] string { "bad" }
} ,
} ,
{
Issuer : "inter a" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter b" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root" ,
"CN=leaf -> CN=inter b -> CN=inter a -> CN=root" ,
} ,
} ,
{
// Build the following graph, we should find both paths:
// * Trust Anchor -> A -> C -> EE
// * Trust Anchor -> A -> B -> C -> EE
//
// +---------+
// | Trust |
// | Anchor |
// +---------+
// |
// v
// +---+
// | A |
// +---+
// | |
// | +----+
// | v
// | +---+
// | | B |
// | +---+
// | |
// | +---v
// v v
// +---+
// | C |
// +---+
// |
// v
// +----+
// | EE |
// +----+
name : "all paths" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-03-02 13:20:02 -08:00
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter b" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=leaf -> CN=inter c -> CN=inter a -> CN=root" ,
"CN=leaf -> CN=inter c -> CN=inter b -> CN=inter a -> CN=root" ,
} ,
} ,
{
// Build the following graph, which contains a cross-signature loop
// (A and C cross sign each other). Paths that include the A -> C -> A
// (and vice versa) loop should be ignored, resulting in the paths:
// * Trust Anchor -> A -> B -> EE
// * Trust Anchor -> C -> B -> EE
// * Trust Anchor -> A -> C -> B -> EE
// * Trust Anchor -> C -> A -> B -> EE
//
// +---------+
// | Trust |
// | Anchor |
// +---------+
// | |
// v v
// +---+ +---+
// | A |<-->| C |
// +---+ +---+
// | |
// | +---+ |
// +->| B |<-+
// +---+
// |
// v
// +----+
// | EE |
// +----+
name : "ignore cross-sig loops" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-03-02 13:20:02 -08:00
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "root" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter b" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root" ,
"CN=leaf -> CN=inter b -> CN=inter a -> CN=root" ,
"CN=leaf -> CN=inter b -> CN=inter c -> CN=inter a -> CN=root" ,
"CN=leaf -> CN=inter b -> CN=inter c -> CN=root" ,
} ,
} ,
2022-04-19 12:05:10 -07:00
{
// Build a simple two node graph, where the leaf is directly issued from
// the root and both certificates have matching subject and public key, but
// the leaf has SANs.
name : "leaf with same subject, key, as parent but with SAN" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-04-19 12:05:10 -07:00
Leaf : "root" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "root" ,
Type : leafCertificate ,
MutateTemplate : func ( c * Certificate ) {
c . DNSNames = [ ] string { "localhost" }
} ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=root -> CN=root" ,
} ,
} ,
2022-05-02 12:00:36 -07:00
{
// Build a basic graph with two paths from leaf to root, but the path passing
// through C should be ignored, because it has invalid EKU nesting.
name : "ignore invalid EKU path" ,
graph : trustGraphDescription {
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
Roots : [ ] rootDescription { { Subject : "root" } } ,
2022-05-02 12:00:36 -07:00
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "root" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . ExtKeyUsage = [ ] ExtKeyUsage { ExtKeyUsageCodeSigning }
} ,
} ,
{
Issuer : "inter a" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . ExtKeyUsage = [ ] ExtKeyUsage { ExtKeyUsageServerAuth }
} ,
} ,
{
Issuer : "inter b" ,
Subject : "leaf" ,
Type : leafCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . ExtKeyUsage = [ ] ExtKeyUsage { ExtKeyUsageServerAuth }
} ,
} ,
} ,
} ,
expectedChains : [ ] string {
"CN=leaf -> CN=inter b -> CN=inter a -> CN=root" ,
} ,
} ,
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
{
// A name constraint on the root should apply to any names that appear
// on the intermediate, meaning there is no valid chain.
2023-06-13 23:01:11 +00:00
name : "constrained root, invalid intermediate" ,
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
graph : trustGraphDescription {
Roots : [ ] rootDescription {
{
Subject : "root" ,
MutateTemplate : func ( t * Certificate ) {
t . PermittedDNSDomains = [ ] string { "example.com" }
} ,
} ,
} ,
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . DNSNames = [ ] string { "beep.com" }
} ,
} ,
{
Issuer : "inter" ,
Subject : "leaf" ,
Type : leafCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . DNSNames = [ ] string { "www.example.com" }
} ,
} ,
} ,
} ,
expectedErr : "x509: a root or intermediate certificate is not authorized to sign for this name: DNS name \"beep.com\" is not permitted by any constraint" ,
} ,
{
// A name constraint on the intermediate does not apply to the intermediate
// itself, so this is a valid chain.
2023-06-13 23:01:11 +00:00
name : "constrained intermediate, non-matching SAN" ,
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
graph : trustGraphDescription {
Roots : [ ] rootDescription { { Subject : "root" } } ,
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter" ,
Type : intermediateCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . DNSNames = [ ] string { "beep.com" }
t . PermittedDNSDomains = [ ] string { "example.com" }
} ,
} ,
{
Issuer : "inter" ,
Subject : "leaf" ,
Type : leafCertificate ,
MutateTemplate : func ( t * Certificate ) {
t . DNSNames = [ ] string { "www.example.com" }
} ,
} ,
} ,
} ,
expectedChains : [ ] string { "CN=leaf -> CN=inter -> CN=root" } ,
} ,
2023-08-14 10:44:29 -07:00
{
// A code constraint on the root, applying to one of two intermediates in the graph, should
// result in only one valid chain.
name : "code constrained root, two paths, one valid" ,
graph : trustGraphDescription {
Roots : [ ] rootDescription { { Subject : "root" , Constraint : func ( chain [ ] * Certificate ) error {
for _ , c := range chain {
if c . Subject . CommonName == "inter a" {
return errors . New ( "bad" )
}
}
return nil
} } } ,
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter a" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "root" ,
Subject : "inter b" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter a" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter b" ,
Subject : "inter c" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter c" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedChains : [ ] string { "CN=leaf -> CN=inter c -> CN=inter b -> CN=root" } ,
} ,
{
// A code constraint on the root, applying to the only path, should result in an error.
name : "code constrained root, one invalid path" ,
graph : trustGraphDescription {
Roots : [ ] rootDescription { { Subject : "root" , Constraint : func ( chain [ ] * Certificate ) error {
for _ , c := range chain {
if c . Subject . CommonName == "leaf" {
return errors . New ( "bad" )
}
}
return nil
} } } ,
Leaf : "leaf" ,
Graph : [ ] trustGraphEdge {
{
Issuer : "root" ,
Subject : "inter" ,
Type : intermediateCertificate ,
} ,
{
Issuer : "inter" ,
Subject : "leaf" ,
Type : leafCertificate ,
} ,
} ,
} ,
expectedErr : "x509: certificate signed by unknown authority (possibly because of \"bad\" while trying to verify candidate authority certificate \"root\")" ,
} ,
2022-03-02 13:20:02 -08:00
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
roots , intermediates , leaf := buildTrustGraph ( t , tc . graph )
chains , err := leaf . Verify ( VerifyOptions {
Roots : roots ,
Intermediates : intermediates ,
} )
if err != nil && err . Error ( ) != tc . expectedErr {
t . Fatalf ( "unexpected error: got %q, want %q" , err , tc . expectedErr )
}
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new
certificate is considered for inclusion in a chain we check if it has
name constraints, and if it does, check that they apply to the certs
already in the chain, discarding it if the current chain violates any
of the constraints the candidate introduces.
This check was not acting as intended in two ways. The first was that
we only checked that the constraints on the candidate certificate
applied to the leaf certificate, and not the rest of the certiifcates in
the chain. This was the intended behavior pre-1.19, but in 1.19 we
intended for the constraints to be applied to the entire chain (although
obviously they were not).
The second was that we checked that the candidates constraints applied
to the candidate itself. This is not conformant with RFC 5280, which
says that during path building the constraint should only be applied to
the certificates which follow the certificate which introduces the
constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a
name constraint, the constraint should only apply to certificate C).
The intended behavior introduced in 1.19 was mainly intended to reject
dubious chains which the WebPKI disallows, and are relatively rare, but
don't have significant security impact. Since the constraints were
properly applied to the leaf certificate, there should be no real impact
to the majority of users.
Fixes #59171
Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478216
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-03-21 13:45:18 -07:00
if len ( tc . expectedChains ) == 0 {
return
}
2022-03-02 13:20:02 -08:00
gotChains := chainsToStrings ( chains )
2024-07-24 10:32:31 +00:00
if ! slices . Equal ( gotChains , tc . expectedChains ) {
2022-03-02 13:20:02 -08:00
t . Errorf ( "unexpected chains returned:\ngot:\n\t%s\nwant:\n\t%s" , strings . Join ( gotChains , "\n\t" ) , strings . Join ( tc . expectedChains , "\n\t" ) )
}
} )
}
}
2022-05-02 12:00:36 -07:00
func TestEKUEnforcement ( t * testing . T ) {
type ekuDescs struct {
EKUs [ ] ExtKeyUsage
Unknown [ ] asn1 . ObjectIdentifier
}
tests := [ ] struct {
name string
root ekuDescs
inters [ ] ekuDescs
leaf ekuDescs
verifyEKUs [ ] ExtKeyUsage
err string
} {
{
name : "valid, full chain" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
inters : [ ] ekuDescs { ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
} ,
{
name : "valid, only leaf has EKU" ,
root : ekuDescs { } ,
inters : [ ] ekuDescs { ekuDescs { } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
} ,
{
name : "invalid, serverAuth not nested" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageClientAuth } } ,
inters : [ ] ekuDescs { ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
err : "x509: certificate specifies an incompatible key usage" ,
} ,
{
name : "valid, two EKUs, one path" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
inters : [ ] ekuDescs { ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } ,
} ,
{
name : "invalid, ladder" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
inters : [ ] ekuDescs {
ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } ,
ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageClientAuth } } ,
ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } } ,
ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
} ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } ,
err : "x509: certificate specifies an incompatible key usage" ,
} ,
{
name : "valid, intermediate has no EKU" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
inters : [ ] ekuDescs { ekuDescs { } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
} ,
{
name : "invalid, intermediate has no EKU and no nested path" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageClientAuth } } ,
inters : [ ] ekuDescs { ekuDescs { } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth , ExtKeyUsageClientAuth } ,
err : "x509: certificate specifies an incompatible key usage" ,
} ,
{
name : "invalid, intermediate has unknown EKU" ,
root : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
inters : [ ] ekuDescs { ekuDescs { Unknown : [ ] asn1 . ObjectIdentifier { { 1 , 2 , 3 } } } } ,
leaf : ekuDescs { EKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
err : "x509: certificate specifies an incompatible key usage" ,
} ,
}
k , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatalf ( "failed to generate test key: %s" , err )
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
rootPool := NewCertPool ( )
root := genCertEdge ( t , "root" , k , func ( c * Certificate ) {
c . ExtKeyUsage = tc . root . EKUs
c . UnknownExtKeyUsage = tc . root . Unknown
} , rootCertificate , nil , k )
rootPool . AddCert ( root )
parent := root
interPool := NewCertPool ( )
for i , interEKUs := range tc . inters {
inter := genCertEdge ( t , fmt . Sprintf ( "inter %d" , i ) , k , func ( c * Certificate ) {
c . ExtKeyUsage = interEKUs . EKUs
c . UnknownExtKeyUsage = interEKUs . Unknown
} , intermediateCertificate , parent , k )
interPool . AddCert ( inter )
parent = inter
}
leaf := genCertEdge ( t , "leaf" , k , func ( c * Certificate ) {
c . ExtKeyUsage = tc . leaf . EKUs
c . UnknownExtKeyUsage = tc . leaf . Unknown
} , intermediateCertificate , parent , k )
_ , err := leaf . Verify ( VerifyOptions { Roots : rootPool , Intermediates : interPool , KeyUsages : tc . verifyEKUs } )
if err == nil && tc . err != "" {
t . Errorf ( "expected error" )
} else if err != nil && err . Error ( ) != tc . err {
2024-11-16 11:17:54 -08:00
t . Errorf ( "unexpected error: got %q, want %q" , err . Error ( ) , tc . err )
2022-05-02 12:00:36 -07:00
}
} )
}
}
func TestVerifyEKURootAsLeaf ( t * testing . T ) {
k , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatalf ( "failed to generate key: %s" , err )
}
for _ , tc := range [ ] struct {
rootEKUs [ ] ExtKeyUsage
verifyEKUs [ ] ExtKeyUsage
succeed bool
} {
{
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
succeed : true ,
} ,
{
rootEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
succeed : true ,
} ,
{
rootEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
succeed : true ,
} ,
{
rootEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageAny } ,
succeed : true ,
} ,
{
rootEKUs : [ ] ExtKeyUsage { ExtKeyUsageAny } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
succeed : true ,
} ,
{
rootEKUs : [ ] ExtKeyUsage { ExtKeyUsageClientAuth } ,
verifyEKUs : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
succeed : false ,
} ,
} {
t . Run ( fmt . Sprintf ( "root EKUs %#v, verify EKUs %#v" , tc . rootEKUs , tc . verifyEKUs ) , func ( t * testing . T ) {
tmpl := & Certificate {
SerialNumber : big . NewInt ( 1 ) ,
Subject : pkix . Name { CommonName : "root" } ,
NotBefore : time . Now ( ) . Add ( - time . Hour ) ,
NotAfter : time . Now ( ) . Add ( time . Hour ) ,
DNSNames : [ ] string { "localhost" } ,
ExtKeyUsage : tc . rootEKUs ,
}
rootDER , err := CreateCertificate ( rand . Reader , tmpl , tmpl , k . Public ( ) , k )
if err != nil {
t . Fatalf ( "failed to create certificate: %s" , err )
}
root , err := ParseCertificate ( rootDER )
if err != nil {
t . Fatalf ( "failed to parse certificate: %s" , err )
}
roots := NewCertPool ( )
roots . AddCert ( root )
_ , err = root . Verify ( VerifyOptions { Roots : roots , KeyUsages : tc . verifyEKUs } )
if err == nil && ! tc . succeed {
t . Error ( "verification succeed" )
} else if err != nil && tc . succeed {
t . Errorf ( "verification failed: %q" , err )
}
} )
}
}
2024-01-18 12:51:13 -08:00
func TestVerifyNilPubKey ( t * testing . T ) {
c := & Certificate {
RawIssuer : [ ] byte { 1 , 2 , 3 } ,
AuthorityKeyId : [ ] byte { 1 , 2 , 3 } ,
}
opts := & VerifyOptions { }
opts . Roots = NewCertPool ( )
r := & Certificate {
RawSubject : [ ] byte { 1 , 2 , 3 } ,
SubjectKeyId : [ ] byte { 1 , 2 , 3 } ,
}
opts . Roots . AddCert ( r )
_ , err := c . buildChains ( [ ] * Certificate { r } , nil , opts )
if _ , ok := err . ( UnknownAuthorityError ) ; ! ok {
t . Fatalf ( "buildChains returned unexpected error, got: %v, want %v" , err , UnknownAuthorityError { } )
}
}
2024-05-13 13:20:37 -07:00
func TestVerifyBareWildcard ( t * testing . T ) {
k , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatalf ( "failed to generate key: %s" , err )
}
tmpl := & Certificate {
SerialNumber : big . NewInt ( 1 ) ,
Subject : pkix . Name { CommonName : "test" } ,
NotBefore : time . Now ( ) . Add ( - time . Hour ) ,
NotAfter : time . Now ( ) . Add ( time . Hour ) ,
DNSNames : [ ] string { "*" } ,
}
cDER , err := CreateCertificate ( rand . Reader , tmpl , tmpl , k . Public ( ) , k )
if err != nil {
t . Fatalf ( "failed to create certificate: %s" , err )
}
c , err := ParseCertificate ( cDER )
if err != nil {
t . Fatalf ( "failed to parse certificate: %s" , err )
}
if err := c . VerifyHostname ( "label" ) ; err == nil {
t . Fatalf ( "VerifyHostname unexpected success with bare wildcard SAN" )
}
}
2024-11-16 11:17:54 -08:00
func TestPoliciesValid ( t * testing . T ) {
// These test cases, the comments, and the certificates they rely on, are
// stolen from BoringSSL [0]. We skip the tests which involve certificate
// parsing as part of the verification process. Those tests are in
// TestParsePolicies.
//
// [0] https://boringssl.googlesource.com/boringssl/+/264f4f7a958af6c4ccb04662e302a99dfa7c5b85/crypto/x509/x509_test.cc#5913
testOID1 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 1 } )
testOID2 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 2 } )
testOID3 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 3 } )
testOID4 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 4 } )
testOID5 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 5 } )
loadTestCert := func ( t * testing . T , path string ) * Certificate {
b , err := os . ReadFile ( path )
if err != nil {
t . Fatal ( err )
}
p , _ := pem . Decode ( b )
c , err := ParseCertificate ( p . Bytes )
if err != nil {
t . Fatal ( err )
}
return c
}
root := loadTestCert ( t , "testdata/policy_root.pem" )
root_cross_inhibit_mapping := loadTestCert ( t , "testdata/policy_root_cross_inhibit_mapping.pem" )
root2 := loadTestCert ( t , "testdata/policy_root2.pem" )
intermediate := loadTestCert ( t , "testdata/policy_intermediate.pem" )
intermediate_any := loadTestCert ( t , "testdata/policy_intermediate_any.pem" )
intermediate_mapped := loadTestCert ( t , "testdata/policy_intermediate_mapped.pem" )
intermediate_mapped_any := loadTestCert ( t , "testdata/policy_intermediate_mapped_any.pem" )
intermediate_mapped_oid3 := loadTestCert ( t , "testdata/policy_intermediate_mapped_oid3.pem" )
intermediate_require := loadTestCert ( t , "testdata/policy_intermediate_require.pem" )
intermediate_require1 := loadTestCert ( t , "testdata/policy_intermediate_require1.pem" )
intermediate_require2 := loadTestCert ( t , "testdata/policy_intermediate_require2.pem" )
intermediate_require_no_policies := loadTestCert ( t , "testdata/policy_intermediate_require_no_policies.pem" )
leaf := loadTestCert ( t , "testdata/policy_leaf.pem" )
leaf_any := loadTestCert ( t , "testdata/policy_leaf_any.pem" )
leaf_none := loadTestCert ( t , "testdata/policy_leaf_none.pem" )
leaf_oid1 := loadTestCert ( t , "testdata/policy_leaf_oid1.pem" )
leaf_oid2 := loadTestCert ( t , "testdata/policy_leaf_oid2.pem" )
leaf_oid3 := loadTestCert ( t , "testdata/policy_leaf_oid3.pem" )
leaf_oid4 := loadTestCert ( t , "testdata/policy_leaf_oid4.pem" )
leaf_oid5 := loadTestCert ( t , "testdata/policy_leaf_oid5.pem" )
leaf_require := loadTestCert ( t , "testdata/policy_leaf_require.pem" )
leaf_require1 := loadTestCert ( t , "testdata/policy_leaf_require1.pem" )
type testCase struct {
chain [ ] * Certificate
policies [ ] OID
requireExplicitPolicy bool
inhibitPolicyMapping bool
inhibitAnyPolicy bool
valid bool
}
tests := [ ] testCase {
// The chain is good for |oid1| and |oid2|, but not |oid3|.
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID2 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID1 , testOID2 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID1 , testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
// Without |X509_V_FLAG_EXPLICIT_POLICY|, the policy tree is built and
// intersected with user-specified policies, but it is not required to result
// in any valid policies.
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID1 } ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
valid : true ,
} ,
// However, a CA with policy constraints can require an explicit policy.
{
chain : [ ] * Certificate { leaf , intermediate_require , root } ,
policies : [ ] OID { testOID1 } ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate_require , root } ,
policies : [ ] OID { testOID3 } ,
valid : false ,
} ,
// requireExplicitPolicy applies even if the application does not configure a
// user-initial-policy-set. If the validation results in no policies, the
// chain is invalid.
{
chain : [ ] * Certificate { leaf_none , intermediate_require , root } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
// A leaf can also set requireExplicitPolicy.
{
chain : [ ] * Certificate { leaf_require , intermediate , root } ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_require , intermediate , root } ,
policies : [ ] OID { testOID1 } ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_require , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
valid : false ,
} ,
// requireExplicitPolicy is a count of certificates to skip. If the value is
// not zero by the end of the chain, it doesn't count.
{
chain : [ ] * Certificate { leaf , intermediate_require1 , root } ,
policies : [ ] OID { testOID3 } ,
valid : false ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate_require2 , root } ,
policies : [ ] OID { testOID3 } ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_require1 , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
valid : true ,
} ,
// If multiple certificates specify the constraint, the more constrained value
// wins.
{
chain : [ ] * Certificate { leaf_require1 , intermediate_require1 , root } ,
policies : [ ] OID { testOID3 } ,
valid : false ,
} ,
{
chain : [ ] * Certificate { leaf_require , intermediate_require2 , root } ,
policies : [ ] OID { testOID3 } ,
valid : false ,
} ,
// An intermediate that requires an explicit policy, but then specifies no
// policies should fail verification as a result.
{
chain : [ ] * Certificate { leaf , intermediate_require_no_policies , root } ,
policies : [ ] OID { testOID1 } ,
valid : false ,
} ,
// A constrained intermediate's policy extension has a duplicate policy, which
// is invalid.
// {
// chain: []*Certificate{leaf, intermediate_require_duplicate, root},
// policies: []OID{testOID1},
// valid: false,
// },
// The leaf asserts anyPolicy, but the intermediate does not. The resulting
// valid policies are the intersection.
{
chain : [ ] * Certificate { leaf_any , intermediate , root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_any , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
// The intermediate asserts anyPolicy, but the leaf does not. The resulting
// valid policies are the intersection.
{
chain : [ ] * Certificate { leaf , intermediate_any , root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf , intermediate_any , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
// Both assert anyPolicy. All policies are valid.
{
chain : [ ] * Certificate { leaf_any , intermediate_any , root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_any , intermediate_any , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
// With just a trust anchor, policy checking silently succeeds.
{
chain : [ ] * Certificate { root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
// Although |intermediate_mapped_oid3| contains many mappings, it only accepts
// OID3. Nodes should not be created for the other mappings.
{
chain : [ ] * Certificate { leaf_oid1 , intermediate_mapped_oid3 , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid4 , intermediate_mapped_oid3 , root } ,
policies : [ ] OID { testOID4 } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
// Policy mapping can be inhibited, either by the caller or a certificate in
// the chain, in which case mapped policies are unassertable (apart from some
// anyPolicy edge cases).
{
chain : [ ] * Certificate { leaf_oid1 , intermediate_mapped_oid3 , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
inhibitPolicyMapping : true ,
valid : false ,
} ,
{
chain : [ ] * Certificate { leaf_oid1 , intermediate_mapped_oid3 , root_cross_inhibit_mapping , root2 } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : false ,
} ,
}
for _ , useAny := range [ ] bool { false , true } {
var intermediate * Certificate
if useAny {
intermediate = intermediate_mapped_any
} else {
intermediate = intermediate_mapped
}
extraTests := [ ] testCase {
// OID3 is mapped to {OID1, OID2}, which means OID1 and OID2 (or both) are
// acceptable for OID3.
{
chain : [ ] * Certificate { leaf , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid1 , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid2 , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
// If the intermediate's policies were anyPolicy, OID3 at the leaf, despite
// being mapped, is still acceptable as OID3 at the root. Despite the OID3
// having expected_policy_set = {OID1, OID2}, it can match the anyPolicy
// node instead.
//
// If the intermediate's policies listed OIDs explicitly, OID3 at the leaf
// is not acceptable as OID3 at the root. OID3 has expected_polciy_set =
// {OID1, OID2} and no other node allows OID3.
{
chain : [ ] * Certificate { leaf_oid3 , intermediate , root } ,
policies : [ ] OID { testOID3 } ,
requireExplicitPolicy : true ,
valid : useAny ,
} ,
// If the intermediate's policies were anyPolicy, OID1 at the leaf is no
// longer acceptable as OID1 at the root because policies only match
// anyPolicy when they match no other policy.
//
// If the intermediate's policies listed OIDs explicitly, OID1 at the leaf
// is acceptable as OID1 at the root because it will match both OID1 and
// OID3 (mapped) policies.
{
chain : [ ] * Certificate { leaf_oid1 , intermediate , root } ,
policies : [ ] OID { testOID1 } ,
requireExplicitPolicy : true ,
valid : ! useAny ,
} ,
// All pairs of OID4 and OID5 are mapped together, so either can stand for
// the other.
{
chain : [ ] * Certificate { leaf_oid4 , intermediate , root } ,
policies : [ ] OID { testOID4 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid4 , intermediate , root } ,
policies : [ ] OID { testOID5 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid5 , intermediate , root } ,
policies : [ ] OID { testOID4 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid5 , intermediate , root } ,
policies : [ ] OID { testOID5 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
{
chain : [ ] * Certificate { leaf_oid4 , intermediate , root } ,
policies : [ ] OID { testOID4 , testOID5 } ,
requireExplicitPolicy : true ,
valid : true ,
} ,
}
tests = append ( tests , extraTests ... )
}
for i , tc := range tests {
t . Run ( fmt . Sprint ( i ) , func ( t * testing . T ) {
valid := policiesValid ( tc . chain , VerifyOptions {
CertificatePolicies : tc . policies ,
requireExplicitPolicy : tc . requireExplicitPolicy ,
inhibitPolicyMapping : tc . inhibitPolicyMapping ,
inhibitAnyPolicy : tc . inhibitAnyPolicy ,
} )
if valid != tc . valid {
t . Errorf ( "policiesValid: got %t, want %t" , valid , tc . valid )
}
} )
}
}
2025-05-06 09:27:10 -07:00
func TestInvalidPolicyWithAnyKeyUsage ( t * testing . T ) {
loadTestCert := func ( t * testing . T , path string ) * Certificate {
b , err := os . ReadFile ( path )
if err != nil {
t . Fatal ( err )
}
p , _ := pem . Decode ( b )
c , err := ParseCertificate ( p . Bytes )
if err != nil {
t . Fatal ( err )
}
return c
}
testOID3 := mustNewOIDFromInts ( [ ] uint64 { 1 , 2 , 840 , 113554 , 4 , 1 , 72585 , 2 , 3 } )
root , intermediate , leaf := loadTestCert ( t , "testdata/policy_root.pem" ) , loadTestCert ( t , "testdata/policy_intermediate_require.pem" ) , loadTestCert ( t , "testdata/policy_leaf.pem" )
expectedErr := "x509: no valid chains built: all candidate chains have invalid policies"
roots , intermediates := NewCertPool ( ) , NewCertPool ( )
roots . AddCert ( root )
intermediates . AddCert ( intermediate )
_ , err := leaf . Verify ( VerifyOptions {
Roots : roots ,
Intermediates : intermediates ,
KeyUsages : [ ] ExtKeyUsage { ExtKeyUsageAny } ,
CertificatePolicies : [ ] OID { testOID3 } ,
} )
if err == nil {
t . Fatal ( "unexpected success, invalid policy shouldn't be bypassed by passing VerifyOptions.KeyUsages with ExtKeyUsageAny" )
} else if err . Error ( ) != expectedErr {
t . Fatalf ( "unexpected error, got %q, want %q" , err , expectedErr )
}
}
2025-09-11 16:27:04 -04:00
func TestCertificateChainSignedByECDSA ( t * testing . T ) {
caKey , err := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
if err != nil {
t . Fatal ( err )
}
root := & Certificate {
SerialNumber : big . NewInt ( 1 ) ,
Subject : pkix . Name { CommonName : "X" } ,
NotBefore : time . Now ( ) . Add ( - time . Hour ) ,
NotAfter : time . Now ( ) . Add ( 365 * 24 * time . Hour ) ,
IsCA : true ,
KeyUsage : KeyUsageCertSign | KeyUsageCRLSign ,
BasicConstraintsValid : true ,
}
caDER , err := CreateCertificate ( rand . Reader , root , root , & caKey . PublicKey , caKey )
if err != nil {
t . Fatal ( err )
}
root , err = ParseCertificate ( caDER )
if err != nil {
t . Fatal ( err )
}
leafKey , _ := ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
leaf := & Certificate {
SerialNumber : big . NewInt ( 42 ) ,
Subject : pkix . Name { CommonName : "leaf" } ,
NotBefore : time . Now ( ) . Add ( - 10 * time . Minute ) ,
NotAfter : time . Now ( ) . Add ( 24 * time . Hour ) ,
KeyUsage : KeyUsageDigitalSignature ,
ExtKeyUsage : [ ] ExtKeyUsage { ExtKeyUsageServerAuth } ,
BasicConstraintsValid : true ,
}
leafDER , err := CreateCertificate ( rand . Reader , leaf , root , & leafKey . PublicKey , caKey )
if err != nil {
t . Fatal ( err )
}
leaf , err = ParseCertificate ( leafDER )
if err != nil {
t . Fatal ( err )
}
inter , err := ParseCertificate ( dsaSelfSignedCNX ( t ) )
if err != nil {
t . Fatal ( err )
}
inters := NewCertPool ( )
inters . AddCert ( root )
inters . AddCert ( inter )
wantErr := "certificate signed by unknown authority"
_ , err = leaf . Verify ( VerifyOptions { Intermediates : inters , Roots : NewCertPool ( ) } )
if ! strings . Contains ( err . Error ( ) , wantErr ) {
t . Errorf ( "got %v, want %q" , err , wantErr )
}
}
// dsaSelfSignedCNX produces DER-encoded
// certificate with the properties:
//
// Subject=Issuer=CN=X
// DSA SPKI
// Matching inner/outer signature OIDs
// Dummy ECDSA signature
func dsaSelfSignedCNX ( t * testing . T ) [ ] byte {
t . Helper ( )
var params dsa . Parameters
if err := dsa . GenerateParameters ( & params , rand . Reader , dsa . L1024N160 ) ; err != nil {
t . Fatal ( err )
}
var dsaPriv dsa . PrivateKey
dsaPriv . Parameters = params
if err := dsa . GenerateKey ( & dsaPriv , rand . Reader ) ; err != nil {
t . Fatal ( err )
}
dsaPub := & dsaPriv . PublicKey
type dsaParams struct { P , Q , G * big . Int }
paramDER , err := asn1 . Marshal ( dsaParams { dsaPub . P , dsaPub . Q , dsaPub . G } )
if err != nil {
t . Fatal ( err )
}
yDER , err := asn1 . Marshal ( dsaPub . Y )
if err != nil {
t . Fatal ( err )
}
spki := publicKeyInfo {
Algorithm : pkix . AlgorithmIdentifier {
Algorithm : oidPublicKeyDSA ,
Parameters : asn1 . RawValue { FullBytes : paramDER } ,
} ,
PublicKey : asn1 . BitString { Bytes : yDER , BitLength : 8 * len ( yDER ) } ,
}
rdn := pkix . Name { CommonName : "X" } . ToRDNSequence ( )
b , err := asn1 . Marshal ( rdn )
if err != nil {
t . Fatal ( err )
}
rawName := asn1 . RawValue { FullBytes : b }
algoIdent := pkix . AlgorithmIdentifier { Algorithm : oidSignatureDSAWithSHA256 }
tbs := tbsCertificate {
Version : 0 ,
SerialNumber : big . NewInt ( 1002 ) ,
SignatureAlgorithm : algoIdent ,
Issuer : rawName ,
Validity : validity { NotBefore : time . Now ( ) . Add ( - time . Hour ) , NotAfter : time . Now ( ) . Add ( 24 * time . Hour ) } ,
Subject : rawName ,
PublicKey : spki ,
}
c := certificate {
TBSCertificate : tbs ,
SignatureAlgorithm : algoIdent ,
SignatureValue : asn1 . BitString { Bytes : [ ] byte { 0 } , BitLength : 8 } ,
}
dsaDER , err := asn1 . Marshal ( c )
if err != nil {
t . Fatal ( err )
}
return dsaDER
}