forked from TrueCloudLab/certificates
Add support for SubjectalternativeName type.
Move code around and some fixes.
This commit is contained in:
parent
2556b57906
commit
738304bc6f
4 changed files with 205 additions and 99 deletions
87
x509util/algorithms.go
Normal file
87
x509util/algorithms.go
Normal 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
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue