问题
Is there any easy way to get the complete subject DN (or issuer DN) from an x509 certificate in go as a string?
I was not able to find any methods like ".String()" in pkix.Name
回答1:
Solution (thanks to a colleague):
var oid = map[string]string{
"2.5.4.3": "CN",
"2.5.4.4": "SN",
"2.5.4.5": "serialNumber",
"2.5.4.6": "C",
"2.5.4.7": "L",
"2.5.4.8": "ST",
"2.5.4.9": "streetAddress",
"2.5.4.10": "O",
"2.5.4.11": "OU",
"2.5.4.12": "title",
"2.5.4.17": "postalCode",
"2.5.4.42": "GN",
"2.5.4.43": "initials",
"2.5.4.44": "generationQualifier",
"2.5.4.46": "dnQualifier",
"2.5.4.65": "pseudonym",
"0.9.2342.19200300.100.1.25": "DC",
"1.2.840.113549.1.9.1": "emailAddress",
"0.9.2342.19200300.100.1.1": "userid",
}
func getDNFromCert(namespace pkix.Name, sep string) (string, error) {
subject := []string{}
for _, s := range namespace.ToRDNSequence() {
for _, i := range s {
if v, ok := i.Value.(string); ok {
if name, ok := oid[i.Type.String()]; ok {
// <oid name>=<value>
subject = append(subject, fmt.Sprintf("%s=%s", name, v))
} else {
// <oid>=<value> if no <oid name> is found
subject = append(subject, fmt.Sprintf("%s=%s", i.Type.String(), v))
}
} else {
// <oid>=<value in default format> if value is not string
subject = append(subject, fmt.Sprintf("%s=%v", i.Type.String, v))
}
}
}
return sep + strings.Join(subject, sep), nil
}
calling the function:
subj, err := getDNFromCert(x509Cert.Subject, "/")
if err != nil {
// do error handling
}
fmt.Println(subj)
output (example):
/C=US/O=some organization/OU=unit/CN=common name
this seems to be the only "easy" solution
回答2:
I faced the same task today. You could get subject from certificate this way:
// d is []byte with your certificate
cert, err := x509.ParseCertificate(d)
fmt.Printf("%+v\n", cert.Subject.ToRDNSequence())
// Output: CN=client1,OU=MyClients,O=MongoDB-Cluster,L=Austin,ST=TX,C=US
回答3:
In order to get complete subject DN (or issuer DN) from an x509 certificate, you may use next code:
cert, err := x509.ParseCertificate(certData)
if err != nil {
return err
}
var subject pkix.RDNSequence
if _, err := asn1.Unmarshal(cert.RawSubject, &subject); err != nil {
return err
}
fmt.Plrintln(subject.String()
Similarly, if you need to get only some specific object value from the subject (or issuer) you may use next approach. Example below retrieves UID from subject (which is not defined in the stdlib https://github.com/golang/go/issues/25667)
// http://www.alvestrand.no/objectid/0.9.2342.19200300.100.1.1.html
const oidUserID = "0.9.2342.19200300.100.1.1"
var UID string
cert, err := x509.ParseCertificate(certData)
if err != nil {
return err
}
// manually parsing the Certificate subject to get the
// UID field, which is being ignored by the stdlib
// https://github.com/golang/go/issues/25667
var subject pkix.RDNSequence
if _, err := asn1.Unmarshal(cert.RawSubject, &subject); err != nil {
return err
}
for _, s := range subject {
for _, i := range s {
if i.Type.String() == oidUserID {
if v, ok := i.Value.(string); ok {
UID = v
}
}
}
}
fmt.Println(UID)
UPDATE: Simplified way to get the UID, thanks to @FiloSottile:
// http://www.alvestrand.no/objectid/0.9.2342.19200300.100.1.1.html
var oidUserID = []int{0, 9, 2342, 19200300, 100, 1, 1}
var UID string
cert, err := x509.ParseCertificate(certData)
if err != nil {
return err
}
// reading the UID from list of unprased
// objects from Subject
for _, n := range cert.Subject.Names {
if n.Type.Equal(oidUserID) {
if v, ok := n.Value.(string); ok {
UID = v
}
}
}
fmt.Println(UID)
回答4:
Using directly
fmt.Sprintf("%+v", cert.Subject.ToRDNSequence())
does the work in go 1.9. For superior versions of go (>=1.10), it works with ".String()" in pkix.Name
回答5:
For now you can just use:
cert, _ := x509.ParseCertificate(certDERBlock.Bytes)
fmt.Println(cert.Subject.CommonName)
https://golang.org/pkg/crypto/x509/#Certificate
https://golang.org/pkg/crypto/x509/pkix/#Name
回答6:
Here is the function that I use.
var (
cnNameOid = asn1.ObjectIdentifier{2, 5, 4, 3}
emailOid = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}
userIDOid = asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}
dcNameOid = asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}
)
// RDNSToString returns the Relative Distinguish Name as a string.
func RDNSToString(rdns *pkix.RDNSequence) string {
var buf strings.Builder
for _, rdn := range *rdns {
if len(rdn) == 0 {
continue
}
for _, atv := range rdn {
value, ok := atv.Value.(string)
if !ok {
continue
}
t := atv.Type
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
switch t[3] {
case 3:
buf.WriteString("/CN=") // common name
buf.WriteString(value)
case 4:
buf.WriteString("/SN=") // surname
buf.WriteString(value)
case 5:
buf.WriteString("/SERIALNUMBER=")
buf.WriteString(value)
case 6:
buf.WriteString("/C=") // country
buf.WriteString(value)
case 7:
buf.WriteString("/L=") // locality
buf.WriteString(value)
case 8:
buf.WriteString("/ST=") // state
buf.WriteString(value)
case 9:
buf.WriteString("/STREET=")
buf.WriteString(value)
case 10:
buf.WriteString("/O=") // organization
buf.WriteString(value)
case 11:
buf.WriteString("/OU=") // organization unit
buf.WriteString(value)
case 12:
buf.WriteString("/T=") // title
buf.WriteString(value)
case 17:
buf.WriteString("/PC=") // postal code
buf.WriteString(value)
case 42:
buf.WriteString("/GN=") // given name
buf.WriteString(value)
case 43:
buf.WriteString("/initials=")
buf.WriteString(value)
case 44:
buf.WriteString("/generationQualifier=")
buf.WriteString(value)
case 46:
buf.WriteString("/dnQualifier=")
buf.WriteString(value)
case 65:
buf.WriteString("/pseudonym=")
buf.WriteString(value)
}
} else if t.Equal(dcNameOid) {
buf.WriteString("/DC=") // domain component
buf.WriteString(value)
} else if t.Equal(emailOid) {
buf.WriteString("/MAIL=")
buf.WriteString(value)
} else if t.Equal(userIDOid) {
buf.WriteString("/UID=") // user ID
buf.WriteString(value)
}
}
}
return buf.String()
}
This is the code to get the RDNSequence. The example gets the Subject name.
...
cert, err := x509.ParseCertificate(certData)
if err != nil {
...
}
var rdns pkix.RDNSequence
if _, err := asn1.Unmarshal(cert.RawSubject, &rdns); err != nil {
...
}
fmt.Println("Subject:", RDNSToString(&rdns))
...
来源:https://stackoverflow.com/questions/39125873/golang-subject-dn-from-x509-cert