Commit 0b37f05d authored by Adam Langley's avatar Adam Langley

crypto/x509: follow OpenSSL and emit Extension structures directly in CSRs.

I don't know if I got lost in the old PKCS documents, or whether this is
a case where reality diverges from the spec, but OpenSSL clearly stuffs
PKIX Extension objects in CSR attributues directly[1].

In either case, doing what OpenSSL does seems valid here and allows the
critical flag in extensions to be serialised.

Fixes #13739.

[1] https://github.com/openssl/openssl/blob/e3713c365c2657236439fea00822a43aa396d112/crypto/x509/x509_req.c#L173

Change-Id: Ic1e73ba9bd383a357a2aa8fc4f6bd76811bbefcc
Reviewed-on: https://go-review.googlesource.com/70851
Run-TryBot: Adam Langley <agl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarFilippo Valsorda <filippo@golang.org>
parent c529141d
...@@ -2317,7 +2317,7 @@ func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawVal ...@@ -2317,7 +2317,7 @@ func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawVal
return rawAttributes, nil return rawAttributes, nil
} }
// parseRawAttributes Unmarshals RawAttributes intos AttributeTypeAndValueSETs. // parseRawAttributes Unmarshals RawAttributes into AttributeTypeAndValueSETs.
func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET { func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET {
var attributes []pkix.AttributeTypeAndValueSET var attributes []pkix.AttributeTypeAndValueSET
for _, rawAttr := range rawAttributes { for _, rawAttr := range rawAttributes {
...@@ -2410,27 +2410,38 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv ...@@ -2410,27 +2410,38 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
extensions = append(extensions, template.ExtraExtensions...) extensions = append(extensions, template.ExtraExtensions...)
var attributes []pkix.AttributeTypeAndValueSET // Make a copy of template.Attributes because we may alter it below.
attributes = append(attributes, template.Attributes...) attributes := make([]pkix.AttributeTypeAndValueSET, 0, len(template.Attributes))
for _, attr := range template.Attributes {
values := make([][]pkix.AttributeTypeAndValue, len(attr.Value))
copy(values, attr.Value)
attributes = append(attributes, pkix.AttributeTypeAndValueSET{
Type: attr.Type,
Value: values,
})
}
extensionsAppended := false
if len(extensions) > 0 { if len(extensions) > 0 {
// Append the extensions to an existing attribute if possible.
for _, atvSet := range attributes {
if !atvSet.Type.Equal(oidExtensionRequest) || len(atvSet.Value) == 0 {
continue
}
// specifiedExtensions contains all the extensions that we // specifiedExtensions contains all the extensions that we
// found specified via template.Attributes. // found specified via template.Attributes.
specifiedExtensions := make(map[string]bool) specifiedExtensions := make(map[string]bool)
for _, atvSet := range template.Attributes {
if !atvSet.Type.Equal(oidExtensionRequest) {
continue
}
for _, atvs := range atvSet.Value { for _, atvs := range atvSet.Value {
for _, atv := range atvs { for _, atv := range atvs {
specifiedExtensions[atv.Type.String()] = true specifiedExtensions[atv.Type.String()] = true
} }
} }
}
atvs := make([]pkix.AttributeTypeAndValue, 0, len(extensions)) newValue := make([]pkix.AttributeTypeAndValue, 0, len(atvSet.Value[0])+len(extensions))
newValue = append(newValue, atvSet.Value[0]...)
for _, e := range extensions { for _, e := range extensions {
if specifiedExtensions[e.Id.String()] { if specifiedExtensions[e.Id.String()] {
// Attributes already contained a value for // Attributes already contained a value for
...@@ -2438,47 +2449,55 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv ...@@ -2438,47 +2449,55 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
continue continue
} }
atvs = append(atvs, pkix.AttributeTypeAndValue{ newValue = append(newValue, pkix.AttributeTypeAndValue{
// There is no place for the critical flag in a CSR. // There is no place for the critical
// flag in an AttributeTypeAndValue.
Type: e.Id, Type: e.Id,
Value: e.Value, Value: e.Value,
}) })
} }
// Append the extensions to an existing attribute if possible. atvSet.Value[0] = newValue
appended := false extensionsAppended = true
for _, atvSet := range attributes { break
if !atvSet.Type.Equal(oidExtensionRequest) || len(atvSet.Value) == 0 { }
continue
} }
atvSet.Value[0] = append(atvSet.Value[0], atvs...) rawAttributes, err := newRawAttributes(attributes)
appended = true if err != nil {
break return
} }
// Otherwise, add a new attribute for the extensions. // If not included in attributes, add a new attribute for the
if !appended { // extensions.
attributes = append(attributes, pkix.AttributeTypeAndValueSET{ if len(extensions) > 0 && !extensionsAppended {
attr := struct {
Type asn1.ObjectIdentifier
Value [][]pkix.Extension `asn1:"set"`
}{
Type: oidExtensionRequest, Type: oidExtensionRequest,
Value: [][]pkix.AttributeTypeAndValue{ Value: [][]pkix.Extension{extensions},
atvs,
},
})
} }
b, err := asn1.Marshal(attr)
if err != nil {
return nil, errors.New("x509: failed to serialise extensions attribute: " + err.Error())
}
var rawValue asn1.RawValue
if _, err := asn1.Unmarshal(b, &rawValue); err != nil {
return nil, err
}
rawAttributes = append(rawAttributes, rawValue)
} }
asn1Subject := template.RawSubject asn1Subject := template.RawSubject
if len(asn1Subject) == 0 { if len(asn1Subject) == 0 {
asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence()) asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence())
if err != nil { if err != nil {
return return nil, err
}
} }
rawAttributes, err := newRawAttributes(attributes)
if err != nil {
return
} }
tbsCSR := tbsCertificateRequest{ tbsCSR := tbsCertificateRequest{
......
...@@ -1242,6 +1242,7 @@ func TestCertificateRequestOverrides(t *testing.T) { ...@@ -1242,6 +1242,7 @@ func TestCertificateRequestOverrides(t *testing.T) {
{ {
Id: oidExtensionSubjectAltName, Id: oidExtensionSubjectAltName,
Value: sanContents, Value: sanContents,
Critical: true,
}, },
}, },
} }
...@@ -1252,6 +1253,10 @@ func TestCertificateRequestOverrides(t *testing.T) { ...@@ -1252,6 +1253,10 @@ func TestCertificateRequestOverrides(t *testing.T) {
t.Errorf("Extension did not override template. Got %v\n", csr.DNSNames) t.Errorf("Extension did not override template. Got %v\n", csr.DNSNames)
} }
if len(csr.Extensions) != 1 || !csr.Extensions[0].Id.Equal(oidExtensionSubjectAltName) || !csr.Extensions[0].Critical {
t.Errorf("SAN extension was not faithfully copied, got %#v", csr.Extensions)
}
// If there is already an attribute with X.509 extensions then the // If there is already an attribute with X.509 extensions then the
// extra extensions should be added to it rather than creating a CSR // extra extensions should be added to it rather than creating a CSR
// with two extension attributes. // with two extension attributes.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment