insert.go 5.1 KB

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