You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
8.8 KiB
288 lines
8.8 KiB
package cmstest
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"math/big"
|
|
"time"
|
|
"unicode/utf16"
|
|
)
|
|
|
|
// CertSpec describes a certificate that NewCert writes field by field, from
|
|
// the DER of its tbsCertificate, to write what crypto/x509 does not let one
|
|
// write: names of any string type with any bytes, an attribute twice or none,
|
|
// no version, times with a fraction, an extension twice, a compressed key or
|
|
// an even modulus, and any signature, which DateKeys does not check (spec
|
|
// §29.10). Each field holds the DER of its element as it goes in the
|
|
// certificate; nil takes the default of a certificate of version 3 of the key.
|
|
type CertSpec struct {
|
|
// CN names the subject, CN = CN in a UTF8String, when Subject is nil.
|
|
CN string
|
|
// From and To are the validity, 2020-01-01 to 2040-01-01 by default,
|
|
// written as RFC 5280 does when NotBefore and NotAfter are nil.
|
|
From, To time.Time
|
|
// Version is the field [0] EXPLICIT of the version, INTEGER 2 (version
|
|
// 3) by default; NoVersion leaves it out, as a version 1 certificate.
|
|
Version []byte
|
|
NoVersion bool
|
|
// Serial is the serialNumber, a random positive INTEGER by default.
|
|
Serial []byte
|
|
// SigAlg is the AlgorithmIdentifier of the signature, in tbsCertificate
|
|
// and after it: that of the key with SHA-256 by default.
|
|
SigAlg []byte
|
|
// Issuer and Subject are the names; the issuer is the subject by default,
|
|
// as in a self-signed certificate.
|
|
Issuer, Subject []byte
|
|
// NotBefore and NotAfter are the times of validity as written.
|
|
NotBefore, NotAfter []byte
|
|
// SPKI is the SubjectPublicKeyInfo, that of the key by default.
|
|
SPKI []byte
|
|
// UniqueIDs are written after the SPKI: [1] issuerUniqueID and [2]
|
|
// subjectUniqueID.
|
|
UniqueIDs [][]byte
|
|
// SKI is the keyIdentifier of the extension subjectKeyIdentifier of the
|
|
// default extensions, and what a sid by subjectKeyIdentifier names: 20
|
|
// bytes of the hash of the SPKI by default.
|
|
SKI []byte
|
|
// Extensions are the Extension elements of [3]: subjectKeyIdentifier and
|
|
// keyUsage by default. An empty, non-nil slice writes [3] with an empty
|
|
// SEQUENCE, and NoExtensions writes no [3].
|
|
Extensions [][]byte
|
|
NoExtensions bool
|
|
// After are elements written at the end of tbsCertificate.
|
|
After [][]byte
|
|
// Signature is the BIT STRING of the signature as written; by default
|
|
// the key signs tbsCertificate.
|
|
Signature []byte
|
|
}
|
|
|
|
// NewCert returns a signer of key whose certificate is the one that spec
|
|
// describes.
|
|
func NewCert(spec CertSpec, key crypto.Signer) Signer {
|
|
from, to := spec.From, spec.To
|
|
if from.IsZero() {
|
|
from = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
}
|
|
if to.IsZero() {
|
|
to = time.Date(2040, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
}
|
|
serial := spec.Serial
|
|
if serial == nil {
|
|
n, err := rand.Int(rand.Reader, big.NewInt(1<<62))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
serial = BigInt(n.Add(n, big.NewInt(1)))
|
|
}
|
|
sigAlg := spec.SigAlg
|
|
if sigAlg == nil {
|
|
sigAlg = AlgID(OIDECDSA256)
|
|
if _, ok := key.(*rsa.PrivateKey); ok {
|
|
sigAlg = AlgID(OIDSHA256RSA, Null())
|
|
}
|
|
}
|
|
subject := spec.Subject
|
|
if subject == nil {
|
|
cn := spec.CN
|
|
if cn == "" {
|
|
cn = "DateKeys test"
|
|
}
|
|
subject = Name(ATV(OIDCommonName, UTF8(cn)))
|
|
}
|
|
issuer := spec.Issuer
|
|
if issuer == nil {
|
|
issuer = subject
|
|
}
|
|
notBefore, notAfter := spec.NotBefore, spec.NotAfter
|
|
if notBefore == nil {
|
|
notBefore = CertTimeOf(from)
|
|
}
|
|
if notAfter == nil {
|
|
notAfter = CertTimeOf(to)
|
|
}
|
|
spki := spec.SPKI
|
|
if spki == nil {
|
|
spki = SPKI(key.Public())
|
|
}
|
|
ski := spec.SKI
|
|
if ski == nil {
|
|
h := sha256.Sum256(spki)
|
|
ski = h[:20]
|
|
}
|
|
var tbs [][]byte
|
|
if !spec.NoVersion {
|
|
version := spec.Version
|
|
if version == nil {
|
|
version = tlv(0xa0, Int(2))
|
|
}
|
|
tbs = append(tbs, version)
|
|
}
|
|
tbs = append(tbs, serial, sigAlg, issuer, Seq(notBefore, notAfter), subject, spki)
|
|
tbs = append(tbs, spec.UniqueIDs...)
|
|
if !spec.NoExtensions {
|
|
exts := spec.Extensions
|
|
if exts == nil {
|
|
exts = [][]byte{ExtSKI(ski), ExtKeyUsage()}
|
|
}
|
|
tbs = append(tbs, tlv(0xa3, Seq(exts...)))
|
|
}
|
|
tbsDER := Seq(append(tbs, spec.After...)...)
|
|
signature := spec.Signature
|
|
if signature == nil {
|
|
_, sig := sign(key, Options{Hash: crypto.SHA256}, sum(crypto.SHA256, tbsDER))
|
|
signature = BitString(sig)
|
|
}
|
|
c := &Cert{Raw: Seq(tbsDER, sigAlg, signature), RawIssuer: issuer, Serial: serial}
|
|
if spec.Extensions == nil && !spec.NoExtensions || spec.SKI != nil {
|
|
c.SubjectKeyId = ski
|
|
}
|
|
return Signer{Cert: c, Key: key}
|
|
}
|
|
|
|
// The attribute types of a name.
|
|
var (
|
|
OIDCommonName = asn1.ObjectIdentifier{2, 5, 4, 3}
|
|
OIDSurname = asn1.ObjectIdentifier{2, 5, 4, 4}
|
|
OIDSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5}
|
|
OIDCountry = asn1.ObjectIdentifier{2, 5, 4, 6}
|
|
OIDOrganization = asn1.ObjectIdentifier{2, 5, 4, 10}
|
|
OIDOrgUnit = asn1.ObjectIdentifier{2, 5, 4, 11}
|
|
OIDGivenName = asn1.ObjectIdentifier{2, 5, 4, 42}
|
|
|
|
OIDSKI = asn1.ObjectIdentifier{2, 5, 29, 14}
|
|
OIDKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15}
|
|
)
|
|
|
|
// Name is a Name with one RelativeDistinguishedName for each attribute, in
|
|
// the order given.
|
|
func Name(atvs ...[]byte) []byte {
|
|
rdns := make([][]byte, len(atvs))
|
|
for i, a := range atvs {
|
|
rdns[i] = tlv(0x31, a)
|
|
}
|
|
return Seq(rdns...)
|
|
}
|
|
|
|
// RDN is a RelativeDistinguishedName of several attributes, in the order
|
|
// given, for NameOf.
|
|
func RDN(atvs ...[]byte) []byte { return tlv(0x31, atvs...) }
|
|
|
|
// NameOf is a Name of the RelativeDistinguishedName elements given.
|
|
func NameOf(rdns ...[]byte) []byte { return Seq(rdns...) }
|
|
|
|
// ATV is an AttributeTypeAndValue.
|
|
func ATV(oid asn1.ObjectIdentifier, value []byte) []byte { return Seq(OID(oid), value) }
|
|
|
|
// The string types of a name, with the bytes as given: those that break
|
|
// their type too.
|
|
|
|
// UTF8 is a UTF8String.
|
|
func UTF8(s string) []byte { return tlv(0x0c, []byte(s)) }
|
|
|
|
// Numeric is a NumericString.
|
|
func Numeric(s string) []byte { return tlv(0x12, []byte(s)) }
|
|
|
|
// Printable is a PrintableString.
|
|
func Printable(s string) []byte { return tlv(0x13, []byte(s)) }
|
|
|
|
// Teletex is a TeletexString.
|
|
func Teletex(s string) []byte { return tlv(0x14, []byte(s)) }
|
|
|
|
// IA5 is an IA5String.
|
|
func IA5(s string) []byte { return tlv(0x16, []byte(s)) }
|
|
|
|
// Visible is a VisibleString.
|
|
func Visible(s string) []byte { return tlv(0x1a, []byte(s)) }
|
|
|
|
// BMP is a BMPString with the bytes as given: of odd length, or with a
|
|
// surrogate.
|
|
func BMP(b []byte) []byte { return tlv(0x1e, b) }
|
|
|
|
// BMPText is the BMPString of s in UTF-16BE; a code point outside the BMP
|
|
// becomes a surrogate pair.
|
|
func BMPText(s string) []byte {
|
|
var b []byte
|
|
for _, u := range utf16.Encode([]rune(s)) {
|
|
b = append(b, byte(u>>8), byte(u))
|
|
}
|
|
return BMP(b)
|
|
}
|
|
|
|
// Extension is an Extension, with critical only when it is true, as DER
|
|
// writes it.
|
|
func Extension(oid asn1.ObjectIdentifier, critical bool, value []byte) []byte {
|
|
f := [][]byte{OID(oid)}
|
|
if critical {
|
|
f = append(f, Bool(true))
|
|
}
|
|
return Seq(append(f, Octets(value))...)
|
|
}
|
|
|
|
// ExtSKI is the extension subjectKeyIdentifier with the keyIdentifier id.
|
|
func ExtSKI(id []byte) []byte { return Extension(OIDSKI, false, Octets(id)) }
|
|
|
|
// ExtKeyUsage is the extension keyUsage with digitalSignature.
|
|
func ExtKeyUsage() []byte { return Extension(OIDKeyUsage, true, []byte{0x03, 0x02, 0x07, 0x80}) }
|
|
|
|
// SPKI is the SubjectPublicKeyInfo of a public key, as crypto/x509 writes it:
|
|
// an EC point uncompressed, and RSA with NULL parameters.
|
|
func SPKI(pub crypto.PublicKey) []byte {
|
|
b, err := x509.MarshalPKIXPublicKey(pub)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// CurveOID returns the named curve of curve: P-256, P-384 or P-521.
|
|
func CurveOID(curve elliptic.Curve) asn1.ObjectIdentifier {
|
|
switch curve {
|
|
case elliptic.P384():
|
|
return OIDP384
|
|
case elliptic.P521():
|
|
return OIDP521
|
|
}
|
|
return OIDP256
|
|
}
|
|
|
|
// Uncompressed returns the point of pub, uncompressed.
|
|
func Uncompressed(pub *ecdsa.PublicKey) []byte {
|
|
b, err := pub.Bytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Compressed returns the point of pub, compressed (SEC 1 2.3.3).
|
|
func Compressed(pub *ecdsa.PublicKey) []byte {
|
|
u := Uncompressed(pub)
|
|
n := (len(u) - 1) / 2
|
|
return append([]byte{2 | u[len(u)-1]&1}, u[1:1+n]...)
|
|
}
|
|
|
|
// SPKIEC is the SubjectPublicKeyInfo of id-ecPublicKey with the parameters
|
|
// and the point given.
|
|
func SPKIEC(params, point []byte) []byte {
|
|
return Seq(AlgID(OIDECPublicKey, params), BitString(point))
|
|
}
|
|
|
|
// SPKICompressed is the SubjectPublicKeyInfo of pub with its point
|
|
// compressed, which the table of spec §29.10 does not have.
|
|
func SPKICompressed(pub *ecdsa.PublicKey) []byte {
|
|
return SPKIEC(OID(CurveOID(pub.Curve)), Compressed(pub))
|
|
}
|
|
|
|
// SPKIRSA is the SubjectPublicKeyInfo of rsaEncryption with NULL parameters
|
|
// and the modulus and exponent given: an even modulus, or a size outside the
|
|
// table.
|
|
func SPKIRSA(n, e *big.Int) []byte {
|
|
return Seq(AlgID(OIDRSA, Null()), BitString(Seq(BigInt(n), BigInt(e))))
|
|
}
|