Add support for SubjectalternativeName type.

Move code around and some fixes.
This commit is contained in:
Mariano Cano 2020-07-01 18:27:29 -07:00
parent 2556b57906
commit 738304bc6f
4 changed files with 205 additions and 99 deletions

87
x509util/algorithms.go Normal file
View file

@ -0,0 +1,87 @@
package x509util
import (
"crypto/x509"
"strings"
"github.com/pkg/errors"
)
// List of signature algorithms, all of them have values in upper case to match
// them with the string representation.
const (
MD2_RSA = "MD2-RSA"
MD5_RSA = "MD5-RSA"
SHA1_RSA = "SHA1-RSA"
SHA256_RSA = "SHA256-RSA"
SHA384_RSA = "SHA384-RSA"
SHA512_RSA = "SHA512-RSA"
SHA256_RSAPSS = "SHA256-RSAPSS"
SHA384_RSAPSS = "SHA384-RSAPSS"
SHA512_RSAPSS = "SHA512-RSAPSS"
DSA_SHA1 = "DSA-SHA1"
DSA_SHA256 = "DSA-SHA256"
ECDSA_SHA1 = "ECDSA-SHA1"
ECDSA_SHA256 = "ECDSA-SHA256"
ECDSA_SHA384 = "ECDSA-SHA384"
ECDSA_SHA512 = "ECDSA-SHA512"
Ed25519 = "ED25519"
)
// SignatureAlgorithm is the JSON representation of the X509 signature algorithms
type SignatureAlgorithm x509.SignatureAlgorithm
// Set sets the signature algorithm in the given certificate.
func (s SignatureAlgorithm) Set(c *x509.Certificate) {
c.SignatureAlgorithm = x509.SignatureAlgorithm(s)
}
// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
// object in the Subject struct or a string as just the subject common name.
func (s *SignatureAlgorithm) UnmarshalJSON(data []byte) error {
name, err := unmarshalString(data)
if err != nil {
return err
}
var sa x509.SignatureAlgorithm
switch strings.ToUpper(name) {
case MD2_RSA:
sa = x509.MD2WithRSA
case MD5_RSA:
sa = x509.MD5WithRSA
case SHA1_RSA:
sa = x509.SHA1WithRSA
case SHA256_RSA:
sa = x509.SHA256WithRSA
case SHA384_RSA:
sa = x509.SHA384WithRSA
case SHA512_RSA:
sa = x509.SHA512WithRSA
case SHA256_RSAPSS:
sa = x509.SHA256WithRSAPSS
case SHA384_RSAPSS:
sa = x509.SHA384WithRSAPSS
case SHA512_RSAPSS:
sa = x509.SHA512WithRSAPSS
case DSA_SHA1:
sa = x509.DSAWithSHA1
case DSA_SHA256:
sa = x509.DSAWithSHA256
case ECDSA_SHA1:
sa = x509.ECDSAWithSHA1
case ECDSA_SHA256:
sa = x509.ECDSAWithSHA256
case ECDSA_SHA384:
sa = x509.ECDSAWithSHA384
case ECDSA_SHA512:
sa = x509.ECDSAWithSHA512
case Ed25519:
sa = x509.PureEd25519
default:
return errors.Errorf("unsupported signatureAlgorithm %s", name)
}
*s = SignatureAlgorithm(sa)
return nil
}

View file

@ -5,7 +5,6 @@ import (
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/json" "encoding/json"
"math/big" "math/big"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -13,11 +12,13 @@ import (
type Certificate struct { type Certificate struct {
Version int `json:"version"` Version int `json:"version"`
Subject Subject `json:"subject"` Subject Subject `json:"subject"`
Issuer Issuer `json:"issuer"`
SerialNumber SerialNumber `json:"serialNumber"` SerialNumber SerialNumber `json:"serialNumber"`
DNSNames MultiString `json:"dnsNames"` DNSNames MultiString `json:"dnsNames"`
EmailAddresses MultiString `json:"emailAddresses"` EmailAddresses MultiString `json:"emailAddresses"`
IPAddresses MultiIP `json:"ipAddresses"` IPAddresses MultiIP `json:"ipAddresses"`
URIs MultiURL `json:"uris"` URIs MultiURL `json:"uris"`
SANs []SubjectAlternativeName `json:"sans"`
Extensions []Extension `json:"extensions"` Extensions []Extension `json:"extensions"`
KeyUsage KeyUsage `json:"keyUsage"` KeyUsage KeyUsage `json:"keyUsage"`
ExtKeyUsage ExtKeyUsage `json:"extKeyUsage"` ExtKeyUsage ExtKeyUsage `json:"extKeyUsage"`
@ -104,8 +105,9 @@ func (c *Certificate) GetCertificate() *x509.Certificate {
return cert return cert
} }
// Subject is the JSON representation of the X509 subject field. // Name is the JSON representation of X.501 type Name, used in the X.509 subject
type Subject struct { // and issuer fields.
type Name struct {
Country MultiString `json:"country"` Country MultiString `json:"country"`
Organization MultiString `json:"organization"` Organization MultiString `json:"organization"`
OrganizationalUnit MultiString `json:"organizationUnit"` OrganizationalUnit MultiString `json:"organizationUnit"`
@ -117,6 +119,26 @@ type Subject struct {
CommonName string `json:"commonName"` CommonName string `json:"commonName"`
} }
// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
// object in the Subject struct or a string as just the subject common name.
func (n *Name) UnmarshalJSON(data []byte) error {
if cn, ok := maybeString(data); ok {
n.CommonName = cn
return nil
}
type nameAlias Name
var nn nameAlias
if err := json.Unmarshal(data, &nn); err != nil {
return errors.Wrap(err, "error unmarshaling json")
}
*n = Name(nn)
return nil
}
// Subject is the JSON representation of the X.509 subject field.
type Subject Name
func newSubject(n pkix.Name) Subject { func newSubject(n pkix.Name) Subject {
return Subject{ return Subject{
Country: n.Country, Country: n.Country,
@ -146,21 +168,36 @@ func (s Subject) Set(c *x509.Certificate) {
} }
} }
// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON // Issuer is the JSON representation of the X.509 issuer field.
// object in the Subject struct or a string as just the subject common name. type Issuer Name
func (s *Subject) UnmarshalJSON(data []byte) error {
if cn, ok := maybeString(data); ok {
s.CommonName = cn
return nil
}
type subjectAlias Subject func newIssuer(n pkix.Name) Issuer {
var ss subjectAlias return Issuer{
if err := json.Unmarshal(data, &ss); err != nil { Country: n.Country,
return errors.Wrap(err, "error unmarshaling json") Organization: n.Organization,
OrganizationalUnit: n.OrganizationalUnit,
Locality: n.Locality,
Province: n.Province,
StreetAddress: n.StreetAddress,
PostalCode: n.PostalCode,
SerialNumber: n.SerialNumber,
CommonName: n.CommonName,
}
}
// Set sets the issuer in the given certificate.
func (i Issuer) Set(c *x509.Certificate) {
c.Issuer = pkix.Name{
Country: i.Country,
Organization: i.Organization,
OrganizationalUnit: i.OrganizationalUnit,
Locality: i.Locality,
Province: i.Province,
StreetAddress: i.StreetAddress,
PostalCode: i.PostalCode,
SerialNumber: i.SerialNumber,
CommonName: i.CommonName,
} }
*s = Subject(ss)
return nil
} }
// SerialNumber is the JSON representation of the X509 serial number. // SerialNumber is the JSON representation of the X509 serial number.
@ -173,6 +210,13 @@ func (s SerialNumber) Set(c *x509.Certificate) {
c.SerialNumber = s.Int c.SerialNumber = s.Int
} }
func (s *SerialNumber) MarshalJSON() ([]byte, error) {
if s == nil || s.Int == nil {
return []byte(`null`), nil
}
return s.Int.MarshalJSON()
}
// UnmarshalJSON implements the json.Unmarshal interface and unmarshals an // UnmarshalJSON implements the json.Unmarshal interface and unmarshals an
// integer or a string into a serial number. If a string is used, a prefix of // integer or a string into a serial number. If a string is used, a prefix of
// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8, and “0x” or // “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8, and “0x” or
@ -201,63 +245,3 @@ func (s *SerialNumber) UnmarshalJSON(data []byte) error {
} }
return nil return nil
} }
// SignatureAlgorithm is the JSON representation of the X509 signature algorithms
type SignatureAlgorithm x509.SignatureAlgorithm
// Set sets the signature algorithm in the given certificate.
func (s SignatureAlgorithm) Set(c *x509.Certificate) {
c.SignatureAlgorithm = s
}
// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
// object in the Subject struct or a string as just the subject common name.
func (s *SignatureAlgorithm) UnmarshalJSON(data []byte) error {
s, err := unmarshalString(data)
if err != nil {
return err
}
var sa x509.SignatureAlgorithm
switch strings.ToUpper(s) {
case "MD2-RSA":
sa = x509.MD2WithRSA
case "MD5-RSA":
sa = x509.MD5WithRSA
case "SHA1-RSA":
sa = x509.SHA1WithRSA
case "SHA1-RSA":
sa = x509.SHA1WithRSA
case "SHA256-RSA":
sa = x509.SHA256WithRSA
case "SHA384-RSA":
sa = x509.SHA384WithRSA
case "SHA512-RSA":
sa = x509.SHA512WithRSA
case "SHA256-RSAPSS":
sa = x509.SHA256WithRSAPSS
case "SHA384-RSAPSS":
sa = x509.SHA384WithRSAPSS
case "SHA512-RSAPSS":
sa = x509.SHA512WithRSAPSS
case "DSA-SHA1":
sa = x509.DSAWithSHA1
case "DSA-SHA256":
sa = x509.DSAWithSHA256
case "ECDSA-SHA1":
sa = x509.ECDSAWithSHA1
case "ECDSA-SHA256":
sa = x509.ECDSAWithSHA256
case "ECDSA-SHA384":
sa = x509.ECDSAWithSHA384
case "ECDSA-SHA512":
sa = x509.ECDSAWithSHA512
case "ED25519":
sa = x509.PureEd25519
default:
return errors.Errorf("unsupported signatureAlgorithm %s", s)
}
*s = SignatureAlgorithm(sa)
return nil
}

View file

@ -42,7 +42,5 @@ func (c *CertificateRequest) GetCertificate() *Certificate {
Extensions: c.Extensions, Extensions: c.Extensions,
PublicKey: c.PublicKey, PublicKey: c.PublicKey,
PublicKeyAlgorithm: c.PublicKeyAlgorithm, PublicKeyAlgorithm: c.PublicKeyAlgorithm,
PublicKey: c.PublicKey,
PublicKeyAlgorithm: c.PublicKeyAlgorithm,
} }
} }

View file

@ -4,8 +4,13 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"fmt"
"net"
"net/url"
"strings" "strings"
"github.com/smallstep/cli/crypto/x509util"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -80,6 +85,38 @@ func (o *ObjectIdentifier) UnmarshalJSON(data []byte) error {
return nil return nil
} }
type SubjectAlternativeName struct {
Type string `json:"type"`
Value string `json:"value"`
}
func (s SubjectAlternativeName) Set(c *x509.Certificate) {
switch strings.ToLower(s.Type) {
case "dns":
c.DNSNames = append(c.DNSNames, s.Value)
case "email":
c.EmailAddresses = append(c.EmailAddresses, s.Value)
case "ip":
// The validation of the IP would happen in the unmarshaling, but just
// to be sure we are only adding valid IPs.
if ip := net.ParseIP(s.Value); ip != nil {
c.IPAddresses = append(c.IPAddresses, ip)
}
case "uri":
if u, err := url.Parse(s.Value); err != nil {
c.URIs = append(c.URIs, u)
}
case "auto", "":
dnsNames, ips, emails, uris := x509util.SplitSANs([]string{s.Value})
c.DNSNames = append(c.DNSNames, dnsNames...)
c.IPAddresses = append(c.IPAddresses, ips...)
c.EmailAddresses = append(c.EmailAddresses, emails...)
c.URIs = append(c.URIs, uris...)
default:
panic(fmt.Sprintf("unsupported subject alternative name type %s", s.Type))
}
}
// KeyUsage type represents the JSON array used to represent the key usages of a // KeyUsage type represents the JSON array used to represent the key usages of a
// X509 certificate. // X509 certificate.
type KeyUsage x509.KeyUsage type KeyUsage x509.KeyUsage