insert.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package certadd
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "encoding/base64"
  6. "encoding/hex"
  7. "encoding/json"
  8. "io"
  9. "math/big"
  10. "net/http"
  11. "time"
  12. "github.com/cloudflare/cfssl/api"
  13. "github.com/cloudflare/cfssl/certdb"
  14. "github.com/cloudflare/cfssl/errors"
  15. "github.com/cloudflare/cfssl/helpers"
  16. "github.com/cloudflare/cfssl/ocsp"
  17. "github.com/jmoiron/sqlx/types"
  18. stdocsp "golang.org/x/crypto/ocsp"
  19. )
  20. // This is patterned on
  21. // https://github.com/cloudflare/cfssl/blob/master/api/revoke/revoke.go. This
  22. // file defines an HTTP endpoint handler that accepts certificates and
  23. // inserts them into a certdb, optionally also creating an OCSP
  24. // response for them. If so, it will also return the OCSP response as
  25. // a base64 encoded string.
  26. // A Handler accepts new SSL certificates and inserts them into the
  27. // certdb, creating an appropriate OCSP response for them.
  28. type Handler struct {
  29. dbAccessor certdb.Accessor
  30. signer ocsp.Signer
  31. }
  32. // NewHandler creates a new Handler from a certdb.Accessor and ocsp.Signer
  33. func NewHandler(dbAccessor certdb.Accessor, signer ocsp.Signer) http.Handler {
  34. return &api.HTTPHandler{
  35. Handler: &Handler{
  36. dbAccessor: dbAccessor,
  37. signer: signer,
  38. },
  39. Methods: []string{"POST"},
  40. }
  41. }
  42. // AddRequest describes a request from a client to insert a
  43. // certificate into the database.
  44. type AddRequest struct {
  45. Serial string `json:"serial_number"`
  46. AKI string `json:"authority_key_identifier"`
  47. CALabel string `json:"ca_label"`
  48. Status string `json:"status"`
  49. Reason int `json:"reason"`
  50. Expiry time.Time `json:"expiry"`
  51. RevokedAt time.Time `json:"revoked_at"`
  52. PEM string `json:"pem"`
  53. IssuedAt *time.Time `json:"issued_at"`
  54. NotBefore *time.Time `json:"not_before"`
  55. MetadataJSON types.JSONText `json:"metadata"`
  56. SansJSON types.JSONText `json:"sans"`
  57. CommonName string `json:"common_name"`
  58. }
  59. // Map of valid reason codes
  60. var validReasons = map[int]bool{
  61. stdocsp.Unspecified: true,
  62. stdocsp.KeyCompromise: true,
  63. stdocsp.CACompromise: true,
  64. stdocsp.AffiliationChanged: true,
  65. stdocsp.Superseded: true,
  66. stdocsp.CessationOfOperation: true,
  67. stdocsp.CertificateHold: true,
  68. stdocsp.RemoveFromCRL: true,
  69. stdocsp.PrivilegeWithdrawn: true,
  70. stdocsp.AACompromise: true,
  71. }
  72. // Handle handles HTTP requests to add certificates
  73. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
  74. body, err := io.ReadAll(r.Body)
  75. if err != nil {
  76. return err
  77. }
  78. r.Body.Close()
  79. var req AddRequest
  80. err = json.Unmarshal(body, &req)
  81. if err != nil {
  82. return errors.NewBadRequestString("Unable to parse certificate addition request")
  83. }
  84. if len(req.Serial) == 0 {
  85. return errors.NewBadRequestString("Serial number is required but not provided")
  86. }
  87. if len(req.AKI) == 0 {
  88. return errors.NewBadRequestString("Authority key identifier is required but not provided")
  89. }
  90. if _, present := ocsp.StatusCode[req.Status]; !present {
  91. return errors.NewBadRequestString("Invalid certificate status")
  92. }
  93. if ocsp.StatusCode[req.Status] == stdocsp.Revoked {
  94. if req.RevokedAt == (time.Time{}) {
  95. return errors.NewBadRequestString("Revoked certificate should specify when it was revoked")
  96. }
  97. if _, present := validReasons[req.Reason]; !present {
  98. return errors.NewBadRequestString("Invalid certificate status reason code")
  99. }
  100. }
  101. if len(req.PEM) == 0 {
  102. return errors.NewBadRequestString("The provided certificate is empty")
  103. }
  104. if req.Expiry.IsZero() {
  105. return errors.NewBadRequestString("Expiry is required but not provided")
  106. }
  107. // Parse the certificate and validate that it matches
  108. cert, err := helpers.ParseCertificatePEM([]byte(req.PEM))
  109. if err != nil {
  110. return errors.NewBadRequestString("Unable to parse PEM encoded certificates")
  111. }
  112. serialBigInt := new(big.Int)
  113. if _, success := serialBigInt.SetString(req.Serial, 10); !success {
  114. return errors.NewBadRequestString("Unable to parse serial key of request")
  115. }
  116. if serialBigInt.Cmp(cert.SerialNumber) != 0 {
  117. return errors.NewBadRequestString("Serial key of request and certificate do not match")
  118. }
  119. aki, err := hex.DecodeString(req.AKI)
  120. if err != nil {
  121. return errors.NewBadRequestString("Unable to decode authority key identifier")
  122. }
  123. if !bytes.Equal(aki, cert.AuthorityKeyId) {
  124. return errors.NewBadRequestString("Authority key identifier of request and certificate do not match")
  125. }
  126. if req.Expiry != cert.NotAfter {
  127. return errors.NewBadRequestString("Expiry of request and certificate do not match")
  128. }
  129. cr := certdb.CertificateRecord{
  130. Serial: req.Serial,
  131. AKI: req.AKI,
  132. CALabel: req.CALabel,
  133. Status: req.Status,
  134. Reason: req.Reason,
  135. Expiry: req.Expiry,
  136. RevokedAt: req.RevokedAt,
  137. PEM: req.PEM,
  138. IssuedAt: req.IssuedAt,
  139. NotBefore: req.NotBefore,
  140. MetadataJSON: req.MetadataJSON,
  141. SANsJSON: req.SansJSON,
  142. CommonName: sql.NullString{String: req.CommonName, Valid: req.CommonName != ""},
  143. }
  144. err = h.dbAccessor.InsertCertificate(cr)
  145. if err != nil {
  146. return err
  147. }
  148. result := map[string]string{}
  149. if h.signer != nil {
  150. // Now create an appropriate OCSP response
  151. sr := ocsp.SignRequest{
  152. Certificate: cert,
  153. Status: req.Status,
  154. Reason: req.Reason,
  155. RevokedAt: req.RevokedAt,
  156. }
  157. ocspResponse, err := h.signer.Sign(sr)
  158. if err != nil {
  159. return err
  160. }
  161. // We parse the OCSP response in order to get the next
  162. // update time/expiry time
  163. ocspParsed, err := stdocsp.ParseResponse(ocspResponse, nil)
  164. if err != nil {
  165. return err
  166. }
  167. result["ocsp_response"] = base64.StdEncoding.EncodeToString(ocspResponse)
  168. ocspRecord := certdb.OCSPRecord{
  169. Serial: req.Serial,
  170. AKI: req.AKI,
  171. Body: string(ocspResponse),
  172. Expiry: ocspParsed.NextUpdate,
  173. }
  174. if err = h.dbAccessor.InsertOCSP(ocspRecord); err != nil {
  175. return err
  176. }
  177. }
  178. return api.SendResponse(w, result)
  179. }