]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/crypto/x509/x509.go
Use GoGOST's PublicKey wrappers
[gostls13.git] / src / crypto / x509 / x509.go
index bcc14a0056d5e542bd567b6d31fe44c6025da387..494c3b37fb928fc52e0f5b9b7567ca31496b8240 100644 (file)
@@ -2,12 +2,28 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package x509 parses X.509-encoded keys and certificates.
+// Package x509 implements a subset of the X.509 standard.
+//
+// It allows parsing and generating certificates, certificate signing
+// requests, certificate revocation lists, and encoded public and private keys.
+// It provides a certificate verifier, complete with a chain builder.
+//
+// The package targets the X.509 technical profile defined by the IETF (RFC
+// 2459/3280/5280), and as further restricted by the CA/Browser Forum Baseline
+// Requirements. There is minimal support for features outside of these
+// profiles, as the primary goal of the package is to provide compatibility
+// with the publicly trusted TLS certificate ecosystem and its policies and
+// constraints.
+//
+// On macOS and Windows, certificate verification is handled by system APIs, but
+// the package aims to apply consistent validation rules across operating
+// systems.
 package x509
 
 import (
        "bytes"
        "crypto"
+       "crypto/ecdh"
        "crypto/ecdsa"
        "crypto/ed25519"
        "crypto/elliptic"
@@ -33,6 +49,7 @@ import (
        _ "crypto/sha256"
        _ "crypto/sha512"
 
+       "crypto/go.cypherpunks.ru/gogost/v5/gost3410"
        "golang.org/x/crypto/cryptobyte"
        cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
 )
@@ -44,12 +61,12 @@ type pkixPublicKey struct {
        BitString asn1.BitString
 }
 
-// ParsePKIXPublicKey parses a public key in PKIX, ASN.1 DER form.
-// The encoded public key is a SubjectPublicKeyInfo structure
-// (see RFC 5280, Section 4.1).
+// ParsePKIXPublicKey parses a public key in PKIX, ASN.1 DER form. The encoded
+// public key is a SubjectPublicKeyInfo structure (see RFC 5280, Section 4.1).
 //
-// It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, or
-// ed25519.PublicKey. More types might be supported in the future.
+// It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
+// ed25519.PublicKey (not a pointer), or *ecdh.PublicKey (for X25519).
+// More types might be supported in the future.
 //
 // This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
 func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) {
@@ -62,11 +79,7 @@ func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) {
        } else if len(rest) != 0 {
                return nil, errors.New("x509: trailing data after ASN.1 of public-key")
        }
-       algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
-       if algo == UnknownPublicKeyAlgorithm {
-               return nil, errors.New("x509: unknown public key algorithm")
-       }
-       return parsePublicKey(algo, &pki)
+       return parsePublicKey(&pki)
 }
 
 func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) {
@@ -84,11 +97,14 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
                // RFC 3279, Section 2.3.1.
                publicKeyAlgorithm.Parameters = asn1.NullRawValue
        case *ecdsa.PublicKey:
-               publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
                oid, ok := oidFromNamedCurve(pub.Curve)
                if !ok {
                        return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
                }
+               if !pub.Curve.IsOnCurve(pub.X, pub.Y) {
+                       return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: invalid elliptic curve public key")
+               }
+               publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
                publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
                var paramBytes []byte
                paramBytes, err = asn1.Marshal(oid)
@@ -99,6 +115,80 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
        case ed25519.PublicKey:
                publicKeyBytes = pub
                publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
+       case *ecdh.PublicKey:
+               publicKeyBytes = pub.Bytes()
+               if pub.Curve() == ecdh.X25519() {
+                       publicKeyAlgorithm.Algorithm = oidPublicKeyX25519
+               } else {
+                       oid, ok := oidFromECDHCurve(pub.Curve())
+                       if !ok {
+                               return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
+                       }
+                       publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
+                       var paramBytes []byte
+                       paramBytes, err = asn1.Marshal(oid)
+                       if err != nil {
+                               return
+                       }
+                       publicKeyAlgorithm.Parameters.FullBytes = paramBytes
+               }
+       case *gost3410.PublicKey:
+               builder := cryptobyte.NewBuilder(nil)
+               builder.AddASN1OctetString(pub.Raw())
+               publicKeyBytes, err = builder.Bytes()
+               if err != nil {
+                       return
+               }
+               params := GostR341012PublicKeyParameters{}
+               switch pub.C.Name {
+               case "id-GostR3410-2001-CryptoPro-A-ParamSet":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidGostR34102001CryptoProAParamSet
+                       params.DigestParamSet = oidTc26Gost34112012256
+               case "id-GostR3410-2001-CryptoPro-B-ParamSet":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidGostR34102001CryptoProBParamSet
+                       params.DigestParamSet = oidTc26Gost34112012256
+               case "id-GostR3410-2001-CryptoPro-C-ParamSet":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidGostR34102001CryptoProCParamSet
+                       params.DigestParamSet = oidTc26Gost34112012256
+               case "id-GostR3410-2001-CryptoPro-XchA-ParamSet":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidGostR34102001CryptoProXchAParamSet
+                       params.DigestParamSet = oidTc26Gost34112012256
+               case "id-GostR3410-2001-CryptoPro-XchB-ParamSet":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidGostR34102001CryptoProXchBParamSet
+                       params.DigestParamSet = oidTc26Gost34112012256
+               case "id-tc26-gost-3410-12-256-paramSetA":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidTc26Gost341012256ParamSetA
+               case "id-tc26-gost-3410-12-256-paramSetB":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidTc26Gost341012256ParamSetB
+               case "id-tc26-gost-3410-12-256-paramSetC":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidTc26Gost341012256ParamSetC
+               case "id-tc26-gost-3410-12-256-paramSetD":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012256
+                       params.PublicKeyParamSet = oidTc26Gost341012256ParamSetD
+               case "id-tc26-gost-3410-12-512-paramSetA":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012512
+                       params.PublicKeyParamSet = oidTc26Gost341012512ParamSetA
+               case "id-tc26-gost-3410-12-512-paramSetB":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012512
+                       params.PublicKeyParamSet = oidTc26Gost341012512ParamSetB
+               case "id-tc26-gost-3410-12-512-paramSetC":
+                       publicKeyAlgorithm.Algorithm = oidTc26Gost341012512
+                       params.PublicKeyParamSet = oidTc26Gost341012512ParamSetC
+               default:
+                       return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported GOST elliptic curve")
+               }
+               publicKeyAlgorithm.Parameters.FullBytes, err = asn1.Marshal(params)
+               if err != nil {
+                       panic(err)
+               }
        default:
                return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
        }
@@ -110,8 +200,9 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
 // The encoded public key is a SubjectPublicKeyInfo structure
 // (see RFC 5280, Section 4.1).
 //
-// The following key types are currently supported: *rsa.PublicKey, *ecdsa.PublicKey
-// and ed25519.PublicKey. Unsupported key types result in an error.
+// The following key types are currently supported: *rsa.PublicKey,
+// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey.
+// Unsupported key types result in an error.
 //
 // This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
 func MarshalPKIXPublicKey(pub any) ([]byte, error) {
@@ -138,7 +229,6 @@ func MarshalPKIXPublicKey(pub any) ([]byte, error) {
 // These structures reflect the ASN.1 structure of X.509 certificates.:
 
 type certificate struct {
-       Raw                asn1.RawContent
        TBSCertificate     tbsCertificate
        SignatureAlgorithm pkix.AlgorithmIdentifier
        SignatureValue     asn1.BitString
@@ -155,7 +245,7 @@ type tbsCertificate struct {
        PublicKey          publicKeyInfo
        UniqueId           asn1.BitString   `asn1:"optional,tag:1"`
        SubjectUniqueId    asn1.BitString   `asn1:"optional,tag:2"`
-       Extensions         []pkix.Extension `asn1:"optional,explicit,tag:3"`
+       Extensions         []pkix.Extension `asn1:"omitempty,optional,explicit,tag:3"`
 }
 
 type dsaAlgorithmParameters struct {
@@ -198,6 +288,8 @@ const (
        SHA384WithRSAPSS
        SHA512WithRSAPSS
        PureEd25519
+       GOST256
+       GOST512
 )
 
 func (algo SignatureAlgorithm) isRSAPSS() bool {
@@ -223,9 +315,10 @@ type PublicKeyAlgorithm int
 const (
        UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
        RSA
-       DSA // Unsupported.
+       DSA // Only supported for parsing.
        ECDSA
        Ed25519
+       GOST
 )
 
 var publicKeyAlgoName = [...]string{
@@ -233,6 +326,7 @@ var publicKeyAlgoName = [...]string{
        DSA:     "DSA",
        ECDSA:   "ECDSA",
        Ed25519: "Ed25519",
+       GOST:    "GOST",
 }
 
 func (algo PublicKeyAlgorithm) String() string {
@@ -308,6 +402,25 @@ var (
        oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
        oidSignatureEd25519         = asn1.ObjectIdentifier{1, 3, 101, 112}
 
+       oidTc26Gost341012256                  = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 1, 1}
+       oidTc26Gost341012512                  = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 1, 2}
+       oidTc26Gost34112012256                = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 2, 2}
+       oidTc26Gost34112012512                = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 2, 3}
+       oidTc26Gost341012256Signature         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 3, 2}
+       oidTc26Gost341012512Signature         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 1, 3, 3}
+       oidGostR34102001CryptoProAParamSet    = asn1.ObjectIdentifier{1, 2, 643, 2, 2, 35, 1}
+       oidGostR34102001CryptoProBParamSet    = asn1.ObjectIdentifier{1, 2, 643, 2, 2, 35, 2}
+       oidGostR34102001CryptoProCParamSet    = asn1.ObjectIdentifier{1, 2, 643, 2, 2, 35, 3}
+       oidGostR34102001CryptoProXchAParamSet = asn1.ObjectIdentifier{1, 2, 643, 2, 2, 36, 0}
+       oidGostR34102001CryptoProXchBParamSet = asn1.ObjectIdentifier{1, 2, 643, 2, 2, 36, 1}
+       oidTc26Gost341012256ParamSetA         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 1, 1}
+       oidTc26Gost341012256ParamSetB         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 1, 2}
+       oidTc26Gost341012256ParamSetC         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 1, 3}
+       oidTc26Gost341012256ParamSetD         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 1, 4}
+       oidTc26Gost341012512ParamSetA         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 2, 1}
+       oidTc26Gost341012512ParamSetB         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 2, 2}
+       oidTc26Gost341012512ParamSetC         = asn1.ObjectIdentifier{1, 2, 643, 7, 1, 2, 1, 2, 3}
+
        oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
        oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
        oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
@@ -344,6 +457,8 @@ var signatureAlgorithmDetails = []struct {
        {ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, ECDSA, crypto.SHA384},
        {ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, ECDSA, crypto.SHA512},
        {PureEd25519, "Ed25519", oidSignatureEd25519, Ed25519, crypto.Hash(0) /* no pre-hashing */},
+       {GOST256, "GOST256", oidTc26Gost341012256Signature, GOST, crypto.GOSTR34112012256},
+       {GOST512, "GOST512", oidTc26Gost341012512Signature, GOST, crypto.GOSTR34112012512},
 }
 
 // hashToPSSParameters contains the DER encoded RSA PSS parameters for the
@@ -427,27 +542,37 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm
        return UnknownSignatureAlgorithm
 }
 
-// RFC 3279, 2.3 Public Key Algorithms
-//
-//     pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
-//             rsadsi(113549) pkcs(1) 1 }
-//
-// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
-//
-//     id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
-//             x9-57(10040) x9cm(4) 1 }
-//
-// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
-//
-//     id-ecPublicKey OBJECT IDENTIFIER ::= {
-//             iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
 var (
-       oidPublicKeyRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
-       oidPublicKeyDSA     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
-       oidPublicKeyECDSA   = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
-       oidPublicKeyEd25519 = oidSignatureEd25519
+       // RFC 3279, 2.3 Public Key Algorithms
+       //
+       //      pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
+       //              rsadsi(113549) pkcs(1) 1 }
+       //
+       // rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
+       //
+       //      id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
+       //              x9-57(10040) x9cm(4) 1 }
+       oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+       oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+       // RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
+       //
+       //      id-ecPublicKey OBJECT IDENTIFIER ::= {
+       //              iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
+       oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
+       // RFC 8410, Section 3
+       //
+       //      id-X25519    OBJECT IDENTIFIER ::= { 1 3 101 110 }
+       //      id-Ed25519   OBJECT IDENTIFIER ::= { 1 3 101 112 }
+       oidPublicKeyX25519  = asn1.ObjectIdentifier{1, 3, 101, 110}
+       oidPublicKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
+
+       oidPublicKeyGOST256 = oidTc26Gost341012256
+       oidPublicKeyGOST512 = oidTc26Gost341012512
 )
 
+// getPublicKeyAlgorithmFromOID returns the exposed PublicKeyAlgorithm
+// identifier for public key types supported in certificates and CSRs. Marshal
+// and Parse functions may support a different set of public key types.
 func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
        switch {
        case oid.Equal(oidPublicKeyRSA):
@@ -458,6 +583,10 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm
                return ECDSA
        case oid.Equal(oidPublicKeyEd25519):
                return Ed25519
+       case oid.Equal(oidPublicKeyGOST256):
+               return GOST
+       case oid.Equal(oidPublicKeyGOST512):
+               return GOST
        }
        return UnknownPublicKeyAlgorithm
 }
@@ -514,6 +643,21 @@ func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
        return nil, false
 }
 
+func oidFromECDHCurve(curve ecdh.Curve) (asn1.ObjectIdentifier, bool) {
+       switch curve {
+       case ecdh.X25519():
+               return oidPublicKeyX25519, true
+       case ecdh.P256():
+               return oidNamedCurveP256, true
+       case ecdh.P384():
+               return oidNamedCurveP384, true
+       case ecdh.P521():
+               return oidNamedCurveP521, true
+       }
+
+       return nil, false
+}
+
 // KeyUsage represents the set of actions that are valid for a given key. It's
 // a bitmap of the KeyUsage* constants.
 type KeyUsage int
@@ -725,15 +869,12 @@ type Certificate struct {
 // involves algorithms that are not currently implemented.
 var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented")
 
-// debugAllowSHA1 allows SHA-1 signatures. See issue 41682.
-var debugAllowSHA1 = godebug.Get("x509sha1") == "1"
-
 // An InsecureAlgorithmError indicates that the SignatureAlgorithm used to
 // generate the signature is not secure, and the signature has been rejected.
 //
 // To temporarily restore support for SHA-1 signatures, include the value
 // "x509sha1=1" in the GODEBUG environment variable. Note that this option will
-// be removed in Go 1.19.
+// be removed in a future release.
 type InsecureAlgorithmError SignatureAlgorithm
 
 func (e InsecureAlgorithmError) Error() string {
@@ -764,8 +905,10 @@ func (c *Certificate) hasSANExtension() bool {
        return oidInExtensions(oidExtensionSubjectAltName, c.Extensions)
 }
 
-// CheckSignatureFrom verifies that the signature on c is a valid signature
-// from parent. SHA1WithRSA and ECDSAWithSHA1 signatures are not supported.
+// CheckSignatureFrom verifies that the signature on c is a valid signature from parent.
+//
+// This is a low-level API that performs very limited checks, and not a full
+// path verifier. Most users should use [Certificate.Verify] instead.
 func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
        // RFC 5280, 4.2.1.9:
        // "If the basic constraints extension is not present in a version 3
@@ -785,13 +928,16 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
                return ErrUnsupportedAlgorithm
        }
 
-       // TODO(agl): don't ignore the path length constraint.
-
-       return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, debugAllowSHA1)
+       return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, false)
 }
 
 // CheckSignature verifies that signature is a valid signature over signed from
 // c's public key.
+//
+// This is a low-level API that performs no validity checks on the certificate.
+//
+// [MD5WithRSA] signatures are rejected, while [SHA1WithRSA] and [ECDSAWithSHA1]
+// signatures are currently accepted.
 func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) error {
        return checkSignature(algo, signed, signature, c.PublicKey, true)
 }
@@ -813,6 +959,8 @@ func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm,
        return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey)
 }
 
+var x509sha1 = godebug.New("x509sha1")
+
 // checkSignature verifies that signature is a valid signature over signed from
 // a crypto.PublicKey.
 func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) {
@@ -834,7 +982,8 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
        case crypto.MD5:
                return InsecureAlgorithmError(algo)
        case crypto.SHA1:
-               if !allowSHA1 {
+               // SHA-1 signatures are mostly disabled. See go.dev/issue/41682.
+               if !allowSHA1 && x509sha1.Value() != "1" {
                        return InsecureAlgorithmError(algo)
                }
                fallthrough
@@ -873,6 +1022,18 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
                        return errors.New("x509: Ed25519 verification failure")
                }
                return
+       case *gost3410.PublicKey:
+               if pubKeyAlgo != GOST {
+                       return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
+               }
+               ok, err := gost3410.PublicKeyReverseDigest{Pub: pub}.VerifyDigest(signed, signature)
+               if err != nil {
+                       return err
+               }
+               if !ok {
+                       return errors.New("x509: GOST verification failure")
+               }
+               return err
        }
        return ErrUnsupportedAlgorithm
 }
@@ -973,7 +1134,7 @@ var (
        oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2}
 )
 
-// oidNotInExtensions reports whether an extension with the given oid exists in
+// oidInExtensions reports whether an extension with the given oid exists in
 // extensions.
 func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool {
        for _, e := range extensions {
@@ -1373,8 +1534,19 @@ func signingParamsForPublicKey(pub any, requestedSigAlgo SignatureAlgorithm) (ha
                pubType = Ed25519
                sigAlgo.Algorithm = oidSignatureEd25519
 
+       case *gost3410.PublicKey:
+               pubType = GOST
+               switch pub.C.PointSize() {
+               case 256 / 8:
+                       hashFunc = crypto.GOSTR34112012256
+                       sigAlgo.Algorithm = oidTc26Gost341012256Signature
+               case 512 / 8:
+                       hashFunc = crypto.GOSTR34112012512
+                       sigAlgo.Algorithm = oidTc26Gost341012512Signature
+               }
+
        default:
-               err = errors.New("x509: only RSA, ECDSA and Ed25519 keys supported")
+               err = errors.New("x509: only RSA, ECDSA, Ed25519 and GOST keys supported")
        }
 
        if err != nil {
@@ -1397,6 +1569,10 @@ func signingParamsForPublicKey(pub any, requestedSigAlgo SignatureAlgorithm) (ha
                                err = errors.New("x509: cannot sign with hash function requested")
                                return
                        }
+                       if hashFunc == crypto.MD5 {
+                               err = errors.New("x509: signing with MD5 is not supported")
+                               return
+                       }
                        if requestedSigAlgo.isRSAPSS() {
                                sigAlgo.Parameters = hashToPSSParameters[hashFunc]
                        }
@@ -1478,6 +1654,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
                return nil, errors.New("x509: no SerialNumber given")
        }
 
+       // RFC 5280 Section 4.1.2.2: serial number must positive
+       //
+       // We _should_ also restrict serials to <= 20 octets, but it turns out a lot of people
+       // get this wrong, in part because the encoding can itself alter the length of the
+       // serial. For now we accept these non-conformant serials.
+       if template.SerialNumber.Sign() == -1 {
+               return nil, errors.New("x509: serial number must be positive")
+       }
+
        if template.BasicConstraintsValid && !template.IsCA && template.MaxPathLen != -1 && (template.MaxPathLen != 0 || template.MaxPathLenZero) {
                return nil, errors.New("x509: only CAs are allowed to specify MaxPathLen")
        }
@@ -1491,6 +1676,9 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
        if err != nil {
                return nil, err
        }
+       if getPublicKeyAlgorithmFromOID(publicKeyAlgorithm.Algorithm) == UnknownPublicKeyAlgorithm {
+               return nil, fmt.Errorf("x509: unsupported public key type: %T", pub)
+       }
 
        asn1Issuer, err := subjectBytes(parent)
        if err != nil {
@@ -1572,7 +1760,6 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
        }
 
        signedCert, err := asn1.Marshal(certificate{
-               nil,
                c,
                signatureAlgorithm,
                asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
@@ -1582,15 +1769,8 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
        }
 
        // Check the signature to ensure the crypto.Signer behaved correctly.
-       sigAlg := getSignatureAlgorithmFromAI(signatureAlgorithm)
-       switch sigAlg {
-       case MD5WithRSA:
-               // We skip the check if the signature algorithm is only supported for
-               // signing, not verification.
-       default:
-               if err := checkSignature(sigAlg, c.Raw, signature, key.Public(), true); err != nil {
-                       return nil, fmt.Errorf("x509: signature over certificate returned by signer is invalid: %w", err)
-               }
+       if err := checkSignature(getSignatureAlgorithmFromAI(signatureAlgorithm), c.Raw, signature, key.Public(), true); err != nil {
+               return nil, fmt.Errorf("x509: signature over certificate returned by signer is invalid: %w", err)
        }
 
        return signedCert, nil
@@ -1809,6 +1989,7 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
        }
 
        var ret []pkix.Extension
+       requestedExts := make(map[string]bool)
        for _, rawAttr := range rawAttributes {
                var attr pkcs10Attribute
                if rest, err := asn1.Unmarshal(rawAttr.FullBytes, &attr); err != nil || len(rest) != 0 || len(attr.Values) == 0 {
@@ -1824,6 +2005,13 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
                if _, err := asn1.Unmarshal(attr.Values[0].FullBytes, &extensions); err != nil {
                        return nil, err
                }
+               for _, ext := range extensions {
+                       oidStr := ext.Id.String()
+                       if requestedExts[oidStr] {
+                               return nil, errors.New("x509: certificate request contains duplicate requested extensions")
+                       }
+                       requestedExts[oidStr] = true
+               }
                ret = append(ret, extensions...)
        }
 
@@ -2038,9 +2226,11 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
        }
 
        var err error
-       out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCSR.PublicKey)
-       if err != nil {
-               return nil, err
+       if out.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
+               out.PublicKey, err = parsePublicKey(&in.TBSCSR.PublicKey)
+               if err != nil {
+                       return nil, err
+               }
        }
 
        var subject pkix.RDNSequence
@@ -2077,11 +2267,21 @@ func (c *CertificateRequest) CheckSignature() error {
 // RevocationList contains the fields used to create an X.509 v2 Certificate
 // Revocation list with CreateRevocationList.
 type RevocationList struct {
-       Raw                  []byte
+       // Raw contains the complete ASN.1 DER content of the CRL (tbsCertList,
+       // signatureAlgorithm, and signatureValue.)
+       Raw []byte
+       // RawTBSRevocationList contains just the tbsCertList portion of the ASN.1
+       // DER.
        RawTBSRevocationList []byte
-       RawIssuer            []byte
-
-       Issuer         pkix.Name
+       // RawIssuer contains the DER encoded Issuer.
+       RawIssuer []byte
+
+       // Issuer contains the DN of the issuing certificate.
+       Issuer pkix.Name
+       // AuthorityKeyId is used to identify the public key associated with the
+       // issuing certificate. It is populated from the authorityKeyIdentifier
+       // extension when parsing a CRL. It is ignored when creating a CRL; the
+       // extension is populated from the issuing certificate itself.
        AuthorityKeyId []byte
 
        Signature []byte
@@ -2097,7 +2297,8 @@ type RevocationList struct {
 
        // Number is used to populate the X.509 v2 cRLNumber extension in the CRL,
        // which should be a monotonically increasing sequence number for a given
-       // CRL scope and CRL issuer.
+       // CRL scope and CRL issuer. It is also populated from the cRLNumber
+       // extension when parsing a CRL.
        Number *big.Int
 
        // ThisUpdate is used to populate the thisUpdate field in the CRL, which
@@ -2117,6 +2318,29 @@ type RevocationList struct {
        ExtraExtensions []pkix.Extension
 }
 
+// These structures reflect the ASN.1 structure of X.509 CRLs better than
+// the existing crypto/x509/pkix variants do. These mirror the existing
+// certificate structs in this file.
+//
+// Notably, we include issuer as an asn1.RawValue, mirroring the behavior of
+// tbsCertificate and allowing raw (unparsed) subjects to be passed cleanly.
+type certificateList struct {
+       TBSCertList        tbsCertificateList
+       SignatureAlgorithm pkix.AlgorithmIdentifier
+       SignatureValue     asn1.BitString
+}
+
+type tbsCertificateList struct {
+       Raw                 asn1.RawContent
+       Version             int `asn1:"optional,default:0"`
+       Signature           pkix.AlgorithmIdentifier
+       Issuer              asn1.RawValue
+       ThisUpdate          time.Time
+       NextUpdate          time.Time                 `asn1:"optional"`
+       RevokedCertificates []pkix.RevokedCertificate `asn1:"optional"`
+       Extensions          []pkix.Extension          `asn1:"tag:0,optional,explicit"`
+}
+
 // CreateRevocationList creates a new X.509 v2 Certificate Revocation List,
 // according to RFC 5280, based on template.
 //
@@ -2165,15 +2389,25 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
        if err != nil {
                return nil, err
        }
+
+       if numBytes := template.Number.Bytes(); len(numBytes) > 20 || (len(numBytes) == 20 && numBytes[0]&0x80 != 0) {
+               return nil, errors.New("x509: CRL number exceeds 20 octets")
+       }
        crlNum, err := asn1.Marshal(template.Number)
        if err != nil {
                return nil, err
        }
 
-       tbsCertList := pkix.TBSCertificateList{
+       // Correctly use the issuer's subject sequence if one is specified.
+       issuerSubject, err := subjectBytes(issuer)
+       if err != nil {
+               return nil, err
+       }
+
+       tbsCertList := tbsCertificateList{
                Version:    1, // v2
                Signature:  signatureAlgorithm,
-               Issuer:     issuer.Subject.ToRDNSequence(),
+               Issuer:     asn1.RawValue{FullBytes: issuerSubject},
                ThisUpdate: template.ThisUpdate.UTC(),
                NextUpdate: template.NextUpdate.UTC(),
                Extensions: []pkix.Extension{
@@ -2200,6 +2434,10 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
                return nil, err
        }
 
+       // Optimization to only marshal this struct once, when signing and
+       // then embedding in certificateList below.
+       tbsCertList.Raw = tbsCertListContents
+
        input := tbsCertListContents
        if hashFunc != 0 {
                h := hashFunc.New()
@@ -2219,7 +2457,7 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
                return nil, err
        }
 
-       return asn1.Marshal(pkix.CertificateList{
+       return asn1.Marshal(certificateList{
                TBSCertList:        tbsCertList,
                SignatureAlgorithm: signatureAlgorithm,
                SignatureValue:     asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},