signer.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Package localca implements a localca that is useful for testing the
  2. // transport package. To use the localca, see the New and Load
  3. // functions.
  4. package localca
  5. import (
  6. "crypto/x509"
  7. "encoding/pem"
  8. "errors"
  9. "fmt"
  10. "time"
  11. "github.com/cloudflare/cfssl/config"
  12. "github.com/cloudflare/cfssl/csr"
  13. "github.com/cloudflare/cfssl/helpers"
  14. "github.com/cloudflare/cfssl/initca"
  15. "github.com/cloudflare/cfssl/signer"
  16. "github.com/cloudflare/cfssl/signer/local"
  17. )
  18. // CA is a local transport CertificateAuthority that is useful for
  19. // tests.
  20. type CA struct {
  21. s *local.Signer
  22. disabled bool
  23. // Label and Profile are used to select the CFSSL signer
  24. // components if they should be anything but the default.
  25. Label string `json:"label"`
  26. Profile string `json:"profile"`
  27. // The KeyFile and CertFile are required when using Load to
  28. // construct a CA.
  29. KeyFile string `json:"private_key,omitempty"`
  30. CertFile string `json:"certificate,omitempty"`
  31. }
  32. // Toggle switches the CA between operable mode and inoperable
  33. // mode. This is useful in testing to verify behaviours when a CA is
  34. // unavailable.
  35. func (lca *CA) Toggle() {
  36. lca.disabled = !lca.disabled
  37. }
  38. var errNotSetup = errors.New("transport: local CA has not been setup")
  39. // CACertificate returns the certificate authority's certificate.
  40. func (lca *CA) CACertificate() ([]byte, error) {
  41. if lca.s == nil {
  42. return nil, errNotSetup
  43. }
  44. cert, err := lca.s.Certificate(lca.Label, lca.Profile)
  45. if err != nil {
  46. return nil, err
  47. }
  48. p := &pem.Block{
  49. Type: "CERTIFICATE",
  50. Bytes: cert.Raw,
  51. }
  52. return pem.EncodeToMemory(p), nil
  53. }
  54. var errDisabled = errors.New("transport: local CA is deactivated")
  55. // SignCSR submits a PKCS #10 certificate signing request to a CA for
  56. // signing.
  57. func (lca *CA) SignCSR(csrPEM []byte) ([]byte, error) {
  58. if lca == nil || lca.s == nil {
  59. return nil, errNotSetup
  60. }
  61. if lca.disabled {
  62. return nil, errDisabled
  63. }
  64. p, _ := pem.Decode(csrPEM)
  65. if p == nil || p.Type != "CERTIFICATE REQUEST" {
  66. return nil, errors.New("transport: invalid PEM-encoded certificate signing request")
  67. }
  68. csr, err := x509.ParseCertificateRequest(p.Bytes)
  69. if err != nil {
  70. return nil, err
  71. }
  72. hosts := make([]string, 0, len(csr.DNSNames)+len(csr.IPAddresses))
  73. copy(hosts, csr.DNSNames)
  74. for i := range csr.IPAddresses {
  75. hosts = append(hosts, csr.IPAddresses[i].String())
  76. }
  77. sreq := signer.SignRequest{
  78. Hosts: hosts,
  79. Request: string(csrPEM),
  80. Profile: lca.Profile,
  81. Label: lca.Label,
  82. }
  83. return lca.s.Sign(sreq)
  84. }
  85. // ExampleRequest can be used as a sample request, or the returned
  86. // request can be modified.
  87. func ExampleRequest() *csr.CertificateRequest {
  88. return &csr.CertificateRequest{
  89. Hosts: []string{"localhost"},
  90. KeyRequest: &csr.KeyRequest{
  91. A: "ecdsa",
  92. S: 256,
  93. },
  94. CN: "Transport Failover Test Local CA",
  95. CA: &csr.CAConfig{
  96. PathLength: 1,
  97. Expiry: "30m",
  98. },
  99. }
  100. }
  101. // ExampleSigningConfig returns a sample config.Signing with only a
  102. // default profile.
  103. func ExampleSigningConfig() *config.Signing {
  104. return &config.Signing{
  105. Default: &config.SigningProfile{
  106. Expiry: 15 * time.Minute,
  107. Usage: []string{
  108. "server auth", "client auth",
  109. "signing", "key encipherment",
  110. },
  111. },
  112. }
  113. }
  114. // New generates a new CA from a certificate request and signing profile.
  115. func New(req *csr.CertificateRequest, profiles *config.Signing) (*CA, error) {
  116. certPEM, _, keyPEM, err := initca.New(req)
  117. if err != nil {
  118. return nil, err
  119. }
  120. // If initca returns successfully, the following (which are
  121. // all CFSSL internal functions) should not return an
  122. // error. If they do, we should abort --- something about
  123. // CFSSL has become inconsistent, and it can't be trusted.
  124. priv, err := helpers.ParsePrivateKeyPEM(keyPEM)
  125. if err != nil {
  126. return nil, fmt.Errorf("CFSSL-generated private key can't be parsed: %w", err)
  127. }
  128. cert, err := helpers.ParseCertificatePEM(certPEM)
  129. if err != nil {
  130. return nil, fmt.Errorf("CFSSL-generated private key can't be parsed: %w", err)
  131. }
  132. s, err := local.NewSigner(priv, cert, helpers.SignerAlgo(priv), profiles)
  133. if err != nil {
  134. return nil, fmt.Errorf("a signer could not be constructed: %w", err)
  135. }
  136. return NewFromSigner(s), nil
  137. }
  138. // NewFromSigner constructs a local CA from a CFSSL signer.
  139. func NewFromSigner(s *local.Signer) *CA {
  140. return &CA{s: s}
  141. }
  142. // Load reads the key and certificate from the files specified in the
  143. // CA.
  144. func Load(lca *CA, profiles *config.Signing) (err error) {
  145. lca.s, err = local.NewSignerFromFile(lca.CertFile, lca.KeyFile, profiles)
  146. return err
  147. }