certinfo.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package certinfo
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "crypto/x509/pkix"
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "net"
  10. "strings"
  11. "time"
  12. "github.com/cloudflare/cfssl/helpers"
  13. )
  14. // Certificate represents a JSON description of an X.509 certificate.
  15. type Certificate struct {
  16. Subject Name `json:"subject,omitempty"`
  17. Issuer Name `json:"issuer,omitempty"`
  18. SerialNumber string `json:"serial_number,omitempty"`
  19. SANs []string `json:"sans,omitempty"`
  20. NotBefore time.Time `json:"not_before"`
  21. NotAfter time.Time `json:"not_after"`
  22. SignatureAlgorithm string `json:"sigalg"`
  23. AKI string `json:"authority_key_id"`
  24. SKI string `json:"subject_key_id"`
  25. RawPEM string `json:"pem"`
  26. }
  27. // Name represents a JSON description of a PKIX Name
  28. type Name struct {
  29. CommonName string `json:"common_name,omitempty"`
  30. SerialNumber string `json:"serial_number,omitempty"`
  31. Country string `json:"country,omitempty"`
  32. Organization string `json:"organization,omitempty"`
  33. OrganizationalUnit string `json:"organizational_unit,omitempty"`
  34. Locality string `json:"locality,omitempty"`
  35. Province string `json:"province,omitempty"`
  36. StreetAddress string `json:"street_address,omitempty"`
  37. PostalCode string `json:"postal_code,omitempty"`
  38. Names []interface{} `json:"names,omitempty"`
  39. //ExtraNames []interface{} `json:"extra_names,omitempty"`
  40. }
  41. // ParseName parses a new name from a *pkix.Name
  42. func ParseName(name pkix.Name) Name {
  43. n := Name{
  44. CommonName: name.CommonName,
  45. SerialNumber: name.SerialNumber,
  46. Country: strings.Join(name.Country, ","),
  47. Organization: strings.Join(name.Organization, ","),
  48. OrganizationalUnit: strings.Join(name.OrganizationalUnit, ","),
  49. Locality: strings.Join(name.Locality, ","),
  50. Province: strings.Join(name.Province, ","),
  51. StreetAddress: strings.Join(name.StreetAddress, ","),
  52. PostalCode: strings.Join(name.PostalCode, ","),
  53. }
  54. for i := range name.Names {
  55. n.Names = append(n.Names, name.Names[i].Value)
  56. }
  57. // ExtraNames aren't supported in Go 1.4
  58. // for i := range name.ExtraNames {
  59. // n.ExtraNames = append(n.ExtraNames, name.ExtraNames[i].Value)
  60. // }
  61. return n
  62. }
  63. func formatKeyID(id []byte) string {
  64. var s string
  65. for i, c := range id {
  66. if i > 0 {
  67. s += ":"
  68. }
  69. s += fmt.Sprintf("%02X", c)
  70. }
  71. return s
  72. }
  73. // ParseCertificate parses an x509 certificate.
  74. func ParseCertificate(cert *x509.Certificate) *Certificate {
  75. c := &Certificate{
  76. RawPEM: string(helpers.EncodeCertificatePEM(cert)),
  77. SignatureAlgorithm: helpers.SignatureString(cert.SignatureAlgorithm),
  78. NotBefore: cert.NotBefore,
  79. NotAfter: cert.NotAfter,
  80. Subject: ParseName(cert.Subject),
  81. Issuer: ParseName(cert.Issuer),
  82. SANs: cert.DNSNames,
  83. AKI: formatKeyID(cert.AuthorityKeyId),
  84. SKI: formatKeyID(cert.SubjectKeyId),
  85. SerialNumber: cert.SerialNumber.String(),
  86. }
  87. for _, ip := range cert.IPAddresses {
  88. c.SANs = append(c.SANs, ip.String())
  89. }
  90. return c
  91. }
  92. // ParseCertificateFile parses x509 certificate file.
  93. func ParseCertificateFile(certFile string) (*Certificate, error) {
  94. certPEM, err := ioutil.ReadFile(certFile)
  95. if err != nil {
  96. return nil, err
  97. }
  98. return ParseCertificatePEM(certPEM)
  99. }
  100. // ParseCertificatePEM parses an x509 certificate PEM.
  101. func ParseCertificatePEM(certPEM []byte) (*Certificate, error) {
  102. cert, err := helpers.ParseCertificatePEM(certPEM)
  103. if err != nil {
  104. return nil, err
  105. }
  106. return ParseCertificate(cert), nil
  107. }
  108. // ParseCSRPEM uses the helper to parse an x509 CSR PEM.
  109. func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
  110. csrObject, err := helpers.ParseCSRPEM(csrPEM)
  111. if err != nil {
  112. return nil, err
  113. }
  114. return csrObject, nil
  115. }
  116. // ParseCSRFile uses the helper to parse an x509 CSR PEM file.
  117. func ParseCSRFile(csrFile string) (*x509.CertificateRequest, error) {
  118. csrPEM, err := ioutil.ReadFile(csrFile)
  119. if err != nil {
  120. return nil, err
  121. }
  122. return ParseCSRPEM(csrPEM)
  123. }
  124. // ParseCertificateDomain parses the certificate served by the given domain.
  125. func ParseCertificateDomain(domain string) (cert *Certificate, err error) {
  126. var host, port string
  127. if host, port, err = net.SplitHostPort(domain); err != nil {
  128. host = domain
  129. port = "443"
  130. }
  131. var conn *tls.Conn
  132. conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 10 * time.Second}, "tcp", net.JoinHostPort(host, port), &tls.Config{InsecureSkipVerify: true})
  133. if err != nil {
  134. return
  135. }
  136. defer conn.Close()
  137. if len(conn.ConnectionState().PeerCertificates) == 0 {
  138. return nil, errors.New("received no server certificates")
  139. }
  140. cert = ParseCertificate(conn.ConnectionState().PeerCertificates[0])
  141. return
  142. }