Голанг тема дн от x509 cert
Есть ли простой способ получить полный DN субъекта (или DN эмитента) из сертификата x509 в виде строки?
Я не смог найти какие-либо методы, такие как ".String()" в pkix.Name
5 ответов
Решение (спасибо коллеге):
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
}
вызов функции:
subj, err := getDNFromCert(x509Cert.Subject, "/")
if err != nil {
// do error handling
}
fmt.Println(subj)
вывод (пример):
/C=US/O=some organization/OU=unit/CN=common name
кажется, это единственное "простое" решение
Чтобы получить полный DN субъекта (или DN эмитента) из сертификата x509, вы можете использовать следующий код:
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()
Точно так же, если вам нужно получить только определенное значение объекта от субъекта (или эмитента), вы можете использовать следующий подход. Пример ниже извлекает UID из субъекта (который не определен в 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)
ОБНОВЛЕНИЕ: Упрощенный способ получения UID благодаря @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)
Я столкнулся с той же задачей сегодня. Вы можете получить предмет из сертификата следующим образом:
// 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
Вот функция, которую я использовал для непосредственного анализа строки, без использования каких-либо внутренних библиотек go.
func parseIssuerDn(issuer string) map[string]string {
trackerResultMap := map[string]string{"C=": "", "O=": "", "CN=": "", "ST=": "", "L=": "", "OU=": ""}
for tracker, _ := range trackerResultMap {
index := strings.Index(issuer, tracker)
if index < 0 {
continue
}
var res string
// track quotes for delimited fields so we know not to split on the comma
quoteCount := 0
for i := index + len(tracker); i < len(issuer); i++ {
char := issuer[i]
// if ", we need to count and delimit
if char == 34 {
quoteCount++
if quoteCount == 2 {
break
} else {
continue
}
}
// comma, lets stop here but only if we don't have quotes
if char == 44 && quoteCount == 0 {
break
}
// add this individual char
res += string(rune(char))
}
trackerResultMap[strings.TrimSuffix(tracker, "=")] = strings.TrimPrefix(res, "=")
}
for k, v := range trackerResultMap {
if len(v) == 0 {
delete(trackerResultMap, k)
}
}
return trackerResultMap
}
Вот функция, которую я использую.
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()
}
Это код для получения RDNSequence. Пример получает имя субъекта.
...
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))
...
Используя напрямую
fmt.Sprintf("%+v", cert.Subject.ToRDNSequence())
выполняет работу в го 1.9. Для улучшенных версий go (>=1.10) он работает с ".String()" в pkix.Name
На данный момент вы можете просто использовать:
cert, _ := x509.ParseCertificate(certDERBlock.Bytes)
fmt.Println(cert.Subject.CommonName)
https://golang.org/pkg/crypto/x509/
https://golang.org/pkg/crypto/x509/pkix/