mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 12:46:18 +08:00
golang version 1.4 is no longer supported
This commit is contained in:
parent
db223b3c7f
commit
aff4830dd8
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/emmansun/gmsm
|
module github.com/emmansun/gmsm
|
||||||
|
|
||||||
go 1.14
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
||||||
|
248
smx509/x509.go
248
smx509/x509.go
@ -238,6 +238,19 @@ var signatureAlgorithmDetails = []struct {
|
|||||||
{SM2WithSM3, "SM2-SM3", oidSignatureSM2WithSM3, x509.ECDSA, crypto.Hash(0) /* no pre-hashing */},
|
{SM2WithSM3, "SM2-SM3", oidSignatureSM2WithSM3, x509.ECDSA, crypto.Hash(0) /* no pre-hashing */},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashToPSSParameters contains the DER encoded RSA PSS parameters for the
|
||||||
|
// SHA256, SHA384, and SHA512 hashes as defined in RFC 3447, Appendix A.2.3.
|
||||||
|
// The parameters contain the following values:
|
||||||
|
// * hashAlgorithm contains the associated hash identifier with NULL parameters
|
||||||
|
// * maskGenAlgorithm always contains the default mgf1SHA1 identifier
|
||||||
|
// * saltLength contains the length of the associated hash
|
||||||
|
// * trailerField always contains the default trailerFieldBC value
|
||||||
|
var hashToPSSParameters = map[crypto.Hash]asn1.RawValue{
|
||||||
|
crypto.SHA256: asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 162, 3, 2, 1, 32}},
|
||||||
|
crypto.SHA384: asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 162, 3, 2, 1, 48}},
|
||||||
|
crypto.SHA512: asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 162, 3, 2, 1, 64}},
|
||||||
|
}
|
||||||
|
|
||||||
// pssParameters reflects the parameters in an AlgorithmIdentifier that
|
// pssParameters reflects the parameters in an AlgorithmIdentifier that
|
||||||
// specifies RSA PSS. See RFC 3447, Appendix A.2.3.
|
// specifies RSA PSS. See RFC 3447, Appendix A.2.3.
|
||||||
type pssParameters struct {
|
type pssParameters struct {
|
||||||
@ -250,51 +263,6 @@ type pssParameters struct {
|
|||||||
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
|
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// rsaPSSParameters returns an asn1.RawValue suitable for use as the Parameters
|
|
||||||
// in an AlgorithmIdentifier that specifies RSA PSS.
|
|
||||||
func rsaPSSParameters(hashFunc crypto.Hash) asn1.RawValue {
|
|
||||||
var hashOID asn1.ObjectIdentifier
|
|
||||||
|
|
||||||
switch hashFunc {
|
|
||||||
case crypto.SHA256:
|
|
||||||
hashOID = oidSHA256
|
|
||||||
case crypto.SHA384:
|
|
||||||
hashOID = oidSHA384
|
|
||||||
case crypto.SHA512:
|
|
||||||
hashOID = oidSHA512
|
|
||||||
}
|
|
||||||
|
|
||||||
params := pssParameters{
|
|
||||||
Hash: pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: hashOID,
|
|
||||||
Parameters: asn1.NullRawValue,
|
|
||||||
},
|
|
||||||
MGF: pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: oidMGF1,
|
|
||||||
},
|
|
||||||
SaltLength: hashFunc.Size(),
|
|
||||||
TrailerField: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
mgf1Params := pkix.AlgorithmIdentifier{
|
|
||||||
Algorithm: hashOID,
|
|
||||||
Parameters: asn1.NullRawValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
params.MGF.Parameters.FullBytes, err = asn1.Marshal(mgf1Params)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
serialized, err := asn1.Marshal(params)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return asn1.RawValue{FullBytes: serialized}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
|
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
|
||||||
if ai.Algorithm.Equal(oidSignatureEd25519) {
|
if ai.Algorithm.Equal(oidSignatureEd25519) {
|
||||||
// RFC 8410, Section 3
|
// RFC 8410, Section 3
|
||||||
@ -654,7 +622,7 @@ func checkSignature(algo x509.SignatureAlgorithm, signed, signature []byte, publ
|
|||||||
if !sm2.VerifyASN1WithSM2(pub, nil, signed, signature) {
|
if !sm2.VerifyASN1WithSM2(pub, nil, signed, signature) {
|
||||||
return errors.New("x509: ECDSA verification failure")
|
return errors.New("x509: ECDSA verification failure")
|
||||||
}
|
}
|
||||||
} else if !verifyECDSAASN1(pub, signed, signature) {
|
} else if !ecdsa.VerifyASN1(pub, signed, signature) {
|
||||||
return errors.New("x509: ECDSA verification failure")
|
return errors.New("x509: ECDSA verification failure")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -1415,9 +1383,15 @@ func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) boo
|
|||||||
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) (derBytes []byte, err error) {
|
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) (derBytes []byte, err error) {
|
||||||
var rawValues []asn1.RawValue
|
var rawValues []asn1.RawValue
|
||||||
for _, name := range dnsNames {
|
for _, name := range dnsNames {
|
||||||
|
if err := isIA5String(name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: 2, Bytes: []byte(name)})
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: 2, Bytes: []byte(name)})
|
||||||
}
|
}
|
||||||
for _, email := range emailAddresses {
|
for _, email := range emailAddresses {
|
||||||
|
if err := isIA5String(email); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: 2, Bytes: []byte(email)})
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: 2, Bytes: []byte(email)})
|
||||||
}
|
}
|
||||||
for _, rawIP := range ipAddresses {
|
for _, rawIP := range ipAddresses {
|
||||||
@ -1429,6 +1403,10 @@ func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris [
|
|||||||
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: 2, Bytes: ip})
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: 2, Bytes: ip})
|
||||||
}
|
}
|
||||||
for _, uri := range uris {
|
for _, uri := range uris {
|
||||||
|
uriStr := uri.String()
|
||||||
|
if err := isIA5String(uriStr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: 2, Bytes: []byte(uri.String())})
|
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: 2, Bytes: []byte(uri.String())})
|
||||||
}
|
}
|
||||||
return asn1.Marshal(rawValues)
|
return asn1.Marshal(rawValues)
|
||||||
@ -1444,67 +1422,32 @@ func isIA5String(s string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildExtensions(template *x509.Certificate, subjectIsEmpty bool, authorityKeyId []byte) (ret []pkix.Extension, err error) {
|
func buildCertExtensions(template *x509.Certificate, subjectIsEmpty bool, authorityKeyId []byte) (ret []pkix.Extension, err error) {
|
||||||
ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
|
ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
|
||||||
n := 0
|
n := 0
|
||||||
|
|
||||||
if template.KeyUsage != 0 &&
|
if template.KeyUsage != 0 &&
|
||||||
!oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
|
!oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
|
||||||
ret[n].Id = oidExtensionKeyUsage
|
ret[n], err = marshalKeyUsage(template.KeyUsage)
|
||||||
ret[n].Critical = true
|
|
||||||
|
|
||||||
var a [2]byte
|
|
||||||
a[0] = reverseBitsInAByte(byte(template.KeyUsage))
|
|
||||||
a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8))
|
|
||||||
|
|
||||||
l := 1
|
|
||||||
if a[1] != 0 {
|
|
||||||
l = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
bitString := a[:l]
|
|
||||||
ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
|
if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
|
||||||
!oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
|
!oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
|
||||||
ret[n].Id = oidExtensionExtendedKeyUsage
|
ret[n], err = marshalExtKeyUsage(template.ExtKeyUsage, template.UnknownExtKeyUsage)
|
||||||
|
|
||||||
var oids []asn1.ObjectIdentifier
|
|
||||||
for _, u := range template.ExtKeyUsage {
|
|
||||||
if oid, ok := oidFromExtKeyUsage(u); ok {
|
|
||||||
oids = append(oids, oid)
|
|
||||||
} else {
|
|
||||||
panic("internal error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oids = append(oids, template.UnknownExtKeyUsage...)
|
|
||||||
|
|
||||||
ret[n].Value, err = asn1.Marshal(oids)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
||||||
if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
|
if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
|
||||||
// Leaving MaxPathLen as zero indicates that no maximum path
|
ret[n], err = marshalBasicConstraints(template.IsCA, template.MaxPathLen, template.MaxPathLenZero)
|
||||||
// length is desired, unless MaxPathLenZero is set. A value of
|
|
||||||
// -1 causes encoding/asn1 to omit the value as desired.
|
|
||||||
maxPathLen := template.MaxPathLen
|
|
||||||
if maxPathLen == 0 && !template.MaxPathLenZero {
|
|
||||||
maxPathLen = -1
|
|
||||||
}
|
|
||||||
ret[n].Id = oidExtensionBasicConstraints
|
|
||||||
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, maxPathLen})
|
|
||||||
ret[n].Critical = true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
@ -1566,14 +1509,9 @@ func buildExtensions(template *x509.Certificate, subjectIsEmpty bool, authorityK
|
|||||||
|
|
||||||
if len(template.PolicyIdentifiers) > 0 &&
|
if len(template.PolicyIdentifiers) > 0 &&
|
||||||
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
|
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
|
||||||
ret[n].Id = oidExtensionCertificatePolicies
|
ret[n], err = marshalCertificatePolicies(template.PolicyIdentifiers)
|
||||||
policies := make([]policyInformation, len(template.PolicyIdentifiers))
|
|
||||||
for i, policy := range template.PolicyIdentifiers {
|
|
||||||
policies[i].Policy = policy
|
|
||||||
}
|
|
||||||
ret[n].Value, err = asn1.Marshal(policies)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
@ -1706,6 +1644,98 @@ func buildExtensions(template *x509.Certificate, subjectIsEmpty bool, authorityK
|
|||||||
return append(ret[:n], template.ExtraExtensions...), nil
|
return append(ret[:n], template.ExtraExtensions...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func marshalKeyUsage(ku x509.KeyUsage) (pkix.Extension, error) {
|
||||||
|
ext := pkix.Extension{Id: oidExtensionKeyUsage, Critical: true}
|
||||||
|
|
||||||
|
var a [2]byte
|
||||||
|
a[0] = reverseBitsInAByte(byte(ku))
|
||||||
|
a[1] = reverseBitsInAByte(byte(ku >> 8))
|
||||||
|
|
||||||
|
l := 1
|
||||||
|
if a[1] != 0 {
|
||||||
|
l = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
bitString := a[:l]
|
||||||
|
var err error
|
||||||
|
ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
|
||||||
|
if err != nil {
|
||||||
|
return ext, err
|
||||||
|
}
|
||||||
|
return ext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalExtKeyUsage(extUsages []x509.ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier) (pkix.Extension, error) {
|
||||||
|
ext := pkix.Extension{Id: oidExtensionExtendedKeyUsage}
|
||||||
|
|
||||||
|
oids := make([]asn1.ObjectIdentifier, len(extUsages)+len(unknownUsages))
|
||||||
|
for i, u := range extUsages {
|
||||||
|
if oid, ok := oidFromExtKeyUsage(u); ok {
|
||||||
|
oids[i] = oid
|
||||||
|
} else {
|
||||||
|
return ext, errors.New("x509: unknown extended key usage")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(oids[len(extUsages):], unknownUsages)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ext.Value, err = asn1.Marshal(oids)
|
||||||
|
if err != nil {
|
||||||
|
return ext, err
|
||||||
|
}
|
||||||
|
return ext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pkix.Extension, error) {
|
||||||
|
ext := pkix.Extension{Id: oidExtensionBasicConstraints, Critical: true}
|
||||||
|
// Leaving MaxPathLen as zero indicates that no maximum path
|
||||||
|
// length is desired, unless MaxPathLenZero is set. A value of
|
||||||
|
// -1 causes encoding/asn1 to omit the value as desired.
|
||||||
|
if maxPathLen == 0 && !maxPathLenZero {
|
||||||
|
maxPathLen = -1
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
ext.Value, err = asn1.Marshal(basicConstraints{isCA, maxPathLen})
|
||||||
|
if err != nil {
|
||||||
|
return ext, nil
|
||||||
|
}
|
||||||
|
return ext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
|
||||||
|
ext := pkix.Extension{Id: oidExtensionCertificatePolicies}
|
||||||
|
policies := make([]policyInformation, len(policyIdentifiers))
|
||||||
|
for i, policy := range policyIdentifiers {
|
||||||
|
policies[i].Policy = policy
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
ext.Value, err = asn1.Marshal(policies)
|
||||||
|
if err != nil {
|
||||||
|
return ext, err
|
||||||
|
}
|
||||||
|
return ext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCSRExtensions(template *x509.CertificateRequest) ([]pkix.Extension, error) {
|
||||||
|
var ret []pkix.Extension
|
||||||
|
|
||||||
|
if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) &&
|
||||||
|
!oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
|
||||||
|
sanBytes, err := marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, pkix.Extension{
|
||||||
|
Id: oidExtensionSubjectAltName,
|
||||||
|
Value: sanBytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(ret, template.ExtraExtensions...), nil
|
||||||
|
}
|
||||||
|
|
||||||
func subjectBytes(cert *x509.Certificate) ([]byte, error) {
|
func subjectBytes(cert *x509.Certificate) ([]byte, error) {
|
||||||
if len(cert.RawSubject) > 0 {
|
if len(cert.RawSubject) > 0 {
|
||||||
return cert.RawSubject, nil
|
return cert.RawSubject, nil
|
||||||
@ -1759,7 +1789,7 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureA
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestedSigAlgo == 0 || sigAlgo.Algorithm.Equal(oidSignatureSM2WithSM3) {
|
if requestedSigAlgo == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1771,12 +1801,12 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureA
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
|
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
|
||||||
if hashFunc == 0 && pubType != x509.Ed25519 {
|
if hashFunc == 0 && pubType != x509.Ed25519 && !sigAlgo.Algorithm.Equal(oidSignatureSM2WithSM3) {
|
||||||
err = errors.New("x509: cannot sign with hash function requested")
|
err = errors.New("x509: cannot sign with hash function requested")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isRSAPSS(requestedSigAlgo) {
|
if isRSAPSS(requestedSigAlgo) {
|
||||||
sigAlgo.Parameters = rsaPSSParameters(hashFunc)
|
sigAlgo.Parameters = hashToPSSParameters[hashFunc]
|
||||||
}
|
}
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
@ -1902,7 +1932,7 @@ func CreateCertificate(rand io.Reader, template, parent *x509.Certificate, pub,
|
|||||||
return nil, errors.New("x509: provided PrivateKey doesn't match parent's PublicKey")
|
return nil, errors.New("x509: provided PrivateKey doesn't match parent's PublicKey")
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions, err := buildExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject), authorityKeyId)
|
extensions, err := buildCertExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject), authorityKeyId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2171,23 +2201,11 @@ func CreateCertificateRequest(rand io.Reader, template *x509.CertificateRequest,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensions []pkix.Extension
|
extensions, err := buildCSRExtensions(template)
|
||||||
|
if err != nil {
|
||||||
if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) &&
|
return nil, err
|
||||||
!oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
|
|
||||||
sanBytes, err := marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions = append(extensions, pkix.Extension{
|
|
||||||
Id: oidExtensionSubjectAltName,
|
|
||||||
Value: sanBytes,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions = append(extensions, template.ExtraExtensions...)
|
|
||||||
|
|
||||||
// Make a copy of template.Attributes because we may alter it below.
|
// Make a copy of template.Attributes because we may alter it below.
|
||||||
attributes := make([]pkix.AttributeTypeAndValueSET, 0, len(template.Attributes))
|
attributes := make([]pkix.AttributeTypeAndValueSET, 0, len(template.Attributes))
|
||||||
for _, attr := range template.Attributes {
|
for _, attr := range template.Attributes {
|
||||||
@ -2422,7 +2440,6 @@ func (c *CertificateRequest) CheckSignature() error {
|
|||||||
// The issuer distinguished name CRL field and authority key identifier
|
// The issuer distinguished name CRL field and authority key identifier
|
||||||
// extension are populated using the issuer certificate. issuer must have
|
// extension are populated using the issuer certificate. issuer must have
|
||||||
// SubjectKeyId set.
|
// SubjectKeyId set.
|
||||||
/*
|
|
||||||
func CreateRevocationList(rand io.Reader, template *x509.RevocationList, issuer *Certificate, priv crypto.Signer) ([]byte, error) {
|
func CreateRevocationList(rand io.Reader, template *x509.RevocationList, issuer *Certificate, priv crypto.Signer) ([]byte, error) {
|
||||||
if template == nil {
|
if template == nil {
|
||||||
return nil, errors.New("x509: template can not be nil")
|
return nil, errors.New("x509: template can not be nil")
|
||||||
@ -2521,4 +2538,3 @@ func CreateRevocationList(rand io.Reader, template *x509.RevocationList, issuer
|
|||||||
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
|
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -2,6 +2,7 @@ package smx509
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
@ -31,6 +32,17 @@ MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAELfjZP28bYfGSvbODYlXiB5bcoXE+
|
|||||||
2LRjjpIH3DcCCct9FuVhi9cm60nDFrbW49k2D3GJco2iWPlr0+5LV+t4AQ==
|
2LRjjpIH3DcCCct9FuVhi9cm60nDFrbW49k2D3GJco2iWPlr0+5LV+t4AQ==
|
||||||
-----END PUBLIC KEY-----
|
-----END PUBLIC KEY-----
|
||||||
`
|
`
|
||||||
|
const publicKeyPemFromHuaweiKms = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEP3JLMIBPGUx88KChOY3WhjNVKOsk
|
||||||
|
RzYP5lpimwVS9CAK6MzL4kqudI7Pqi6hcir35zH8/BHMXzQ4fM2Ojp+59w==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const publicKeyPemFromHuaweiKmsForSign = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAENpoOih+9ASfmKYx5lK5mLsrUK3Am
|
||||||
|
B6kLUsqHlVyglXgoMEwo8Sr8xb/Q3gDMNnd7Wyp2bJE9ksb60ansO4QaKg==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
const publicKeyPemFromAliKmsForSign = `-----BEGIN PUBLIC KEY-----
|
const publicKeyPemFromAliKmsForSign = `-----BEGIN PUBLIC KEY-----
|
||||||
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAERrsLH25zLm2LIo6tivZM9afLprSX
|
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAERrsLH25zLm2LIo6tivZM9afLprSX
|
||||||
@ -40,6 +52,8 @@ MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAERrsLH25zLm2LIo6tivZM9afLprSX
|
|||||||
|
|
||||||
const hashBase64 = `Zsfw9GLu7dnR8tRr3BDk4kFnxIdc8veiKX2gK49LqOA=`
|
const hashBase64 = `Zsfw9GLu7dnR8tRr3BDk4kFnxIdc8veiKX2gK49LqOA=`
|
||||||
const signature = `MEUCIHV5hOCgYzlO4HkrUhct1Cc8BeKmbXNP+ASje5rGOcCYAiEA2XOajXo3/IihtCEJmNpImtWw3uHIy5CX5TIxit7V0gQ=`
|
const signature = `MEUCIHV5hOCgYzlO4HkrUhct1Cc8BeKmbXNP+ASje5rGOcCYAiEA2XOajXo3/IihtCEJmNpImtWw3uHIy5CX5TIxit7V0gQ=`
|
||||||
|
const signatureFromHuawei = `MEQCIGK8rWDJw5K7a6RZP5pDii8iqY3yLmavaXpkl7aDLORqAiAlMiiSvp7OJYBCJmzmwadBiBhdBnCCfIdjiWhXHX9xcw==`
|
||||||
|
|
||||||
const csrFromAli = `-----BEGIN CERTIFICATE REQUEST-----
|
const csrFromAli = `-----BEGIN CERTIFICATE REQUEST-----
|
||||||
MIIBYjCCAQkCAQAwRzELMAkGA1UEBhMCQ04xEzARBgNVBAMMCkNhcmdvU21hcnQx
|
MIIBYjCCAQkCAQAwRzELMAkGA1UEBhMCQ04xEzARBgNVBAMMCkNhcmdvU21hcnQx
|
||||||
DzANBgNVBAcMBlpodWhhaTESMBAGA1UECAwJR3Vhbmdkb25nMFkwEwYHKoZIzj0C
|
DzANBgNVBAcMBlpodWhhaTESMBAGA1UECAwJR3Vhbmdkb25nMFkwEwYHKoZIzj0C
|
||||||
@ -236,6 +250,20 @@ func TestSignByAliVerifyAtLocal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSignByHuaweiVerifyAtLocal(t *testing.T) {
|
||||||
|
dig, err := base64.StdEncoding.DecodeString(signatureFromHuawei)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pub, err := getPublicKey([]byte(publicKeyPemFromHuaweiKmsForSign))
|
||||||
|
pub1 := pub.(*ecdsa.PublicKey)
|
||||||
|
hashValue, _ := base64.StdEncoding.DecodeString(hashBase64)
|
||||||
|
result := sm2.VerifyASN1(pub1, hashValue, dig)
|
||||||
|
if !result {
|
||||||
|
t.Error("Verify fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParsePKIXPublicKey(t *testing.T) {
|
func TestParsePKIXPublicKey(t *testing.T) {
|
||||||
pub, err := getPublicKey([]byte(publicKeyPemFromAliKms))
|
pub, err := getPublicKey([]byte(publicKeyPemFromAliKms))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,6 +277,19 @@ func TestParsePKIXPublicKey(t *testing.T) {
|
|||||||
fmt.Printf("encrypted=%s\n", base64.StdEncoding.EncodeToString(encrypted))
|
fmt.Printf("encrypted=%s\n", base64.StdEncoding.EncodeToString(encrypted))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsePKIXPublicKeyFromHuawei(t *testing.T) {
|
||||||
|
pub, err := getPublicKey([]byte(publicKeyPemFromHuaweiKms))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pub1 := pub.(*ecdsa.PublicKey)
|
||||||
|
encrypted, err := sm2.Encrypt(rand.Reader, pub1, []byte("encryption standard"), sm2.NewASN1EncrypterOpts())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("encrypted=%s\n", base64.RawURLEncoding.EncodeToString(encrypted))
|
||||||
|
}
|
||||||
|
|
||||||
func TestMarshalPKIXPublicKey(t *testing.T) {
|
func TestMarshalPKIXPublicKey(t *testing.T) {
|
||||||
pub, err := getPublicKey([]byte(publicKeyPemFromAliKms))
|
pub, err := getPublicKey([]byte(publicKeyPemFromAliKms))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -681,3 +722,294 @@ func TestCRLCreation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateRevocationList(t *testing.T) {
|
||||||
|
sm2Priv, err := sm2.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate SM2 key: %s", err)
|
||||||
|
}
|
||||||
|
_, ed25519Priv, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate Ed25519 key: %s", err)
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
key crypto.Signer
|
||||||
|
issuer *x509.Certificate
|
||||||
|
template *x509.RevocationList
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil template",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: nil,
|
||||||
|
template: nil,
|
||||||
|
expectedError: "x509: template can not be nil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil issuer",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: nil,
|
||||||
|
template: &x509.RevocationList{},
|
||||||
|
expectedError: "x509: issuer can not be nil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "issuer doesn't have crlSign key usage bit set",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCertSign,
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{},
|
||||||
|
expectedError: "x509: issuer must have the crlSign key usage bit set",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "issuer missing SubjectKeyId",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{},
|
||||||
|
expectedError: "x509: issuer certificate doesn't contain a subject key identifier",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nextUpdate before thisUpdate",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour),
|
||||||
|
NextUpdate: time.Time{},
|
||||||
|
},
|
||||||
|
expectedError: "x509: template.ThisUpdate is after template.NextUpdate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil Number",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
expectedError: "x509: template contains nil Number field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid signature algorithm",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
SignatureAlgorithm: x509.SHA256WithRSA,
|
||||||
|
RevokedCertificates: []pkix.RevokedCertificate{
|
||||||
|
{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
RevocationTime: time.Time{}.Add(time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
expectedError: "x509: requested SignatureAlgorithm does not match private key type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
RevokedCertificates: []pkix.RevokedCertificate{
|
||||||
|
{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
RevocationTime: time.Time{}.Add(time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid, Ed25519 key",
|
||||||
|
key: ed25519Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
RevokedCertificates: []pkix.RevokedCertificate{
|
||||||
|
{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
RevocationTime: time.Time{}.Add(time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid, non-default signature algorithm",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
SignatureAlgorithm: x509.ECDSAWithSHA512,
|
||||||
|
RevokedCertificates: []pkix.RevokedCertificate{
|
||||||
|
{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
RevocationTime: time.Time{}.Add(time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid, extra extension",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
RevokedCertificates: []pkix.RevokedCertificate{
|
||||||
|
{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
RevocationTime: time.Time{}.Add(time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
ExtraExtensions: []pkix.Extension{
|
||||||
|
{
|
||||||
|
Id: []int{2, 5, 29, 99},
|
||||||
|
Value: []byte{5, 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid, empty list",
|
||||||
|
key: sm2Priv,
|
||||||
|
issuer: &x509.Certificate{
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "testing",
|
||||||
|
},
|
||||||
|
SubjectKeyId: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
template: &x509.RevocationList{
|
||||||
|
Number: big.NewInt(5),
|
||||||
|
ThisUpdate: time.Time{}.Add(time.Hour * 24),
|
||||||
|
NextUpdate: time.Time{}.Add(time.Hour * 48),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var issuer *Certificate
|
||||||
|
if tc.issuer != nil {
|
||||||
|
issuer = &Certificate{*tc.issuer}
|
||||||
|
}
|
||||||
|
crl, err := CreateRevocationList(rand.Reader, tc.template, issuer, tc.key)
|
||||||
|
if err != nil && tc.expectedError == "" {
|
||||||
|
t.Fatalf("CreateRevocationList failed unexpectedly: %s", err)
|
||||||
|
} else if err != nil && tc.expectedError != err.Error() {
|
||||||
|
t.Fatalf("CreateRevocationList failed unexpectedly, wanted: %s, got: %s", tc.expectedError, err)
|
||||||
|
} else if err == nil && tc.expectedError != "" {
|
||||||
|
t.Fatalf("CreateRevocationList didn't fail, expected: %s", tc.expectedError)
|
||||||
|
}
|
||||||
|
if tc.expectedError != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedCRL, err := ParseDERCRL(crl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to parse generated CRL: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.template.SignatureAlgorithm != x509.UnknownSignatureAlgorithm &&
|
||||||
|
parsedCRL.SignatureAlgorithm.Algorithm.Equal(signatureAlgorithmDetails[tc.template.SignatureAlgorithm].oid) {
|
||||||
|
t.Fatalf("SignatureAlgorithm mismatch: got %v; want %v.", parsedCRL.SignatureAlgorithm,
|
||||||
|
tc.template.SignatureAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(parsedCRL.TBSCertList.RevokedCertificates, tc.template.RevokedCertificates) {
|
||||||
|
t.Fatalf("RevokedCertificates mismatch: got %v; want %v.",
|
||||||
|
parsedCRL.TBSCertList.RevokedCertificates, tc.template.RevokedCertificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parsedCRL.TBSCertList.Extensions) != 2+len(tc.template.ExtraExtensions) {
|
||||||
|
t.Fatalf("Generated CRL has wrong number of extensions, wanted: %d, got: %d", 2+len(tc.template.ExtraExtensions), len(parsedCRL.TBSCertList.Extensions))
|
||||||
|
}
|
||||||
|
expectedAKI, err := asn1.Marshal(authKeyId{Id: tc.issuer.SubjectKeyId})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("asn1.Marshal failed: %s", err)
|
||||||
|
}
|
||||||
|
akiExt := pkix.Extension{
|
||||||
|
Id: oidExtensionAuthorityKeyId,
|
||||||
|
Value: expectedAKI,
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[0], akiExt) {
|
||||||
|
t.Fatalf("Unexpected first extension: got %v, want %v",
|
||||||
|
parsedCRL.TBSCertList.Extensions[0], akiExt)
|
||||||
|
}
|
||||||
|
expectedNum, err := asn1.Marshal(tc.template.Number)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("asn1.Marshal failed: %s", err)
|
||||||
|
}
|
||||||
|
crlExt := pkix.Extension{
|
||||||
|
Id: oidExtensionCRLNumber,
|
||||||
|
Value: expectedNum,
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[1], crlExt) {
|
||||||
|
t.Fatalf("Unexpected second extension: got %v, want %v",
|
||||||
|
parsedCRL.TBSCertList.Extensions[1], crlExt)
|
||||||
|
}
|
||||||
|
if len(parsedCRL.TBSCertList.Extensions[2:]) == 0 && len(tc.template.ExtraExtensions) == 0 {
|
||||||
|
// If we don't have anything to check return early so we don't
|
||||||
|
// hit a [] != nil false positive below.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[2:], tc.template.ExtraExtensions) {
|
||||||
|
t.Fatalf("Extensions mismatch: got %v; want %v.",
|
||||||
|
parsedCRL.TBSCertList.Extensions[2:], tc.template.ExtraExtensions)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user