mirror of
https://github.com/golang/go
synced 2024-11-11 22:20:22 -07:00
crypto/x509: rewrite certificate parser
Replaces the encoding/asn1 certificate parser with a x/crypto/cryptobyte based parser. This provides a significant increase in performance, mostly due to a reduction of lots of small allocs, as well as almost entirely removing reflection. Since this is a rather large rewrite only the certificate parser is replaced, leaving the parsers for CSRs, CRLs, etc for follow-up work. Since some of the functions that the other parsers use are replaced with cryptobyte versions, they still get a not insignificant performance boost. name old time/op new time/op delta ParseCertificate/ecdsa_leaf-8 44.6µs ± 9% 12.7µs ± 4% -71.58% (p=0.000 n=20+18) ParseCertificate/rsa_leaf-8 46.4µs ± 4% 13.2µs ± 2% -71.49% (p=0.000 n=18+19) name old allocs/op new allocs/op delta ParseCertificate/ecdsa_leaf-8 501 ± 0% 164 ± 0% -67.27% (p=0.000 n=20+20) ParseCertificate/rsa_leaf-8 545 ± 0% 182 ± 0% -66.61% (p=0.000 n=20+20) Fixes #21118 Fixes #44237 Change-Id: Id653f6ae5e405c3cbf0c5c48abb30aa831e30107 Reviewed-on: https://go-review.googlesource.com/c/go/+/274234 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>
This commit is contained in:
parent
5f9fe47dea
commit
51ff3a6965
1005
src/crypto/x509/parser.go
Normal file
1005
src/crypto/x509/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@ package x509
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
@ -24,7 +23,6 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
@ -914,676 +912,6 @@ type distributionPointName struct {
|
||||
RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
|
||||
}
|
||||
|
||||
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
|
||||
asn1Data := keyData.PublicKey.RightAlign()
|
||||
switch algo {
|
||||
case RSA:
|
||||
// RSA public keys must have a NULL in the parameters.
|
||||
// See RFC 3279, Section 2.3.1.
|
||||
if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1.NullBytes) {
|
||||
return nil, errors.New("x509: RSA key missing NULL parameters")
|
||||
}
|
||||
|
||||
p := new(pkcs1PublicKey)
|
||||
rest, err := asn1.Unmarshal(asn1Data, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after RSA public key")
|
||||
}
|
||||
|
||||
if p.N.Sign() <= 0 {
|
||||
return nil, errors.New("x509: RSA modulus is not a positive number")
|
||||
}
|
||||
if p.E <= 0 {
|
||||
return nil, errors.New("x509: RSA public exponent is not a positive number")
|
||||
}
|
||||
|
||||
pub := &rsa.PublicKey{
|
||||
E: p.E,
|
||||
N: p.N,
|
||||
}
|
||||
return pub, nil
|
||||
case DSA:
|
||||
var p *big.Int
|
||||
rest, err := asn1.Unmarshal(asn1Data, &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after DSA public key")
|
||||
}
|
||||
paramsData := keyData.Algorithm.Parameters.FullBytes
|
||||
params := new(dsaAlgorithmParameters)
|
||||
rest, err = asn1.Unmarshal(paramsData, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after DSA parameters")
|
||||
}
|
||||
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
|
||||
return nil, errors.New("x509: zero or negative DSA parameter")
|
||||
}
|
||||
pub := &dsa.PublicKey{
|
||||
Parameters: dsa.Parameters{
|
||||
P: params.P,
|
||||
Q: params.Q,
|
||||
G: params.G,
|
||||
},
|
||||
Y: p,
|
||||
}
|
||||
return pub, nil
|
||||
case ECDSA:
|
||||
paramsData := keyData.Algorithm.Parameters.FullBytes
|
||||
namedCurveOID := new(asn1.ObjectIdentifier)
|
||||
rest, err := asn1.Unmarshal(paramsData, namedCurveOID)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to parse ECDSA parameters as named curve")
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after ECDSA parameters")
|
||||
}
|
||||
namedCurve := namedCurveFromOID(*namedCurveOID)
|
||||
if namedCurve == nil {
|
||||
return nil, errors.New("x509: unsupported elliptic curve")
|
||||
}
|
||||
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
|
||||
if x == nil {
|
||||
return nil, errors.New("x509: failed to unmarshal elliptic curve point")
|
||||
}
|
||||
pub := &ecdsa.PublicKey{
|
||||
Curve: namedCurve,
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
return pub, nil
|
||||
case Ed25519:
|
||||
// RFC 8410, Section 3
|
||||
// > For all of the OIDs, the parameters MUST be absent.
|
||||
if len(keyData.Algorithm.Parameters.FullBytes) != 0 {
|
||||
return nil, errors.New("x509: Ed25519 key encoded with illegal parameters")
|
||||
}
|
||||
if len(asn1Data) != ed25519.PublicKeySize {
|
||||
return nil, errors.New("x509: wrong Ed25519 public key size")
|
||||
}
|
||||
pub := make([]byte, ed25519.PublicKeySize)
|
||||
copy(pub, asn1Data)
|
||||
return ed25519.PublicKey(pub), nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func forEachSAN(extension []byte, callback func(tag int, data []byte) error) error {
|
||||
// RFC 5280, 4.2.1.6
|
||||
|
||||
// SubjectAltName ::= GeneralNames
|
||||
//
|
||||
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
|
||||
//
|
||||
// GeneralName ::= CHOICE {
|
||||
// otherName [0] OtherName,
|
||||
// rfc822Name [1] IA5String,
|
||||
// dNSName [2] IA5String,
|
||||
// x400Address [3] ORAddress,
|
||||
// directoryName [4] Name,
|
||||
// ediPartyName [5] EDIPartyName,
|
||||
// uniformResourceIdentifier [6] IA5String,
|
||||
// iPAddress [7] OCTET STRING,
|
||||
// registeredID [8] OBJECT IDENTIFIER }
|
||||
var seq asn1.RawValue
|
||||
rest, err := asn1.Unmarshal(extension, &seq)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(rest) != 0 {
|
||||
return errors.New("x509: trailing data after X.509 extension")
|
||||
}
|
||||
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
|
||||
return asn1.StructuralError{Msg: "bad SAN sequence"}
|
||||
}
|
||||
|
||||
rest = seq.Bytes
|
||||
for len(rest) > 0 {
|
||||
var v asn1.RawValue
|
||||
rest, err = asn1.Unmarshal(rest, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := callback(v.Tag, v.Bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL, err error) {
|
||||
err = forEachSAN(value, func(tag int, data []byte) error {
|
||||
switch tag {
|
||||
case nameTypeEmail:
|
||||
email := string(data)
|
||||
if err := isIA5String(email); err != nil {
|
||||
return errors.New("x509: SAN rfc822Name is malformed")
|
||||
}
|
||||
emailAddresses = append(emailAddresses, email)
|
||||
case nameTypeDNS:
|
||||
name := string(data)
|
||||
if err := isIA5String(name); err != nil {
|
||||
return errors.New("x509: SAN dNSName is malformed")
|
||||
}
|
||||
dnsNames = append(dnsNames, string(name))
|
||||
case nameTypeURI:
|
||||
uriStr := string(data)
|
||||
if err := isIA5String(uriStr); err != nil {
|
||||
return errors.New("x509: SAN uniformResourceIdentifier is malformed")
|
||||
}
|
||||
uri, err := url.Parse(uriStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
|
||||
}
|
||||
if len(uri.Host) > 0 {
|
||||
if _, ok := domainToReverseLabels(uri.Host); !ok {
|
||||
return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
|
||||
}
|
||||
}
|
||||
uris = append(uris, uri)
|
||||
case nameTypeIP:
|
||||
switch len(data) {
|
||||
case net.IPv4len, net.IPv6len:
|
||||
ipAddresses = append(ipAddresses, data)
|
||||
default:
|
||||
return errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(data)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// isValidIPMask reports whether mask consists of zero or more 1 bits, followed by zero bits.
|
||||
func isValidIPMask(mask []byte) bool {
|
||||
seenZero := false
|
||||
|
||||
for _, b := range mask {
|
||||
if seenZero {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
switch b {
|
||||
case 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe:
|
||||
seenZero = true
|
||||
case 0xff:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandled bool, err error) {
|
||||
// RFC 5280, 4.2.1.10
|
||||
|
||||
// NameConstraints ::= SEQUENCE {
|
||||
// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
|
||||
// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
|
||||
//
|
||||
// GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
|
||||
//
|
||||
// GeneralSubtree ::= SEQUENCE {
|
||||
// base GeneralName,
|
||||
// minimum [0] BaseDistance DEFAULT 0,
|
||||
// maximum [1] BaseDistance OPTIONAL }
|
||||
//
|
||||
// BaseDistance ::= INTEGER (0..MAX)
|
||||
|
||||
outer := cryptobyte.String(e.Value)
|
||||
var toplevel, permitted, excluded cryptobyte.String
|
||||
var havePermitted, haveExcluded bool
|
||||
if !outer.ReadASN1(&toplevel, cryptobyte_asn1.SEQUENCE) ||
|
||||
!outer.Empty() ||
|
||||
!toplevel.ReadOptionalASN1(&permitted, &havePermitted, cryptobyte_asn1.Tag(0).ContextSpecific().Constructed()) ||
|
||||
!toplevel.ReadOptionalASN1(&excluded, &haveExcluded, cryptobyte_asn1.Tag(1).ContextSpecific().Constructed()) ||
|
||||
!toplevel.Empty() {
|
||||
return false, errors.New("x509: invalid NameConstraints extension")
|
||||
}
|
||||
|
||||
if !havePermitted && !haveExcluded || len(permitted) == 0 && len(excluded) == 0 {
|
||||
// From RFC 5280, Section 4.2.1.10:
|
||||
// “either the permittedSubtrees field
|
||||
// or the excludedSubtrees MUST be
|
||||
// present”
|
||||
return false, errors.New("x509: empty name constraints extension")
|
||||
}
|
||||
|
||||
getValues := func(subtrees cryptobyte.String) (dnsNames []string, ips []*net.IPNet, emails, uriDomains []string, err error) {
|
||||
for !subtrees.Empty() {
|
||||
var seq, value cryptobyte.String
|
||||
var tag cryptobyte_asn1.Tag
|
||||
if !subtrees.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) ||
|
||||
!seq.ReadAnyASN1(&value, &tag) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: invalid NameConstraints extension")
|
||||
}
|
||||
|
||||
var (
|
||||
dnsTag = cryptobyte_asn1.Tag(2).ContextSpecific()
|
||||
emailTag = cryptobyte_asn1.Tag(1).ContextSpecific()
|
||||
ipTag = cryptobyte_asn1.Tag(7).ContextSpecific()
|
||||
uriTag = cryptobyte_asn1.Tag(6).ContextSpecific()
|
||||
)
|
||||
|
||||
switch tag {
|
||||
case dnsTag:
|
||||
domain := string(value)
|
||||
if err := isIA5String(domain); err != nil {
|
||||
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
|
||||
}
|
||||
|
||||
trimmedDomain := domain
|
||||
if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
|
||||
// constraints can have a leading
|
||||
// period to exclude the domain
|
||||
// itself, but that's not valid in a
|
||||
// normal domain name.
|
||||
trimmedDomain = trimmedDomain[1:]
|
||||
}
|
||||
if _, ok := domainToReverseLabels(trimmedDomain); !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)
|
||||
}
|
||||
dnsNames = append(dnsNames, domain)
|
||||
|
||||
case ipTag:
|
||||
l := len(value)
|
||||
var ip, mask []byte
|
||||
|
||||
switch l {
|
||||
case 8:
|
||||
ip = value[:4]
|
||||
mask = value[4:]
|
||||
|
||||
case 32:
|
||||
ip = value[:16]
|
||||
mask = value[16:]
|
||||
|
||||
default:
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained value of length %d", l)
|
||||
}
|
||||
|
||||
if !isValidIPMask(mask) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained invalid mask %x", mask)
|
||||
}
|
||||
|
||||
ips = append(ips, &net.IPNet{IP: net.IP(ip), Mask: net.IPMask(mask)})
|
||||
|
||||
case emailTag:
|
||||
constraint := string(value)
|
||||
if err := isIA5String(constraint); err != nil {
|
||||
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
|
||||
}
|
||||
|
||||
// If the constraint contains an @ then
|
||||
// it specifies an exact mailbox name.
|
||||
if strings.Contains(constraint, "@") {
|
||||
if _, ok := parseRFC2821Mailbox(constraint); !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
|
||||
}
|
||||
} else {
|
||||
// Otherwise it's a domain name.
|
||||
domain := constraint
|
||||
if len(domain) > 0 && domain[0] == '.' {
|
||||
domain = domain[1:]
|
||||
}
|
||||
if _, ok := domainToReverseLabels(domain); !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
|
||||
}
|
||||
}
|
||||
emails = append(emails, constraint)
|
||||
|
||||
case uriTag:
|
||||
domain := string(value)
|
||||
if err := isIA5String(domain); err != nil {
|
||||
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
|
||||
}
|
||||
|
||||
if net.ParseIP(domain) != nil {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain)
|
||||
}
|
||||
|
||||
trimmedDomain := domain
|
||||
if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
|
||||
// constraints can have a leading
|
||||
// period to exclude the domain itself,
|
||||
// but that's not valid in a normal
|
||||
// domain name.
|
||||
trimmedDomain = trimmedDomain[1:]
|
||||
}
|
||||
if _, ok := domainToReverseLabels(trimmedDomain); !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain)
|
||||
}
|
||||
uriDomains = append(uriDomains, domain)
|
||||
|
||||
default:
|
||||
unhandled = true
|
||||
}
|
||||
}
|
||||
|
||||
return dnsNames, ips, emails, uriDomains, nil
|
||||
}
|
||||
|
||||
if out.PermittedDNSDomains, out.PermittedIPRanges, out.PermittedEmailAddresses, out.PermittedURIDomains, err = getValues(permitted); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if out.ExcludedDNSDomains, out.ExcludedIPRanges, out.ExcludedEmailAddresses, out.ExcludedURIDomains, err = getValues(excluded); err != nil {
|
||||
return false, err
|
||||
}
|
||||
out.PermittedDNSDomainsCritical = e.Critical
|
||||
|
||||
return unhandled, nil
|
||||
}
|
||||
|
||||
func parseCertificate(in *certificate) (*Certificate, error) {
|
||||
out := new(Certificate)
|
||||
out.Raw = in.Raw
|
||||
out.RawTBSCertificate = in.TBSCertificate.Raw
|
||||
out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw
|
||||
out.RawSubject = in.TBSCertificate.Subject.FullBytes
|
||||
out.RawIssuer = in.TBSCertificate.Issuer.FullBytes
|
||||
|
||||
out.Signature = in.SignatureValue.RightAlign()
|
||||
out.SignatureAlgorithm =
|
||||
getSignatureAlgorithmFromAI(in.TBSCertificate.SignatureAlgorithm)
|
||||
|
||||
out.PublicKeyAlgorithm =
|
||||
getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm)
|
||||
var err error
|
||||
out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Version = in.TBSCertificate.Version + 1
|
||||
out.SerialNumber = in.TBSCertificate.SerialNumber
|
||||
|
||||
var issuer, subject pkix.RDNSequence
|
||||
if rest, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 subject")
|
||||
}
|
||||
if rest, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 issuer")
|
||||
}
|
||||
|
||||
out.Issuer.FillFromRDNSequence(&issuer)
|
||||
out.Subject.FillFromRDNSequence(&subject)
|
||||
|
||||
out.NotBefore = in.TBSCertificate.Validity.NotBefore
|
||||
out.NotAfter = in.TBSCertificate.Validity.NotAfter
|
||||
|
||||
for _, e := range in.TBSCertificate.Extensions {
|
||||
out.Extensions = append(out.Extensions, e)
|
||||
unhandled := false
|
||||
|
||||
if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
|
||||
switch e.Id[3] {
|
||||
case 15:
|
||||
out.KeyUsage, err = parseKeyUsageExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 19:
|
||||
out.IsCA, out.MaxPathLen, err = parseBasicConstraintsExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.BasicConstraintsValid = true
|
||||
out.MaxPathLenZero = out.MaxPathLen == 0
|
||||
case 17:
|
||||
out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 && len(out.URIs) == 0 {
|
||||
// If we didn't parse anything then we do the critical check, below.
|
||||
unhandled = true
|
||||
}
|
||||
|
||||
case 30:
|
||||
unhandled, err = parseNameConstraintsExtension(out, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case 31:
|
||||
// RFC 5280, 4.2.1.13
|
||||
|
||||
// CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
|
||||
//
|
||||
// DistributionPoint ::= SEQUENCE {
|
||||
// distributionPoint [0] DistributionPointName OPTIONAL,
|
||||
// reasons [1] ReasonFlags OPTIONAL,
|
||||
// cRLIssuer [2] GeneralNames OPTIONAL }
|
||||
//
|
||||
// DistributionPointName ::= CHOICE {
|
||||
// fullName [0] GeneralNames,
|
||||
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
|
||||
|
||||
var cdp []distributionPoint
|
||||
if rest, err := asn1.Unmarshal(e.Value, &cdp); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 CRL distribution point")
|
||||
}
|
||||
|
||||
for _, dp := range cdp {
|
||||
// Per RFC 5280, 4.2.1.13, one of distributionPoint or cRLIssuer may be empty.
|
||||
if len(dp.DistributionPoint.FullName) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, fullName := range dp.DistributionPoint.FullName {
|
||||
if fullName.Tag == 6 {
|
||||
out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(fullName.Bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 35:
|
||||
// RFC 5280, 4.2.1.1
|
||||
var a authKeyId
|
||||
if rest, err := asn1.Unmarshal(e.Value, &a); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 authority key-id")
|
||||
}
|
||||
out.AuthorityKeyId = a.Id
|
||||
|
||||
case 37:
|
||||
out.ExtKeyUsage, out.UnknownExtKeyUsage, err = parseExtKeyUsageExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 14:
|
||||
out.SubjectKeyId, err = parseSubjectKeyIdExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 32:
|
||||
out.PolicyIdentifiers, err = parseCertificatePoliciesExtension(e.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
// Unknown extensions are recorded if critical.
|
||||
unhandled = true
|
||||
}
|
||||
} else if e.Id.Equal(oidExtensionAuthorityInfoAccess) {
|
||||
// RFC 5280 4.2.2.1: Authority Information Access
|
||||
var aia []authorityInfoAccess
|
||||
if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 authority information")
|
||||
}
|
||||
|
||||
for _, v := range aia {
|
||||
// GeneralName: uniformResourceIdentifier [6] IA5String
|
||||
if v.Location.Tag != 6 {
|
||||
continue
|
||||
}
|
||||
if v.Method.Equal(oidAuthorityInfoAccessOcsp) {
|
||||
out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes))
|
||||
} else if v.Method.Equal(oidAuthorityInfoAccessIssuers) {
|
||||
out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unknown extensions are recorded if critical.
|
||||
unhandled = true
|
||||
}
|
||||
|
||||
if e.Critical && unhandled {
|
||||
out.UnhandledCriticalExtensions = append(out.UnhandledCriticalExtensions, e.Id)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// parseKeyUsageExtension parses id-ce-keyUsage (2.5.29.15) from RFC 5280
|
||||
// Section 4.2.1.3
|
||||
func parseKeyUsageExtension(ext []byte) (KeyUsage, error) {
|
||||
var usageBits asn1.BitString
|
||||
if rest, err := asn1.Unmarshal(ext, &usageBits); err != nil {
|
||||
return 0, err
|
||||
} else if len(rest) != 0 {
|
||||
return 0, errors.New("x509: trailing data after X.509 KeyUsage")
|
||||
}
|
||||
|
||||
var usage int
|
||||
for i := 0; i < 9; i++ {
|
||||
if usageBits.At(i) != 0 {
|
||||
usage |= 1 << uint(i)
|
||||
}
|
||||
}
|
||||
return KeyUsage(usage), nil
|
||||
}
|
||||
|
||||
// parseBasicConstraintsExtension parses id-ce-basicConstraints (2.5.29.19)
|
||||
// from RFC 5280 Section 4.2.1.9
|
||||
func parseBasicConstraintsExtension(ext []byte) (isCA bool, maxPathLen int, err error) {
|
||||
var constraints basicConstraints
|
||||
if rest, err := asn1.Unmarshal(ext, &constraints); err != nil {
|
||||
return false, 0, err
|
||||
} else if len(rest) != 0 {
|
||||
return false, 0, errors.New("x509: trailing data after X.509 BasicConstraints")
|
||||
}
|
||||
|
||||
// TODO: map out.MaxPathLen to 0 if it has the -1 default value? (Issue 19285)
|
||||
return constraints.IsCA, constraints.MaxPathLen, nil
|
||||
}
|
||||
|
||||
// parseExtKeyUsageExtension parses id-ce-extKeyUsage (2.5.29.37) from
|
||||
// RFC 5280 Section 4.2.1.12
|
||||
func parseExtKeyUsageExtension(ext []byte) ([]ExtKeyUsage, []asn1.ObjectIdentifier, error) {
|
||||
var keyUsage []asn1.ObjectIdentifier
|
||||
if rest, err := asn1.Unmarshal(ext, &keyUsage); err != nil {
|
||||
return nil, nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, nil, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
|
||||
}
|
||||
|
||||
var extKeyUsages []ExtKeyUsage
|
||||
var unknownUsages []asn1.ObjectIdentifier
|
||||
for _, u := range keyUsage {
|
||||
if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
|
||||
extKeyUsages = append(extKeyUsages, extKeyUsage)
|
||||
} else {
|
||||
unknownUsages = append(unknownUsages, u)
|
||||
}
|
||||
}
|
||||
return extKeyUsages, unknownUsages, nil
|
||||
}
|
||||
|
||||
// parseSubjectKeyIdExtension parses id-ce-subjectKeyIdentifier (2.5.29.14)
|
||||
// from RFC 5280 Section 4.2.1.2
|
||||
func parseSubjectKeyIdExtension(ext []byte) ([]byte, error) {
|
||||
var keyid []byte
|
||||
if rest, err := asn1.Unmarshal(ext, &keyid); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 key-id")
|
||||
}
|
||||
return keyid, nil
|
||||
}
|
||||
|
||||
func parseCertificatePoliciesExtension(ext []byte) ([]asn1.ObjectIdentifier, error) {
|
||||
var policies []policyInformation
|
||||
if rest, err := asn1.Unmarshal(ext, &policies); err != nil {
|
||||
return nil, err
|
||||
} else if len(rest) != 0 {
|
||||
return nil, errors.New("x509: trailing data after X.509 certificate policies")
|
||||
}
|
||||
oids := make([]asn1.ObjectIdentifier, len(policies))
|
||||
for i, policy := range policies {
|
||||
oids[i] = policy.Policy
|
||||
}
|
||||
return oids, nil
|
||||
}
|
||||
|
||||
// ParseCertificate parses a single certificate from the given ASN.1 DER data.
|
||||
func ParseCertificate(asn1Data []byte) (*Certificate, error) {
|
||||
var cert certificate
|
||||
rest, err := asn1.Unmarshal(asn1Data, &cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||
}
|
||||
|
||||
return parseCertificate(&cert)
|
||||
}
|
||||
|
||||
// ParseCertificates parses one or more certificates from the given ASN.1 DER
|
||||
// data. The certificates must be concatenated with no intermediate padding.
|
||||
func ParseCertificates(asn1Data []byte) ([]*Certificate, error) {
|
||||
var v []*certificate
|
||||
|
||||
for len(asn1Data) > 0 {
|
||||
cert := new(certificate)
|
||||
var err error
|
||||
asn1Data, err = asn1.Unmarshal(asn1Data, cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = append(v, cert)
|
||||
}
|
||||
|
||||
ret := make([]*Certificate, len(v))
|
||||
for i, ci := range v {
|
||||
cert, err := parseCertificate(ci)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = cert
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func reverseBitsInAByte(in byte) byte {
|
||||
b1 := in>>4 | in<<4
|
||||
b2 := b1>>2&0x33 | b1<<2&0xcc
|
||||
|
@ -2995,3 +2995,182 @@ func TestCertificateRequestRoundtripFields(t *testing.T) {
|
||||
t.Fatalf("Unexpected URIs: got %v, want %v", out.URIs, in.URIs)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseCertificate(b *testing.B) {
|
||||
cases := []struct {
|
||||
name string
|
||||
pem string
|
||||
}{
|
||||
{
|
||||
name: "ecdsa leaf",
|
||||
pem: `-----BEGIN CERTIFICATE-----
|
||||
MIIINjCCBx6gAwIBAgIQHdQ6oBMoe/MJAAAAAEHzmTANBgkqhkiG9w0BAQsFADBG
|
||||
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
|
||||
QzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMDEyMDgwOTExMzZaFw0yMTAzMDIw
|
||||
OTExMzVaMBcxFTATBgNVBAMMDCouZ29vZ2xlLmNvbTBZMBMGByqGSM49AgEGCCqG
|
||||
SM49AwEHA0IABEFYegyHh1AHRS1nar5+zYJgMACcsIQMtg0YMyK/59ml8ERIt/JF
|
||||
kXM3XIvQuCJhghUawZrrAcAs8djZF1U9M4mjggYYMIIGFDAOBgNVHQ8BAf8EBAMC
|
||||
B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
|
||||
6SWWF36XBsmXJ6iV0EHPXUFoMbwwHwYDVR0jBBgwFoAUinR/r4XN7pXNPZzQ4kYU
|
||||
83E1HScwagYIKwYBBQUHAQEEXjBcMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5w
|
||||
a2kuZ29vZy9ndHMxYzMwMQYIKwYBBQUHMAKGJWh0dHA6Ly9wa2kuZ29vZy9yZXBv
|
||||
L2NlcnRzL2d0czFjMy5kZXIwggTCBgNVHREEggS5MIIEtYIMKi5nb29nbGUuY29t
|
||||
gg0qLmFuZHJvaWQuY29tghYqLmFwcGVuZ2luZS5nb29nbGUuY29tggkqLmJkbi5k
|
||||
ZXaCEiouY2xvdWQuZ29vZ2xlLmNvbYIYKi5jcm93ZHNvdXJjZS5nb29nbGUuY29t
|
||||
ghgqLmRhdGFjb21wdXRlLmdvb2dsZS5jb22CBiouZy5jb4IOKi5nY3AuZ3Z0Mi5j
|
||||
b22CESouZ2NwY2RuLmd2dDEuY29tggoqLmdncGh0LmNugg4qLmdrZWNuYXBwcy5j
|
||||
boIWKi5nb29nbGUtYW5hbHl0aWNzLmNvbYILKi5nb29nbGUuY2GCCyouZ29vZ2xl
|
||||
LmNsgg4qLmdvb2dsZS5jby5pboIOKi5nb29nbGUuY28uanCCDiouZ29vZ2xlLmNv
|
||||
LnVrgg8qLmdvb2dsZS5jb20uYXKCDyouZ29vZ2xlLmNvbS5hdYIPKi5nb29nbGUu
|
||||
Y29tLmJygg8qLmdvb2dsZS5jb20uY2+CDyouZ29vZ2xlLmNvbS5teIIPKi5nb29n
|
||||
bGUuY29tLnRygg8qLmdvb2dsZS5jb20udm6CCyouZ29vZ2xlLmRlggsqLmdvb2ds
|
||||
ZS5lc4ILKi5nb29nbGUuZnKCCyouZ29vZ2xlLmh1ggsqLmdvb2dsZS5pdIILKi5n
|
||||
b29nbGUubmyCCyouZ29vZ2xlLnBsggsqLmdvb2dsZS5wdIISKi5nb29nbGVhZGFw
|
||||
aXMuY29tgg8qLmdvb2dsZWFwaXMuY26CESouZ29vZ2xlY25hcHBzLmNughQqLmdv
|
||||
b2dsZWNvbW1lcmNlLmNvbYIRKi5nb29nbGV2aWRlby5jb22CDCouZ3N0YXRpYy5j
|
||||
boINKi5nc3RhdGljLmNvbYISKi5nc3RhdGljY25hcHBzLmNuggoqLmd2dDEuY29t
|
||||
ggoqLmd2dDIuY29tghQqLm1ldHJpYy5nc3RhdGljLmNvbYIMKi51cmNoaW4uY29t
|
||||
ghAqLnVybC5nb29nbGUuY29tghMqLndlYXIuZ2tlY25hcHBzLmNughYqLnlvdXR1
|
||||
YmUtbm9jb29raWUuY29tgg0qLnlvdXR1YmUuY29tghYqLnlvdXR1YmVlZHVjYXRp
|
||||
b24uY29tghEqLnlvdXR1YmVraWRzLmNvbYIHKi55dC5iZYILKi55dGltZy5jb22C
|
||||
GmFuZHJvaWQuY2xpZW50cy5nb29nbGUuY29tggthbmRyb2lkLmNvbYIbZGV2ZWxv
|
||||
cGVyLmFuZHJvaWQuZ29vZ2xlLmNughxkZXZlbG9wZXJzLmFuZHJvaWQuZ29vZ2xl
|
||||
LmNuggRnLmNvgghnZ3BodC5jboIMZ2tlY25hcHBzLmNuggZnb28uZ2yCFGdvb2ds
|
||||
ZS1hbmFseXRpY3MuY29tggpnb29nbGUuY29tgg9nb29nbGVjbmFwcHMuY26CEmdv
|
||||
b2dsZWNvbW1lcmNlLmNvbYIYc291cmNlLmFuZHJvaWQuZ29vZ2xlLmNuggp1cmNo
|
||||
aW4uY29tggp3d3cuZ29vLmdsggh5b3V0dS5iZYILeW91dHViZS5jb22CFHlvdXR1
|
||||
YmVlZHVjYXRpb24uY29tgg95b3V0dWJla2lkcy5jb22CBXl0LmJlMCEGA1UdIAQa
|
||||
MBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwNQYDVR0fBC4wLDAqoCigJoYkaHR0
|
||||
cDovL2NybC5wa2kuZ29vZy9ndHNyMS9ndHMxYzMuY3JsMBMGCisGAQQB1nkCBAMB
|
||||
Af8EAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQAlDQm5zY7JcPxcJ9ulfTGsWV/m6Pro
|
||||
gLYmAlBUPGKy313aetT4Zjz44ZseVtUOKsXVHh4avPA9O+ta1FgkASlbkgJ05ivb
|
||||
j/+MMqkrLemdMv9Svvx3CNaAq2jJ2E+8GdrA1RzMkiNthJCiRafaPnXnN6hOHGNr
|
||||
GtqYfMHsvrRHW8J2IPHW0/MUHmJ/NDu/vNchxke2OEfCPLtseo3hJt8l8HbH+yE8
|
||||
DFrt8YVRi1CLomEyuPJDF4og3O3ZsoXuxcPd9UPxULOCxycdolRw8Iv/Xgr082j3
|
||||
svXC3HUd3apM2Yy3xJAlk/mUkzVXfdJZ+Zy1huNsUoJ+gM8rmpyGhYyx
|
||||
-----END CERTIFICATE-----`,
|
||||
},
|
||||
{
|
||||
name: "rsa leaf",
|
||||
pem: `-----BEGIN CERTIFICATE-----
|
||||
MIIJXjCCCEagAwIBAgIRAPYaTUsjP4iRBQAAAACHSSgwDQYJKoZIhvcNAQELBQAw
|
||||
QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET
|
||||
MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0yMTAxMjYwODQ2MzRaFw0yMTA0MjAwODQ2
|
||||
MzNaMGYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
|
||||
Ew1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMRUwEwYDVQQDDAwq
|
||||
Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC76xx0
|
||||
UdZ36/41rZNPfQ/yQ05vsBLUO0d+3uMOhvDlpst+XvIsG6L+vLDgf3RiQRFlei0h
|
||||
KqqLOtWLDc/y0+OmaaC+8ft1zljBYdvQlAYoZrT79Cc5pAIDq7G1OZ7cC4ahDno/
|
||||
n46FHjT/UTUAMYa8cKWBaMPneMIsKvn8nMdZzHkfO2nUd6OEecn90XweMvNmx8De
|
||||
6h5AlIgG3m66hkD/UCSdxn7yJHBQVdHgkfTqzv3sz2YyBQGNi288F1bn541f6khE
|
||||
fYti1MvXRtkky7yLCQNUG6PtvuSU4cKaNvRklHigf5i1nVdGEuH61gAElZIklSia
|
||||
OVK46UyU4DGtbdWNAgMBAAGjggYpMIIGJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0l
|
||||
BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU8zCvllLd3jhB
|
||||
k//+Wdjo40Q+T3gwHwYDVR0jBBgwFoAUmNH4bhDrz5vsYJ8YkBug630J/SswaAYI
|
||||
KwYBBQUHAQEEXDBaMCsGCCsGAQUFBzABhh9odHRwOi8vb2NzcC5wa2kuZ29vZy9n
|
||||
dHMxbzFjb3JlMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2cvZ3NyMi9HVFMx
|
||||
TzEuY3J0MIIE1wYDVR0RBIIEzjCCBMqCDCouZ29vZ2xlLmNvbYINKi5hbmRyb2lk
|
||||
LmNvbYIWKi5hcHBlbmdpbmUuZ29vZ2xlLmNvbYIJKi5iZG4uZGV2ghIqLmNsb3Vk
|
||||
Lmdvb2dsZS5jb22CGCouY3Jvd2Rzb3VyY2UuZ29vZ2xlLmNvbYIYKi5kYXRhY29t
|
||||
cHV0ZS5nb29nbGUuY29tghMqLmZsYXNoLmFuZHJvaWQuY29tggYqLmcuY2+CDiou
|
||||
Z2NwLmd2dDIuY29tghEqLmdjcGNkbi5ndnQxLmNvbYIKKi5nZ3BodC5jboIOKi5n
|
||||
a2VjbmFwcHMuY26CFiouZ29vZ2xlLWFuYWx5dGljcy5jb22CCyouZ29vZ2xlLmNh
|
||||
ggsqLmdvb2dsZS5jbIIOKi5nb29nbGUuY28uaW6CDiouZ29vZ2xlLmNvLmpwgg4q
|
||||
Lmdvb2dsZS5jby51a4IPKi5nb29nbGUuY29tLmFygg8qLmdvb2dsZS5jb20uYXWC
|
||||
DyouZ29vZ2xlLmNvbS5icoIPKi5nb29nbGUuY29tLmNvgg8qLmdvb2dsZS5jb20u
|
||||
bXiCDyouZ29vZ2xlLmNvbS50coIPKi5nb29nbGUuY29tLnZuggsqLmdvb2dsZS5k
|
||||
ZYILKi5nb29nbGUuZXOCCyouZ29vZ2xlLmZyggsqLmdvb2dsZS5odYILKi5nb29n
|
||||
bGUuaXSCCyouZ29vZ2xlLm5sggsqLmdvb2dsZS5wbIILKi5nb29nbGUucHSCEiou
|
||||
Z29vZ2xlYWRhcGlzLmNvbYIPKi5nb29nbGVhcGlzLmNughEqLmdvb2dsZWNuYXBw
|
||||
cy5jboIUKi5nb29nbGVjb21tZXJjZS5jb22CESouZ29vZ2xldmlkZW8uY29tggwq
|
||||
LmdzdGF0aWMuY26CDSouZ3N0YXRpYy5jb22CEiouZ3N0YXRpY2NuYXBwcy5jboIK
|
||||
Ki5ndnQxLmNvbYIKKi5ndnQyLmNvbYIUKi5tZXRyaWMuZ3N0YXRpYy5jb22CDCou
|
||||
dXJjaGluLmNvbYIQKi51cmwuZ29vZ2xlLmNvbYITKi53ZWFyLmdrZWNuYXBwcy5j
|
||||
boIWKi55b3V0dWJlLW5vY29va2llLmNvbYINKi55b3V0dWJlLmNvbYIWKi55b3V0
|
||||
dWJlZWR1Y2F0aW9uLmNvbYIRKi55b3V0dWJla2lkcy5jb22CByoueXQuYmWCCyou
|
||||
eXRpbWcuY29tghphbmRyb2lkLmNsaWVudHMuZ29vZ2xlLmNvbYILYW5kcm9pZC5j
|
||||
b22CG2RldmVsb3Blci5hbmRyb2lkLmdvb2dsZS5jboIcZGV2ZWxvcGVycy5hbmRy
|
||||
b2lkLmdvb2dsZS5jboIEZy5jb4IIZ2dwaHQuY26CDGdrZWNuYXBwcy5jboIGZ29v
|
||||
LmdsghRnb29nbGUtYW5hbHl0aWNzLmNvbYIKZ29vZ2xlLmNvbYIPZ29vZ2xlY25h
|
||||
cHBzLmNughJnb29nbGVjb21tZXJjZS5jb22CGHNvdXJjZS5hbmRyb2lkLmdvb2ds
|
||||
ZS5jboIKdXJjaGluLmNvbYIKd3d3Lmdvby5nbIIIeW91dHUuYmWCC3lvdXR1YmUu
|
||||
Y29tghR5b3V0dWJlZWR1Y2F0aW9uLmNvbYIPeW91dHViZWtpZHMuY29tggV5dC5i
|
||||
ZTAhBgNVHSAEGjAYMAgGBmeBDAECAjAMBgorBgEEAdZ5AgUDMDMGA1UdHwQsMCow
|
||||
KKAmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29yZS5jcmwwEwYKKwYB
|
||||
BAHWeQIEAwEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBAHh9/ozYUGRd+W5akWlM
|
||||
4WvX808TK2oUISnagbxCCFZ2trpg2oi03CJf4o4o3Je5Qzzz10s22oQY6gPHAR0B
|
||||
QHzrpqAveQw9D5vd8xjgtQ/SAujPzPKNQee5511rS7/EKW9I83ccd5XhhoEyx8A1
|
||||
/65RTS+2hKpJKTMkr0yHBPJV7kUW+n/KIef5YaSOA9VYK7hyH0niDpvm9EmoqvWS
|
||||
U5xAFAe/Xrrq3sxTuDJPQA8alk6h/ql5Klkw6dL53csiPka/MevDqdifWkzuT/6n
|
||||
YK/ePeJzPD17FA9V+N1rcuF3Wk29AZvCOSasdIkIuE82vGr3dfNrsrn9E9lWIbCr
|
||||
Qc4=
|
||||
-----END CERTIFICATE-----`,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
b.Run(c.name, func(b *testing.B) {
|
||||
pemBlock, _ := pem.Decode([]byte(c.pem))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCertificateRawEquals(t *testing.T) {
|
||||
p, _ := pem.Decode([]byte(pemCertificate))
|
||||
cert, err := ParseCertificate(p.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse certificate: %s", err)
|
||||
}
|
||||
if !bytes.Equal(p.Bytes, cert.Raw) {
|
||||
t.Fatalf("unexpected Certificate.Raw\ngot: %x\nwant: %x\n", cert.Raw, p.Bytes)
|
||||
}
|
||||
fmt.Printf("in: %x\nout: %x\n", p.Bytes, cert.Raw)
|
||||
}
|
||||
|
||||
// mismatchingSigAlgIDPEM contains a certificate where the Certificate
|
||||
// signatureAlgorithm and the TBSCertificate signature contain
|
||||
// mismatching OIDs
|
||||
const mismatchingSigAlgIDPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBBzCBrqADAgECAgEAMAoGCCqGSM49BAMCMAAwIhgPMDAwMTAxMDEwMDAwMDBa
|
||||
GA8wMDAxMDEwMTAwMDAwMFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOqV
|
||||
EDuVXxwZgIU3+dOwv1SsMu0xuV48hf7xmK8n7sAMYgllB+96DnPqBeboJj4snYnx
|
||||
0AcE0PDVQ1l4Z3YXsQWjFTATMBEGA1UdEQEB/wQHMAWCA2FzZDAKBggqhkjOPQQD
|
||||
AwNIADBFAiBi1jz/T2HT5nAfrD7zsgR+68qh7Erc6Q4qlxYBOgKG4QIhAOtjIn+Q
|
||||
tA+bq+55P3ntxTOVRq0nv1mwnkjwt9cQR9Fn
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// mismatchingSigAlgParamPEM contains a certificate where the Certificate
|
||||
// signatureAlgorithm and the TBSCertificate signature contain
|
||||
// mismatching parameters
|
||||
const mismatchingSigAlgParamPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBCTCBrqADAgECAgEAMAoGCCqGSM49BAMCMAAwIhgPMDAwMTAxMDEwMDAwMDBa
|
||||
GA8wMDAxMDEwMTAwMDAwMFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOqV
|
||||
EDuVXxwZgIU3+dOwv1SsMu0xuV48hf7xmK8n7sAMYgllB+96DnPqBeboJj4snYnx
|
||||
0AcE0PDVQ1l4Z3YXsQWjFTATMBEGA1UdEQEB/wQHMAWCA2FzZDAMBggqhkjOPQQD
|
||||
AgUAA0gAMEUCIGLWPP9PYdPmcB+sPvOyBH7ryqHsStzpDiqXFgE6AobhAiEA62Mi
|
||||
f5C0D5ur7nk/ee3FM5VGrSe/WbCeSPC31xBH0Wc=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
func TestSigAlgMismatch(t *testing.T) {
|
||||
for _, certPEM := range []string{mismatchingSigAlgIDPEM, mismatchingSigAlgParamPEM} {
|
||||
b, _ := pem.Decode([]byte(certPEM))
|
||||
if b == nil {
|
||||
t.Fatalf("couldn't decode test certificate")
|
||||
}
|
||||
_, err := ParseCertificate(b.Bytes)
|
||||
if err == nil {
|
||||
t.Fatalf("expected ParseCertificate to fail")
|
||||
}
|
||||
expected := "x509: inner and outer signature algorithm identifiers don't match"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("unexpected error from ParseCertificate: got %q, want %q", err.Error(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user