bundle.go 5.2 KB

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