【问题标题】:golang subject dn from x509 cert来自 x509 证书的 golang 主题 dn
【发布时间】:2016-12-31 17:46:00
【问题描述】:

是否有任何简单的方法可以从 x509 证书中以字符串形式获取完整的主题 DN(或颁发者 DN)?

我在 pkix.Name 中找不到像“.String()”这样的方法

【问题讨论】:

    标签: go x509


    【解决方案1】:

    这是我最终用来直接解析字符串的函数,而不使用任何 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
    }
    

    【讨论】:

      【解决方案2】:

      这是我使用的功能。

      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()
      }
      

      这是获取 RDNS 序列的代码。该示例获取主题名称。

      ...
      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))
      ...
      

      【讨论】:

        【解决方案3】:

        现在你可以使用:

        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

        【讨论】:

          【解决方案4】:

          直接使用 fmt.Sprintf("%+v", cert.Subject.ToRDNSequence())

          在 go 1.9 中完成这项工作。 对于 go (>=1.10) 的高级版本,它适用于 pkix.Name 中的“.String()”

          【讨论】:

            【解决方案5】:

            为了从 x509 证书中获取完整的主题 DN(或颁发者 DN),您可以使用以下代码:

            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(未在标准库 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)
            

            【讨论】:

              【解决方案6】:

              我今天面临同样的任务。您可以通过这种方式从证书中获取主题:

              // 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
              

              【讨论】:

                【解决方案7】:

                解决方案(感谢一位同事):

                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
                

                这似乎是唯一“简单”的解决方案

                【讨论】:

                • 这段代码不起作用,因为namespace.ToRDNSequence()不会重新创建证书的原始RDNS序列,它将添加仅在stdlib中定义的对象,因此将丢失那些未定义的对象.请参阅下面我的答案中的工作代码示例。
                猜你喜欢
                • 1970-01-01
                • 2023-03-12
                • 2012-07-16
                • 1970-01-01
                • 1970-01-01
                • 2019-04-02
                • 2017-01-15
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多