bundle.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package bundler
  2. import (
  3. "bytes"
  4. "crypto/ecdsa"
  5. "crypto/rsa"
  6. "crypto/x509"
  7. "crypto/x509/pkix"
  8. "encoding/json"
  9. "encoding/pem"
  10. "errors"
  11. "fmt"
  12. "time"
  13. "github.com/cloudflare/cfssl/helpers"
  14. )
  15. // A Bundle contains a certificate and its trust chain. It is intended
  16. // to store the most widely applicable chain, with shortness an
  17. // explicit goal.
  18. type Bundle struct {
  19. Chain []*x509.Certificate
  20. Cert *x509.Certificate
  21. Root *x509.Certificate
  22. Key interface{}
  23. Issuer *pkix.Name
  24. Subject *pkix.Name
  25. Expires time.Time
  26. LeafExpires time.Time
  27. Hostnames []string
  28. Status *BundleStatus
  29. }
  30. // BundleStatus is designated for various status reporting.
  31. type BundleStatus struct {
  32. // A flag on whether a new bundle is generated
  33. IsRebundled bool `json:"rebundled"`
  34. // A list of SKIs of expiring certificates
  35. ExpiringSKIs []string `json:"expiring_SKIs"`
  36. // A list of untrusted root store names
  37. Untrusted []string `json:"untrusted_root_stores"`
  38. // A list of human readable warning messages based on the bundle status.
  39. Messages []string `json:"messages"`
  40. // A status code consists of binary flags
  41. Code int `json:"code"`
  42. }
  43. type chain []*x509.Certificate
  44. func (c chain) MarshalJSON() ([]byte, error) {
  45. var buf bytes.Buffer
  46. for _, cert := range c {
  47. buf.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
  48. }
  49. ret := bytes.TrimSpace(buf.Bytes())
  50. return json.Marshal(string(ret))
  51. }
  52. // PemBlockToString turns a pem.Block into the string encoded form.
  53. func PemBlockToString(block *pem.Block) string {
  54. if block.Bytes == nil || block.Type == "" {
  55. return ""
  56. }
  57. return string(bytes.TrimSpace(pem.EncodeToMemory(block)))
  58. }
  59. var typeToName = map[int]string{
  60. 3: "CommonName",
  61. 5: "SerialNumber",
  62. 6: "Country",
  63. 7: "Locality",
  64. 8: "Province",
  65. 9: "StreetAddress",
  66. 10: "Organization",
  67. 11: "OrganizationalUnit",
  68. 17: "PostalCode",
  69. }
  70. type names []pkix.AttributeTypeAndValue
  71. func (n names) MarshalJSON() ([]byte, error) {
  72. var buf bytes.Buffer
  73. for _, name := range n {
  74. buf.WriteString(fmt.Sprintf("/%s=%s", typeToName[name.Type[3]], name.Value))
  75. }
  76. return json.Marshal(buf.String())
  77. }
  78. // MarshalJSON serialises the bundle to JSON. The resulting JSON
  79. // structure contains the bundle (as a sequence of PEM-encoded
  80. // certificates), the certificate, the private key, the size of they
  81. // key, the issuer(s), the subject name(s), the expiration, the
  82. // hostname(s), the OCSP server, and the signature on the certificate.
  83. func (b *Bundle) MarshalJSON() ([]byte, error) {
  84. if b == nil || b.Cert == nil {
  85. return nil, errors.New("no certificate in bundle")
  86. }
  87. var keyBytes, rootBytes []byte
  88. var keyLength int
  89. var keyType, keyString string
  90. keyLength = helpers.KeyLength(b.Cert.PublicKey)
  91. switch b.Cert.PublicKeyAlgorithm {
  92. case x509.ECDSA:
  93. keyType = fmt.Sprintf("%d-bit ECDSA", keyLength)
  94. case x509.RSA:
  95. keyType = fmt.Sprintf("%d-bit RSA", keyLength)
  96. case x509.DSA:
  97. keyType = "DSA"
  98. default:
  99. keyType = "Unknown"
  100. }
  101. switch key := b.Key.(type) {
  102. case *rsa.PrivateKey:
  103. keyBytes = x509.MarshalPKCS1PrivateKey(key)
  104. keyString = PemBlockToString(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes})
  105. case *ecdsa.PrivateKey:
  106. keyBytes, _ = x509.MarshalECPrivateKey(key)
  107. keyString = PemBlockToString(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})
  108. case fmt.Stringer:
  109. keyString = key.String()
  110. }
  111. if len(b.Hostnames) == 0 {
  112. b.buildHostnames()
  113. }
  114. var ocspSupport = false
  115. if b.Cert.OCSPServer != nil {
  116. ocspSupport = true
  117. }
  118. var crlSupport = false
  119. if b.Cert.CRLDistributionPoints != nil {
  120. crlSupport = true
  121. }
  122. if b.Root != nil {
  123. rootBytes = b.Root.Raw
  124. }
  125. return json.Marshal(map[string]interface{}{
  126. "bundle": chain(b.Chain),
  127. "root": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: rootBytes}),
  128. "crt": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: b.Cert.Raw}),
  129. "key": keyString,
  130. "key_type": keyType,
  131. "key_size": keyLength,
  132. "issuer": names(b.Issuer.Names),
  133. "subject": names(b.Subject.Names),
  134. "expires": b.Expires,
  135. "leaf_expires": b.LeafExpires,
  136. "hostnames": b.Hostnames,
  137. "ocsp_support": ocspSupport,
  138. "crl_support": crlSupport,
  139. "ocsp": b.Cert.OCSPServer,
  140. "signature": helpers.SignatureString(b.Cert.SignatureAlgorithm),
  141. "status": b.Status,
  142. })
  143. }
  144. // buildHostnames sets bundle.Hostnames by the x509 cert's subject CN and DNS names
  145. // Since the subject CN may overlap with one of the DNS names, it needs to handle
  146. // the duplication by a set.
  147. func (b *Bundle) buildHostnames() {
  148. if b.Cert == nil {
  149. return
  150. }
  151. // hset keeps a set of unique hostnames.
  152. hset := make(map[string]bool)
  153. // insert CN into hset
  154. if b.Cert.Subject.CommonName != "" {
  155. hset[b.Cert.Subject.CommonName] = true
  156. }
  157. // insert all DNS names into hset
  158. for _, h := range b.Cert.DNSNames {
  159. hset[h] = true
  160. }
  161. // convert hset to an array of hostnames
  162. b.Hostnames = make([]string, len(hset))
  163. i := 0
  164. for h := range hset {
  165. b.Hostnames[i] = h
  166. i++
  167. }
  168. }