diff --git a/x509util/certificate_test.go b/x509util/certificate_test.go index 140578ec..a84f6eeb 100644 --- a/x509util/certificate_test.go +++ b/x509util/certificate_test.go @@ -105,7 +105,7 @@ func (b *badSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) } func TestNewCertificate(t *testing.T) { - cr, priv := createCertificateRequest(t, "commonName", []string{"foo.com"}) + cr, priv := createCertificateRequest(t, "commonName", []string{"foo.com", "root@foo.com"}) crBadSignateure, _ := createCertificateRequest(t, "fail", []string{"foo.com"}) crBadSignateure.PublicKey = priv.Public() @@ -120,9 +120,10 @@ func TestNewCertificate(t *testing.T) { wantErr bool }{ {"okSimple", args{cr, nil}, &Certificate{ - Subject: Subject{CommonName: "commonName"}, - DNSNames: []string{"foo.com"}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), + Subject: Subject{CommonName: "commonName"}, + DNSNames: []string{"foo.com"}, + EmailAddresses: []string{"root@foo.com"}, + KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth, @@ -142,8 +143,30 @@ func TestNewCertificate(t *testing.T) { PublicKey: priv.Public(), PublicKeyAlgorithm: x509.Ed25519, }, false}, + {"okExample", args{cr, []Option{WithTemplateFile("./testdata/example.tpl", TemplateData{ + SANsKey: []SubjectAlternativeName{ + {Type: "dns", Value: "foo.com"}, + }, + TokenKey: map[string]string{ + "Issuer": "https://iss", + "Subject": "sub", + }, + })}}, &Certificate{ + Subject: Subject{CommonName: "commonName"}, + SANs: []SubjectAlternativeName{{Type: DNSType, Value: "foo.com"}}, + EmailAddresses: []string{"root@foo.com"}, + URIs: []*url.URL{{Scheme: "https", Host: "iss", Fragment: "sub"}}, + KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), + ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ + x509.ExtKeyUsageServerAuth, + x509.ExtKeyUsageClientAuth, + }), + PublicKey: priv.Public(), + PublicKeyAlgorithm: x509.Ed25519, + }, false}, {"badSignature", args{crBadSignateure, nil}, nil, true}, {"failTemplate", args{cr, []Option{WithTemplate(`{{ fail "fatal error }}`, CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, + {"missingTemplate", args{cr, []Option{WithTemplateFile("./testdata/missing.tpl", CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, {"badJson", args{cr, []Option{WithTemplate(`"this is not a json object"`, CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, } for _, tt := range tests { diff --git a/x509util/marshal_utils.go b/x509util/marshal_utils.go index fedc0a1c..4408e41b 100644 --- a/x509util/marshal_utils.go +++ b/x509util/marshal_utils.go @@ -40,17 +40,18 @@ func (m *MultiIP) UnmarshalJSON(data []byte) error { if err != nil { return err } - - ips := make([]net.IP, len(ms)) - for i, s := range ms { - ip := net.ParseIP(s) - if ip == nil { - return errors.Errorf("error unmarshaling json: ip %s is not valid", s) + if ms != nil { + ips := make([]net.IP, len(ms)) + for i, s := range ms { + ip := net.ParseIP(s) + if ip == nil { + return errors.Errorf("error unmarshaling json: ip %s is not valid", s) + } + ips[i] = ip } - ips[i] = ip - } - *m = MultiIP(ips) + *m = MultiIP(ips) + } return nil } @@ -58,23 +59,36 @@ func (m *MultiIP) UnmarshalJSON(data []byte) error { // into a []*net.IPNet. type MultiIPNet []*net.IPNet +// MarshalJSON implements the json.Marshaler interface for MultiIPNet. +func (m MultiIPNet) MarshalJSON() ([]byte, error) { + if m == nil { + return []byte("null"), nil + } + ipNets := make([]string, len(m)) + for i, v := range m { + ipNets[i] = v.String() + } + return json.Marshal(ipNets) +} + // UnmarshalJSON implements the json.Unmarshaler interface for MultiIPNet. func (m *MultiIPNet) UnmarshalJSON(data []byte) error { ms, err := unmarshalMultiString(data) if err != nil { return err } - - ipNets := make([]*net.IPNet, len(ms)) - for i, s := range ms { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - return errors.Wrap(err, "error unmarshaling json") + if ms != nil { + ipNets := make([]*net.IPNet, len(ms)) + for i, s := range ms { + _, ipNet, err := net.ParseCIDR(s) + if err != nil { + return errors.Wrap(err, "error unmarshaling json") + } + ipNets[i] = ipNet } - ipNets[i] = ipNet - } - *m = MultiIPNet(ipNets) + *m = MultiIPNet(ipNets) + } return nil } @@ -84,6 +98,9 @@ type MultiURL []*url.URL // MarshalJSON implements the json.Marshaler interface for MultiURL. func (m MultiURL) MarshalJSON() ([]byte, error) { + if m == nil { + return []byte("null"), nil + } urls := make([]string, len(m)) for i, u := range m { urls[i] = u.String() @@ -97,17 +114,18 @@ func (m *MultiURL) UnmarshalJSON(data []byte) error { if err != nil { return err } - - urls := make([]*url.URL, len(ms)) - for i, s := range ms { - u, err := url.Parse(s) - if err != nil { - return errors.Wrap(err, "error unmarshaling json") + if ms != nil { + urls := make([]*url.URL, len(ms)) + for i, s := range ms { + u, err := url.Parse(s) + if err != nil { + return errors.Wrap(err, "error unmarshaling json") + } + urls[i] = u } - urls[i] = u - } - *m = MultiURL(urls) + *m = MultiURL(urls) + } return nil } @@ -115,6 +133,18 @@ func (m *MultiURL) UnmarshalJSON(data []byte) error { // of strings into a []asn1.ObjectIdentifier. type MultiObjectIdentifier []asn1.ObjectIdentifier +// MarshalJSON implements the json.Marshaler interface for MultiObjectIdentifier. +func (m MultiObjectIdentifier) MarshalJSON() ([]byte, error) { + if m == nil { + return []byte("null"), nil + } + oids := make([]string, len(m)) + for i, u := range m { + oids[i] = u.String() + } + return json.Marshal(oids) +} + // UnmarshalJSON implements the json.Unmarshaler interface for // MultiObjectIdentifier. func (m *MultiObjectIdentifier) UnmarshalJSON(data []byte) error { @@ -122,17 +152,18 @@ func (m *MultiObjectIdentifier) UnmarshalJSON(data []byte) error { if err != nil { return err } - - oids := make([]asn1.ObjectIdentifier, len(ms)) - for i, s := range ms { - oid, err := parseObjectIdentifier(s) - if err != nil { - return err + if ms != nil { + oids := make([]asn1.ObjectIdentifier, len(ms)) + for i, s := range ms { + oid, err := parseObjectIdentifier(s) + if err != nil { + return err + } + oids[i] = oid } - oids[i] = oid - } - *m = MultiObjectIdentifier(oids) + *m = MultiObjectIdentifier(oids) + } return nil } diff --git a/x509util/name.go b/x509util/name.go index 2c6e3dfc..abfdf29e 100644 --- a/x509util/name.go +++ b/x509util/name.go @@ -11,15 +11,15 @@ import ( // Name is the JSON representation of X.501 type Name, used in the X.509 subject // and issuer fields. type Name struct { - Country MultiString `json:"country"` - Organization MultiString `json:"organization"` - OrganizationalUnit MultiString `json:"organizationUnit"` - Locality MultiString `json:"locality"` - Province MultiString `json:"province"` - StreetAddress MultiString `json:"streetAddress"` - PostalCode MultiString `json:"postalCode"` - SerialNumber string `json:"serialNumber"` - CommonName string `json:"commonName"` + Country MultiString `json:"country,omitempty"` + Organization MultiString `json:"organization,omitempty"` + OrganizationalUnit MultiString `json:"organizationalUnit,omitempty"` + Locality MultiString `json:"locality,omitempty"` + Province MultiString `json:"province,omitempty"` + StreetAddress MultiString `json:"streetAddress,omitempty"` + PostalCode MultiString `json:"postalCode,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + CommonName string `json:"commonName,omitempty"` } // UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON @@ -74,6 +74,7 @@ func (s Subject) Set(c *x509.Certificate) { // Issuer is the JSON representation of the X.509 issuer field. type Issuer Name +// nolint:unused func newIssuer(n pkix.Name) Issuer { return Issuer{ Country: n.Country, diff --git a/x509util/templates.go b/x509util/templates.go index 12d516e9..7f82d159 100644 --- a/x509util/templates.go +++ b/x509util/templates.go @@ -101,7 +101,7 @@ const DefaultLeafTemplate = `{ // can be provided to force only the verified domains, if the option is true // `.SANs` will be set with the verified domains. const DefaultIIDLeafTemplate = `{ - "subject": {"commonName": "{{ .Insecure.CR.Subject.CommonName }}"}, + "subject": {"commonName":"{{ .Insecure.CR.Subject.CommonName }}"}, {{- if .SANs }} "sans": {{ toJson .SANs }}, {{- else }}