mirror of
https://github.com/golang/go
synced 2024-11-13 15:00:23 -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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
@ -24,7 +23,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
@ -914,676 +912,6 @@ type distributionPointName struct {
|
|||||||
RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
|
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 {
|
func reverseBitsInAByte(in byte) byte {
|
||||||
b1 := in>>4 | in<<4
|
b1 := in>>4 | in<<4
|
||||||
b2 := b1>>2&0x33 | b1<<2&0xcc
|
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)
|
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