// 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"
_ "crypto/sha256"
_ "crypto/sha512"
+ "crypto/go.cypherpunks.ru/gogost/v5/gost3410"
"golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)
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) {
} 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) {
// 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)
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)
}
// 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) {
// 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
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 {
MD2WithRSA // Unsupported.
MD5WithRSA // Only supported for signing, not verification.
- SHA1WithRSA // Only supported for signing, not verification.
+ SHA1WithRSA // Only supported for signing, and verification of CRLs, CSRs, and OCSP responses.
SHA256WithRSA
SHA384WithRSA
SHA512WithRSA
DSAWithSHA1 // Unsupported.
DSAWithSHA256 // Unsupported.
- ECDSAWithSHA1 // Only supported for signing, not verification.
+ ECDSAWithSHA1 // Only supported for signing, and verification of CRLs, CSRs, and OCSP responses.
ECDSAWithSHA256
ECDSAWithSHA384
ECDSAWithSHA512
SHA384WithRSAPSS
SHA512WithRSAPSS
PureEd25519
+ GOST256
+ GOST512
)
func (algo SignatureAlgorithm) isRSAPSS() bool {
const (
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
RSA
- DSA // Unsupported.
+ DSA // Only supported for parsing.
ECDSA
Ed25519
+ GOST
)
var publicKeyAlgoName = [...]string{
DSA: "DSA",
ECDSA: "ECDSA",
Ed25519: "Ed25519",
+ GOST: "GOST",
}
func (algo PublicKeyAlgorithm) String() string {
// OIDs for signature algorithms
//
-// pkcs-1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
-//
+// pkcs-1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
//
// RFC 3279 2.2.1 RSA Signature Algorithms
//
-// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
+// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
//
-// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
+// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
//
-// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
+// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
//
-// dsaWithSha1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
+// dsaWithSha1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
//
// RFC 3279 2.2.3 ECDSA Signature Algorithm
//
-// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) ansi-x962(10045)
-// signatures(4) ecdsa-with-SHA1(1)}
-//
+// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-x962(10045)
+// signatures(4) ecdsa-with-SHA1(1)}
//
// RFC 4055 5 PKCS #1 Version 1.5
//
-// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
-//
-// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
+// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
//
-// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
+// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
//
+// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
//
// RFC 5758 3.1 DSA Signature Algorithms
//
-// dsaWithSha256 OBJECT IDENTIFIER ::= {
-// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
-// csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
+// dsaWithSha256 OBJECT IDENTIFIER ::= {
+// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
+// csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
//
// RFC 5758 3.2 ECDSA Signature Algorithm
//
-// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
+// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
//
-// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
-//
-// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
+// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
//
+// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
//
// RFC 8410 3 Curve25519 and Curve448 Algorithm Identifiers
//
-// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
-
+// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
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}
{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
// 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
+// - 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}},
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):
return ECDSA
case oid.Equal(oidPublicKeyEd25519):
return Ed25519
+ case oid.Equal(oidPublicKeyGOST256):
+ return GOST
+ case oid.Equal(oidPublicKeyGOST512):
+ return GOST
}
return UnknownPublicKeyAlgorithm
}
// RFC 5480, 2.1.1.1. Named Curve
//
-// secp224r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
+// secp224r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
//
-// secp256r1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
-// prime(1) 7 }
+// secp256r1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
+// prime(1) 7 }
//
-// secp384r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
+// secp384r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
//
-// secp521r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
+// secp521r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
//
// NB: secp256r1 is equivalent to prime256v1
var (
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
// RFC 5280, 4.2.1.12 Extended Key Usage
//
-// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
+// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
//
-// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
//
-// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
-// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
-// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
-// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
-// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
-// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
+// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
+// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
+// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
+// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
+// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
+// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
var (
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
// 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 {
return oidInExtensions(oidExtensionSubjectAltName, c.Extensions)
}
-// CheckSignatureFrom verifies that the signature on c is a valid signature
-// from parent.
+// 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
return ErrUnsupportedAlgorithm
}
- // TODO(agl): don't ignore the path length constraint.
-
- return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature)
+ 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)
+ return checkSignature(algo, signed, signature, c.PublicKey, true)
}
func (c *Certificate) hasNameConstraints() bool {
return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey)
}
-// CheckSignature verifies that signature is a valid signature over signed from
+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) (err error) {
+func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) {
var hashType crypto.Hash
var pubKeyAlgo PublicKeyAlgorithm
case crypto.MD5:
return InsecureAlgorithmError(algo)
case crypto.SHA1:
- if !debugAllowSHA1 {
+ // SHA-1 signatures are mostly disabled. See go.dev/issue/41682.
+ if !allowSHA1 && x509sha1.Value() != "1" {
return InsecureAlgorithmError(algo)
}
fallthrough
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
}
// CheckCRLSignature checks that the signature in crl is from c.
+//
+// Deprecated: Use RevocationList.CheckSignatureFrom instead.
func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) error {
algo := getSignatureAlgorithmFromAI(crl.SignatureAlgorithm)
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
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 {
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
+ return ext, err
}
func marshalExtKeyUsage(extUsages []ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier) (pkix.Extension, error) {
var err error
ext.Value, err = asn1.Marshal(oids)
- if err != nil {
- return ext, err
- }
- return ext, nil
+ return ext, err
}
func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pkix.Extension, error) {
}
var err error
ext.Value, err = asn1.Marshal(basicConstraints{isCA, maxPathLen})
- if err != nil {
- return ext, nil
- }
- return ext, nil
+ return ext, err
}
func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
}
var err error
ext.Value, err = asn1.Marshal(policies)
- if err != nil {
- return ext, err
- }
- return ext, nil
+ return ext, err
}
func buildCSRExtensions(template *CertificateRequest) ([]pkix.Extension, error) {
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 {
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]
}
// CreateCertificate creates a new X.509 v3 certificate based on a template.
// The following members of template are currently used:
//
-// - AuthorityKeyId
-// - BasicConstraintsValid
-// - CRLDistributionPoints
-// - DNSNames
-// - EmailAddresses
-// - ExcludedDNSDomains
-// - ExcludedEmailAddresses
-// - ExcludedIPRanges
-// - ExcludedURIDomains
-// - ExtKeyUsage
-// - ExtraExtensions
-// - IPAddresses
-// - IsCA
-// - IssuingCertificateURL
-// - KeyUsage
-// - MaxPathLen
-// - MaxPathLenZero
-// - NotAfter
-// - NotBefore
-// - OCSPServer
-// - PermittedDNSDomains
-// - PermittedDNSDomainsCritical
-// - PermittedEmailAddresses
-// - PermittedIPRanges
-// - PermittedURIDomains
-// - PolicyIdentifiers
-// - SerialNumber
-// - SignatureAlgorithm
-// - Subject
-// - SubjectKeyId
-// - URIs
-// - UnknownExtKeyUsage
+// - AuthorityKeyId
+// - BasicConstraintsValid
+// - CRLDistributionPoints
+// - DNSNames
+// - EmailAddresses
+// - ExcludedDNSDomains
+// - ExcludedEmailAddresses
+// - ExcludedIPRanges
+// - ExcludedURIDomains
+// - ExtKeyUsage
+// - ExtraExtensions
+// - IPAddresses
+// - IsCA
+// - IssuingCertificateURL
+// - KeyUsage
+// - MaxPathLen
+// - MaxPathLenZero
+// - NotAfter
+// - NotBefore
+// - OCSPServer
+// - PermittedDNSDomains
+// - PermittedDNSDomainsCritical
+// - PermittedEmailAddresses
+// - PermittedIPRanges
+// - PermittedURIDomains
+// - PolicyIdentifiers
+// - SerialNumber
+// - SignatureAlgorithm
+// - Subject
+// - SubjectKeyId
+// - URIs
+// - UnknownExtKeyUsage
//
// The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the
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")
}
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 {
}
signedCert, err := asn1.Marshal(certificate{
- nil,
c,
signatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
}
// Check the signature to ensure the crypto.Signer behaved correctly.
- sigAlg := getSignatureAlgorithmFromAI(signatureAlgorithm)
- switch sigAlg {
- case MD5WithRSA, SHA1WithRSA, ECDSAWithSHA1:
- // 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()); 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
// encoded CRLs will appear where they should be DER encoded, so this function
// will transparently handle PEM encoding as long as there isn't any leading
// garbage.
+//
+// Deprecated: Use ParseRevocationList instead.
func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) {
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
block, _ := pem.Decode(crlBytes)
}
// ParseDERCRL parses a DER encoded CRL from the given bytes.
+//
+// Deprecated: Use ParseRevocationList instead.
func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {
certList := new(pkix.CertificateList)
if rest, err := asn1.Unmarshal(derBytes, certList); err != nil {
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
// contains the given list of revoked certificates.
//
-// Note: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
+// Deprecated: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
// To generate a standards compliant CRL, use CreateRevocationList instead.
func (c *Certificate) CreateCRL(rand io.Reader, priv any, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
key, ok := priv.(crypto.Signer)
}
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 {
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...)
}
// CreateCertificateRequest creates a new certificate request based on a
// template. The following members of template are used:
//
-// - SignatureAlgorithm
-// - Subject
-// - DNSNames
-// - EmailAddresses
-// - IPAddresses
-// - URIs
-// - ExtraExtensions
-// - Attributes (deprecated)
+// - SignatureAlgorithm
+// - Subject
+// - DNSNames
+// - EmailAddresses
+// - IPAddresses
+// - URIs
+// - ExtraExtensions
+// - Attributes (deprecated)
//
// priv is the private key to sign the CSR with, and the corresponding public
// key will be included in the CSR. It must implement crypto.Signer and its
}
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
// CheckSignature reports whether the signature on c is valid.
func (c *CertificateRequest) CheckSignature() error {
- return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey)
+ return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey, true)
}
// RevocationList contains the fields used to create an X.509 v2 Certificate
// Revocation list with CreateRevocationList.
type RevocationList struct {
+ // 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 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
// SignatureAlgorithm is used to determine the signature algorithm to be
// used when signing the CRL. If 0 the default algorithm for the signing
// key will be used.
// 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
// indicates the issuance date of the CRL.
ThisUpdate time.Time
// indicates the date by which the next CRL will be issued. NextUpdate
// must be greater than ThisUpdate.
NextUpdate time.Time
+
+ // Extensions contains raw X.509 extensions. When creating a CRL,
+ // the Extensions field is ignored, see ExtraExtensions.
+ Extensions []pkix.Extension
+
// ExtraExtensions contains any additional extensions to add directly to
// the CRL.
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.
//
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{
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()
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},
})
}
+
+// CheckSignatureFrom verifies that the signature on rl is a valid signature
+// from issuer.
+func (rl *RevocationList) CheckSignatureFrom(parent *Certificate) error {
+ if parent.Version == 3 && !parent.BasicConstraintsValid ||
+ parent.BasicConstraintsValid && !parent.IsCA {
+ return ConstraintViolationError{}
+ }
+
+ if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCRLSign == 0 {
+ return ConstraintViolationError{}
+ }
+
+ if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm {
+ return ErrUnsupportedAlgorithm
+ }
+
+ return parent.CheckSignature(rl.SignatureAlgorithm, rl.RawTBSRevocationList, rl.Signature)
+}