From 005d3092f950cd1dc630323ae44dc30a1e173908 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 16 May 2022 13:12:38 -0700 Subject: [PATCH 1/5] Populate ExtraNames from Names This commit will populate the subject ExtraNames in a certificate request from the information present in Names as long as it is not one of the default fields. This way, we can extract non-default fields as well as populate them in the final certificate if necessary. It also uses the proper type for the deprecated email address attribute in the subject. Related to smallstep/certificates#916 --- x509util/extensions.go | 1 - x509util/name.go | 56 +++++++++++++++++++++++++++++++++++------- x509util/name_test.go | 28 +++++++++++++-------- 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/x509util/extensions.go b/x509util/extensions.go index b0d6f5b7..94ceb7bf 100644 --- a/x509util/extensions.go +++ b/x509util/extensions.go @@ -84,7 +84,6 @@ func newExtensions(extensions []pkix.Extension) []Extension { ret[i] = newExtension(e) } return ret - } // Set adds the extension to the given X509 certificate. diff --git a/x509util/name.go b/x509util/name.go index 190fa699..a0c873c9 100644 --- a/x509util/name.go +++ b/x509util/name.go @@ -9,6 +9,24 @@ import ( "github.com/pkg/errors" ) +// attributeTypeNames are the subject attributes managed by Go and this package. +// newDistinguisedNames will populate .Insecure.CR.ExtraNames with the +// attributes not present on this map. +var attributeTypeNames = map[string]string{ + "2.5.4.6": "C", + "2.5.4.10": "O", + "2.5.4.11": "OU", + "2.5.4.3": "CN", + "2.5.4.5": "SERIALNUMBER", + "2.5.4.7": "L", + "2.5.4.8": "ST", + "2.5.4.9": "STREET", + "2.5.4.17": "POSTALCODE", +} + +// oidEmailAddress is the oid of the deprecated emailAddress in the subject. +var oidEmailAddress = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1} + // Name is the JSON representation of X.501 type Name, used in the X.509 subject // and issuer fields. type Name struct { @@ -35,7 +53,7 @@ func newName(n pkix.Name) Name { PostalCode: n.PostalCode, SerialNumber: n.SerialNumber, CommonName: n.CommonName, - ExtraNames: newDistinguisedNames(n.ExtraNames), + ExtraNames: newDistinguisedNames(n.Names), } } @@ -131,24 +149,44 @@ type DistinguishedName struct { Value interface{} `json:"value"` } +// newDistinguisedNames returns a list of DistinguishedName with the attributes not +// present in attributeTypeNames. func newDistinguisedNames(atvs []pkix.AttributeTypeAndValue) []DistinguishedName { var extraNames []DistinguishedName for _, atv := range atvs { - extraNames = append(extraNames, DistinguishedName{ - Type: ObjectIdentifier(atv.Type), - Value: atv.Value, - }) + if _, ok := attributeTypeNames[atv.Type.String()]; !ok { + extraNames = append(extraNames, DistinguishedName{ + Type: ObjectIdentifier(atv.Type), + Value: atv.Value, + }) + } } return extraNames } +// fromDistinguisedNames converts a list of DistinguisedName to +// []pkix.AttributeTypeAndValue. Note that this method has a special case to +// encode the emailAddress deprecated field (1.2.840.113549.1.9.1). 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, - }) + typ := asn1.ObjectIdentifier(dn.Type) + v, isString := dn.Value.(string) + if typ.Equal(oidEmailAddress) && isString { + atvs = append(atvs, pkix.AttributeTypeAndValue{ + Type: typ, + Value: asn1.RawValue{ + Class: asn1.ClassUniversal, + Tag: asn1.TagIA5String, + Bytes: []byte(v), + }, + }) + } else { + atvs = append(atvs, pkix.AttributeTypeAndValue{ + Type: typ, + Value: dn.Value, + }) + } } return atvs } diff --git a/x509util/name_test.go b/x509util/name_test.go index 2fc1b9a3..7a0c2976 100644 --- a/x509util/name_test.go +++ b/x509util/name_test.go @@ -27,8 +27,9 @@ func Test_newName(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"}, + Names: []pkix.AttributeTypeAndValue{ + {Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "The commonName"}, + {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, }, }}, Name{ Country: []string{"The country"}, @@ -41,7 +42,7 @@ func Test_newName(t *testing.T) { SerialNumber: "The serialNumber", CommonName: "The commonName", ExtraNames: []DistinguishedName{ - {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, }, }}, } @@ -127,8 +128,8 @@ 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"}, + Names: []pkix.AttributeTypeAndValue{ + {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, }, }}, Subject{ Country: []string{"The country"}, @@ -141,7 +142,7 @@ func Test_newSubject(t *testing.T) { SerialNumber: "The serialNumber", CommonName: "The commonName", ExtraNames: []DistinguishedName{ - {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, }, }}, } @@ -405,6 +406,7 @@ func TestIssuer_Set(t *testing.T) { CommonName: "The commonName", ExtraNames: []DistinguishedName{ {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }, }, args{&x509.Certificate{}}, &x509.Certificate{ Issuer: pkix.Name{ @@ -418,7 +420,8 @@ func TestIssuer_Set(t *testing.T) { SerialNumber: "The serialNumber", CommonName: "The commonName", ExtraNames: []pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, + {Type: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }, }, }}, @@ -462,9 +465,12 @@ func Test_newDistinguisedNames(t *testing.T) { want []DistinguishedName }{ {"ok", args{[]pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "The commonName"}, + {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, + {Type: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }}, []DistinguishedName{ - {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, + {Type: ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }}, {"ok nil", args{nil}, nil}, } @@ -488,8 +494,10 @@ func Test_fromDistinguisedNames(t *testing.T) { }{ {"ok", args{[]DistinguishedName{ {Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }}, []pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "jane@example.com"}, + {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, + {Type: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: "custom@example.com"}, }}, {"ok nil", args{nil}, nil}, } From c93d3e3ff5826803c1e528e1daa23e3dad85f53e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 May 2022 11:19:20 -0700 Subject: [PATCH 2/5] Rename newDistinguisedNames to newExtraNames --- x509util/name.go | 6 +++--- x509util/name_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x509util/name.go b/x509util/name.go index a0c873c9..1b1b0948 100644 --- a/x509util/name.go +++ b/x509util/name.go @@ -53,7 +53,7 @@ func newName(n pkix.Name) Name { PostalCode: n.PostalCode, SerialNumber: n.SerialNumber, CommonName: n.CommonName, - ExtraNames: newDistinguisedNames(n.Names), + ExtraNames: newExtraNames(n.Names), } } @@ -149,9 +149,9 @@ type DistinguishedName struct { Value interface{} `json:"value"` } -// newDistinguisedNames returns a list of DistinguishedName with the attributes not +// newExtraNames returns a list of DistinguishedName with the attributes not // present in attributeTypeNames. -func newDistinguisedNames(atvs []pkix.AttributeTypeAndValue) []DistinguishedName { +func newExtraNames(atvs []pkix.AttributeTypeAndValue) []DistinguishedName { var extraNames []DistinguishedName for _, atv := range atvs { if _, ok := attributeTypeNames[atv.Type.String()]; !ok { diff --git a/x509util/name_test.go b/x509util/name_test.go index 7a0c2976..6a4dc0ba 100644 --- a/x509util/name_test.go +++ b/x509util/name_test.go @@ -455,7 +455,7 @@ func TestIssuer_Set(t *testing.T) { } } -func Test_newDistinguisedNames(t *testing.T) { +func Test_newExtraNames(t *testing.T) { type args struct { atvs []pkix.AttributeTypeAndValue } @@ -476,7 +476,7 @@ func Test_newDistinguisedNames(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := newDistinguisedNames(tt.args.atvs); !reflect.DeepEqual(got, tt.want) { + if got := newExtraNames(tt.args.atvs); !reflect.DeepEqual(got, tt.want) { t.Errorf("newDistinguisedNames() = %v, want %v", got, tt.want) } }) From 4434f233470e5eeba2f79a6f988d035987e79f90 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 May 2022 11:24:20 -0700 Subject: [PATCH 3/5] Add all names in test case --- x509util/name_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/x509util/name_test.go b/x509util/name_test.go index 6a4dc0ba..077efaf7 100644 --- a/x509util/name_test.go +++ b/x509util/name_test.go @@ -28,7 +28,17 @@ func Test_newName(t *testing.T) { SerialNumber: "The serialNumber", CommonName: "The commonName", Names: []pkix.AttributeTypeAndValue{ + {Type: asn1.ObjectIdentifier{2, 5, 4, 6}, Value: "The country"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 10}, Value: "The organization"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "The organizationalUnit 1"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 11}, Value: "The organizationalUnit 2"}, {Type: asn1.ObjectIdentifier{2, 5, 4, 3}, Value: "The commonName"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 5}, Value: "The serialNumber"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 7}, Value: "The locality 1"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 7}, Value: "The locality 2"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 8}, Value: "The province"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 9}, Value: "The streetAddress"}, + {Type: asn1.ObjectIdentifier{2, 5, 4, 17}, Value: "The postalCode"}, {Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: asn1.RawValue{Class: asn1.ClassUniversal, Tag: asn1.TagIA5String, Bytes: []byte("jane@example.com")}}, }, }}, Name{ From 02ebccea0e9058103d757fcc7a68dd5274ff7b5e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 May 2022 11:25:40 -0700 Subject: [PATCH 4/5] Fix comment --- x509util/name.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x509util/name.go b/x509util/name.go index 1b1b0948..d1da6eb7 100644 --- a/x509util/name.go +++ b/x509util/name.go @@ -10,8 +10,8 @@ import ( ) // attributeTypeNames are the subject attributes managed by Go and this package. -// newDistinguisedNames will populate .Insecure.CR.ExtraNames with the -// attributes not present on this map. +// newExtraNames will populate .Insecure.CR.ExtraNames with the attributes not +// present on this map. var attributeTypeNames = map[string]string{ "2.5.4.6": "C", "2.5.4.10": "O", From 487ffed70de0a23f5b2a74b7287ff120b2cccf30 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 17 May 2022 14:29:39 -0700 Subject: [PATCH 5/5] Fix typo in function name. --- x509util/name.go | 10 +++++----- x509util/name_test.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x509util/name.go b/x509util/name.go index d1da6eb7..e0cde298 100644 --- a/x509util/name.go +++ b/x509util/name.go @@ -104,7 +104,7 @@ func (s Subject) Set(c *x509.Certificate) { PostalCode: s.PostalCode, SerialNumber: s.SerialNumber, CommonName: s.CommonName, - ExtraNames: fromDistinguisedNames(s.ExtraNames), + ExtraNames: fromDistinguishedNames(s.ExtraNames), } } @@ -138,7 +138,7 @@ func (i Issuer) Set(c *x509.Certificate) { PostalCode: i.PostalCode, SerialNumber: i.SerialNumber, CommonName: i.CommonName, - ExtraNames: fromDistinguisedNames(i.ExtraNames), + ExtraNames: fromDistinguishedNames(i.ExtraNames), } } @@ -164,10 +164,10 @@ func newExtraNames(atvs []pkix.AttributeTypeAndValue) []DistinguishedName { return extraNames } -// fromDistinguisedNames converts a list of DistinguisedName to +// fromDistinguishedNames converts a list of DistinguishedName to // []pkix.AttributeTypeAndValue. Note that this method has a special case to -// encode the emailAddress deprecated field (1.2.840.113549.1.9.1). -func fromDistinguisedNames(dns []DistinguishedName) []pkix.AttributeTypeAndValue { +// encode the deprecated emailAddress field (1.2.840.113549.1.9.1). +func fromDistinguishedNames(dns []DistinguishedName) []pkix.AttributeTypeAndValue { var atvs []pkix.AttributeTypeAndValue for _, dn := range dns { typ := asn1.ObjectIdentifier(dn.Type) diff --git a/x509util/name_test.go b/x509util/name_test.go index 077efaf7..6c017b6e 100644 --- a/x509util/name_test.go +++ b/x509util/name_test.go @@ -513,7 +513,7 @@ func Test_fromDistinguisedNames(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := fromDistinguisedNames(tt.args.dns); !reflect.DeepEqual(got, tt.want) { + if got := fromDistinguishedNames(tt.args.dns); !reflect.DeepEqual(got, tt.want) { t.Errorf("fromDistinguisedNames() = %v, want %v", got, tt.want) } })