generator.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Package generator implements the HTTP handlers for certificate generation.
  2. package generator
  3. import (
  4. "crypto/md5"
  5. "crypto/sha1"
  6. "crypto/sha256"
  7. "crypto/x509"
  8. "encoding/json"
  9. "encoding/pem"
  10. "fmt"
  11. "io"
  12. "net/http"
  13. "github.com/cloudflare/cfssl/api"
  14. "github.com/cloudflare/cfssl/bundler"
  15. "github.com/cloudflare/cfssl/config"
  16. "github.com/cloudflare/cfssl/csr"
  17. "github.com/cloudflare/cfssl/errors"
  18. "github.com/cloudflare/cfssl/log"
  19. "github.com/cloudflare/cfssl/signer"
  20. "github.com/cloudflare/cfssl/signer/universal"
  21. )
  22. const (
  23. // CSRNoHostMessage is used to alert the user to a certificate lacking a hosts field.
  24. CSRNoHostMessage = `This certificate lacks a "hosts" field. This makes it unsuitable for
  25. websites. For more information see the Baseline Requirements for the Issuance and Management
  26. of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
  27. specifically, section 10.2.3 ("Information Requirements").`
  28. // NoBundlerMessage is used to alert the user that the server does not have a bundler initialized.
  29. NoBundlerMessage = `This request requires a bundler, but one is not initialized for the API server.`
  30. )
  31. // Sum contains digests for a certificate or certificate request.
  32. type Sum struct {
  33. MD5 string `json:"md5"`
  34. SHA1 string `json:"sha-1"`
  35. SHA256 string `json:"sha-256"`
  36. }
  37. // Validator is a type of function that contains the logic for validating
  38. // a certificate request.
  39. type Validator func(*csr.CertificateRequest) error
  40. // A CertRequest stores a PEM-encoded private key and corresponding
  41. // CSR; this is returned from the CSR generation endpoint.
  42. type CertRequest struct {
  43. Key string `json:"private_key"`
  44. CSR string `json:"certificate_request"`
  45. Sums map[string]Sum `json:"sums"`
  46. }
  47. // A Handler accepts JSON-encoded certificate requests and
  48. // returns a new private key and certificate request.
  49. type Handler struct {
  50. generator *csr.Generator
  51. }
  52. // NewHandler builds a new Handler from the
  53. // validation function provided.
  54. func NewHandler(validator Validator) (http.Handler, error) {
  55. log.Info("setting up key / CSR generator")
  56. return &api.HTTPHandler{
  57. Handler: &Handler{
  58. generator: &csr.Generator{Validator: validator},
  59. },
  60. Methods: []string{"POST"},
  61. }, nil
  62. }
  63. func computeSum(in []byte) (sum Sum, err error) {
  64. var data []byte
  65. p, _ := pem.Decode(in)
  66. if p == nil {
  67. err = errors.NewBadRequestString("not a CSR or certificate")
  68. return
  69. }
  70. switch p.Type {
  71. case "CERTIFICATE REQUEST":
  72. var req *x509.CertificateRequest
  73. req, err = x509.ParseCertificateRequest(p.Bytes)
  74. if err != nil {
  75. return
  76. }
  77. data = req.Raw
  78. case "CERTIFICATE":
  79. var cert *x509.Certificate
  80. cert, err = x509.ParseCertificate(p.Bytes)
  81. if err != nil {
  82. return
  83. }
  84. data = cert.Raw
  85. default:
  86. err = errors.NewBadRequestString("not a CSR or certificate")
  87. return
  88. }
  89. md5Sum := md5.Sum(data)
  90. sha1Sum := sha1.Sum(data)
  91. sha256Sum := sha256.Sum256(data)
  92. sum.MD5 = fmt.Sprintf("%X", md5Sum[:])
  93. sum.SHA1 = fmt.Sprintf("%X", sha1Sum[:])
  94. sum.SHA256 = fmt.Sprintf("%X", sha256Sum[:])
  95. return
  96. }
  97. // Handle responds to requests for the CA to generate a new private
  98. // key and certificate request on behalf of the client. The format for
  99. // these requests is documented in the API documentation.
  100. func (g *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
  101. log.Info("request for CSR")
  102. body, err := io.ReadAll(r.Body)
  103. if err != nil {
  104. log.Warningf("failed to read request body: %v", err)
  105. return errors.NewBadRequest(err)
  106. }
  107. r.Body.Close()
  108. req := new(csr.CertificateRequest)
  109. req.KeyRequest = csr.NewKeyRequest()
  110. err = json.Unmarshal(body, req)
  111. if err != nil {
  112. log.Warningf("failed to unmarshal request: %v", err)
  113. return errors.NewBadRequest(err)
  114. }
  115. if req.CA != nil {
  116. log.Warningf("request received with CA section")
  117. return errors.NewBadRequestString("ca section only permitted in initca")
  118. }
  119. csr, key, err := g.generator.ProcessRequest(req)
  120. if err != nil {
  121. log.Warningf("failed to process CSR: %v", err)
  122. // The validator returns a *cfssl/errors.HttpError
  123. return err
  124. }
  125. sum, err := computeSum(csr)
  126. if err != nil {
  127. return errors.NewBadRequest(err)
  128. }
  129. // Both key and csr are returned PEM-encoded.
  130. response := api.NewSuccessResponse(&CertRequest{
  131. Key: string(key),
  132. CSR: string(csr),
  133. Sums: map[string]Sum{"certificate_request": sum},
  134. })
  135. w.Header().Set("Content-Type", "application/json")
  136. enc := json.NewEncoder(w)
  137. err = enc.Encode(response)
  138. return err
  139. }
  140. // A CertGeneratorHandler accepts JSON-encoded certificate requests
  141. // and returns a new private key and signed certificate; it handles
  142. // sending the CSR to the server.
  143. type CertGeneratorHandler struct {
  144. generator *csr.Generator
  145. bundler *bundler.Bundler
  146. signer signer.Signer
  147. }
  148. // NewCertGeneratorHandler builds a new handler for generating
  149. // certificates directly from certificate requests; the validator covers
  150. // the certificate request and the CA's key and certificate are used to
  151. // sign the generated request. If remote is not an empty string, the
  152. // handler will send signature requests to the CFSSL instance contained
  153. // in remote.
  154. func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
  155. var err error
  156. log.Info("setting up new generator / signer")
  157. cg := new(CertGeneratorHandler)
  158. if policy == nil {
  159. policy = &config.Signing{
  160. Default: config.DefaultConfig(),
  161. Profiles: nil,
  162. }
  163. }
  164. root := universal.Root{
  165. Config: map[string]string{
  166. "ca-file": caFile,
  167. "ca-key-file": caKeyFile,
  168. },
  169. }
  170. if cg.signer, err = universal.NewSigner(root, policy); err != nil {
  171. log.Errorf("setting up signer failed: %v", err)
  172. return nil, err
  173. }
  174. cg.generator = &csr.Generator{Validator: validator}
  175. return api.HTTPHandler{Handler: cg, Methods: []string{"POST"}}, nil
  176. }
  177. // NewCertGeneratorHandlerFromSigner returns a handler directly from
  178. // the signer and validation function.
  179. func NewCertGeneratorHandlerFromSigner(validator Validator, signer signer.Signer) http.Handler {
  180. return api.HTTPHandler{
  181. Handler: &CertGeneratorHandler{
  182. generator: &csr.Generator{Validator: validator},
  183. signer: signer,
  184. },
  185. Methods: []string{"POST"},
  186. }
  187. }
  188. // SetBundler allows injecting an optional Bundler into the CertGeneratorHandler.
  189. func (cg *CertGeneratorHandler) SetBundler(caBundleFile, intBundleFile string) (err error) {
  190. cg.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
  191. return err
  192. }
  193. type genSignRequest struct {
  194. Request *csr.CertificateRequest `json:"request"`
  195. Profile string `json:"profile"`
  196. Label string `json:"label"`
  197. Bundle bool `json:"bundle"`
  198. }
  199. // Handle responds to requests for the CA to generate a new private
  200. // key and certificate on behalf of the client. The format for these
  201. // requests is documented in the API documentation.
  202. func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error {
  203. log.Info("request for CSR")
  204. req := new(genSignRequest)
  205. req.Request = csr.New()
  206. body, err := io.ReadAll(r.Body)
  207. if err != nil {
  208. log.Warningf("failed to read request body: %v", err)
  209. return errors.NewBadRequest(err)
  210. }
  211. r.Body.Close()
  212. err = json.Unmarshal(body, req)
  213. if err != nil {
  214. log.Warningf("failed to unmarshal request: %v", err)
  215. return errors.NewBadRequest(err)
  216. }
  217. if req.Request == nil {
  218. log.Warning("empty request received")
  219. return errors.NewBadRequestString("missing request section")
  220. }
  221. if req.Request.CA != nil {
  222. log.Warningf("request received with CA section")
  223. return errors.NewBadRequestString("ca section only permitted in initca")
  224. }
  225. csr, key, err := cg.generator.ProcessRequest(req.Request)
  226. if err != nil {
  227. log.Warningf("failed to process CSR: %v", err)
  228. // The validator returns a *cfssl/errors.HttpError
  229. return err
  230. }
  231. signReq := signer.SignRequest{
  232. Request: string(csr),
  233. Profile: req.Profile,
  234. Label: req.Label,
  235. }
  236. certBytes, err := cg.signer.Sign(signReq)
  237. if err != nil {
  238. log.Warningf("failed to sign request: %v", err)
  239. return err
  240. }
  241. reqSum, err := computeSum(csr)
  242. if err != nil {
  243. return errors.NewBadRequest(err)
  244. }
  245. certSum, err := computeSum(certBytes)
  246. if err != nil {
  247. return errors.NewBadRequest(err)
  248. }
  249. result := map[string]interface{}{
  250. "private_key": string(key),
  251. "certificate_request": string(csr),
  252. "certificate": string(certBytes),
  253. "sums": map[string]Sum{
  254. "certificate_request": reqSum,
  255. "certificate": certSum,
  256. },
  257. }
  258. if req.Bundle {
  259. if cg.bundler == nil {
  260. return api.SendResponseWithMessage(w, result, NoBundlerMessage,
  261. errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
  262. }
  263. bundle, err := cg.bundler.BundleFromPEMorDER(certBytes, nil, bundler.Optimal, "")
  264. if err != nil {
  265. return err
  266. }
  267. result["bundle"] = bundle
  268. }
  269. if len(req.Request.Hosts) == 0 {
  270. return api.SendResponseWithMessage(w, result, CSRNoHostMessage,
  271. errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
  272. }
  273. return api.SendResponse(w, result)
  274. }
  275. // CSRValidate does nothing and will never return an error. It exists because NewHandler
  276. // requires a Validator as a parameter.
  277. func CSRValidate(req *csr.CertificateRequest) error {
  278. return nil
  279. }