Skip to content

Commit

Permalink
Add extra names to X509 subjects and issuers.
Browse files Browse the repository at this point in the history
  • Loading branch information
maraino committed Mar 31, 2022
1 parent f1cf702 commit fa29792
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 33 deletions.
90 changes: 59 additions & 31 deletions x509util/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package x509util
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"

"github.com/pkg/errors"
Expand All @@ -11,15 +12,31 @@ 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,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"`
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"`
ExtraNames []DistinguishedName `json:"extraNames,omitempty"`
}

func newName(n pkix.Name) Name {
return Name{
Country: n.Country,
Organization: n.Organization,
OrganizationalUnit: n.OrganizationalUnit,
Locality: n.Locality,
Province: n.Province,
StreetAddress: n.StreetAddress,
PostalCode: n.PostalCode,
SerialNumber: n.SerialNumber,
CommonName: n.CommonName,
ExtraNames: newDistinguisedNames(n.ExtraNames),
}
}

// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
Expand All @@ -43,17 +60,7 @@ func (n *Name) UnmarshalJSON(data []byte) error {
type Subject Name

func newSubject(n pkix.Name) Subject {
return Subject{
Country: n.Country,
Organization: n.Organization,
OrganizationalUnit: n.OrganizationalUnit,
Locality: n.Locality,
Province: n.Province,
StreetAddress: n.StreetAddress,
PostalCode: n.PostalCode,
SerialNumber: n.SerialNumber,
CommonName: n.CommonName,
}
return Subject(newName(n))
}

// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
Expand All @@ -79,24 +86,15 @@ func (s Subject) Set(c *x509.Certificate) {
PostalCode: s.PostalCode,
SerialNumber: s.SerialNumber,
CommonName: s.CommonName,
ExtraNames: fromDistinguisedNames(s.ExtraNames),
}
}

// Issuer is the JSON representation of the X.509 issuer field.
type Issuer Name

func newIssuer(n pkix.Name) Issuer {
return Issuer{
Country: n.Country,
Organization: n.Organization,
OrganizationalUnit: n.OrganizationalUnit,
Locality: n.Locality,
Province: n.Province,
StreetAddress: n.StreetAddress,
PostalCode: n.PostalCode,
SerialNumber: n.SerialNumber,
CommonName: n.CommonName,
}
return Issuer(newName(n))
}

// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON
Expand All @@ -122,5 +120,35 @@ func (i Issuer) Set(c *x509.Certificate) {
PostalCode: i.PostalCode,
SerialNumber: i.SerialNumber,
CommonName: i.CommonName,
ExtraNames: fromDistinguisedNames(i.ExtraNames),
}
}

// DistinguishedName mirrors the ASN.1 structure AttributeTypeAndValue in RFC
// 5280, Section 4.1.2.4.
type DistinguishedName struct {
Type ObjectIdentifier `json:"type"`
Value interface{} `json:"value"`
}

func newDistinguisedNames(atvs []pkix.AttributeTypeAndValue) []DistinguishedName {
var extraNames []DistinguishedName
for _, atv := range atvs {
extraNames = append(extraNames, DistinguishedName{
Type: ObjectIdentifier(atv.Type),
Value: atv.Value,
})
}
return extraNames
}

func fromDistinguisedNames(dns []DistinguishedName) []pkix.AttributeTypeAndValue {
var atvs []pkix.AttributeTypeAndValue
for _, dn := range dns {
atvs = append(atvs, pkix.AttributeTypeAndValue{
Type: asn1.ObjectIdentifier(dn.Type),
Value: dn.Value,
})
}
return atvs
}
123 changes: 121 additions & 2 deletions x509util/name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,57 @@ package x509util
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"reflect"
"testing"
)

func Test_newName(t *testing.T) {
type args struct {
n pkix.Name
}
tests := []struct {
name string
args args
want Name
}{
{"ok", args{pkix.Name{
Country: []string{"The country"},
Organization: []string{"The organization"},
OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"},
Locality: []string{"The locality 1", "The locality 2"},
Province: []string{"The province"},
StreetAddress: []string{"The streetAddress"},
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []pkix.AttributeTypeAndValue{
{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}}, Name{
Country: []string{"The country"},
Organization: []string{"The organization"},
OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"},
Locality: []string{"The locality 1", "The locality 2"},
Province: []string{"The province"},
StreetAddress: []string{"The streetAddress"},
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := newName(tt.args.n); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newName() = %v, want %v", got, tt.want)
}
})
}
}

func TestName_UnmarshalJSON(t *testing.T) {
type args struct {
data []byte
Expand All @@ -29,7 +76,8 @@ func TestName_UnmarshalJSON(t *testing.T) {
"streetAddress": "The streetAddress",
"postalCode": "The postalCode",
"serialNumber": "The serialNumber",
"commonName": "The commonName"
"commonName": "The commonName",
"extraNames": [{"type":"1.2.840.113549.1.9.1", "value":"jane@example.com"}]
}`)}, Name{
Country: []string{"The country"},
Organization: []string{"The organization"},
Expand All @@ -40,6 +88,9 @@ func TestName_UnmarshalJSON(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}, false},
{"number", args{[]byte("1234")}, Name{}, true},
{"badJSON", args{[]byte("'badJSON'")}, Name{}, true},
Expand Down Expand Up @@ -76,6 +127,9 @@ func Test_newSubject(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []pkix.AttributeTypeAndValue{
{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}}, Subject{
Country: []string{"The country"},
Organization: []string{"The organization"},
Expand All @@ -86,6 +140,9 @@ func Test_newSubject(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}},
}
for _, tt := range tests {
Expand Down Expand Up @@ -282,7 +339,8 @@ func TestIssuer_UnmarshalJSON(t *testing.T) {
"streetAddress": "The streetAddress",
"postalCode": "The postalCode",
"serialNumber": "The serialNumber",
"commonName": "The commonName"
"commonName": "The commonName",
"extraNames": [{"type":"1.2.840.113549.1.9.1", "value":"jane@example.com"}]
}`)}, Issuer{
Country: []string{"The country"},
Organization: []string{"The organization"},
Expand All @@ -293,6 +351,9 @@ func TestIssuer_UnmarshalJSON(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}, false},
{"number", args{[]byte("1234")}, Issuer{}, true},
{"badJSON", args{[]byte("'badJSON'")}, Issuer{}, true},
Expand Down Expand Up @@ -321,6 +382,7 @@ func TestIssuer_Set(t *testing.T) {
PostalCode MultiString
SerialNumber string
CommonName string
ExtraNames []DistinguishedName
}
type args struct {
c *x509.Certificate
Expand All @@ -341,6 +403,9 @@ func TestIssuer_Set(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
}, args{&x509.Certificate{}}, &x509.Certificate{
Issuer: pkix.Name{
Country: []string{"The country"},
Expand All @@ -352,6 +417,9 @@ func TestIssuer_Set(t *testing.T) {
PostalCode: []string{"The postalCode"},
SerialNumber: "The serialNumber",
CommonName: "The commonName",
ExtraNames: []pkix.AttributeTypeAndValue{
{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
},
},
}},
{"overwrite", fields{
Expand All @@ -374,6 +442,7 @@ func TestIssuer_Set(t *testing.T) {
PostalCode: tt.fields.PostalCode,
SerialNumber: tt.fields.SerialNumber,
CommonName: tt.fields.CommonName,
ExtraNames: tt.fields.ExtraNames,
}
i.Set(tt.args.c)
if !reflect.DeepEqual(tt.args.c, tt.want) {
Expand All @@ -382,3 +451,53 @@ func TestIssuer_Set(t *testing.T) {
})
}
}

func Test_newDistinguisedNames(t *testing.T) {
type args struct {
atvs []pkix.AttributeTypeAndValue
}
tests := []struct {
name string
args args
want []DistinguishedName
}{
{"ok", args{[]pkix.AttributeTypeAndValue{
{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
}}, []DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
}},
{"ok nil", args{nil}, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := newDistinguisedNames(tt.args.atvs); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newDistinguisedNames() = %v, want %v", got, tt.want)
}
})
}
}

func Test_fromDistinguisedNames(t *testing.T) {
type args struct {
dns []DistinguishedName
}
tests := []struct {
name string
args args
want []pkix.AttributeTypeAndValue
}{
{"ok", args{[]DistinguishedName{
{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
}}, []pkix.AttributeTypeAndValue{
{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"},
}},
{"ok nil", args{nil}, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := fromDistinguisedNames(tt.args.dns); !reflect.DeepEqual(got, tt.want) {
t.Errorf("fromDistinguisedNames() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit fa29792

Please sign in to comment.