gitea源码

cert.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Copyright 2014 The Gogs Authors. All rights reserved.
  3. // Copyright 2016 The Gitea Authors. All rights reserved.
  4. // SPDX-License-Identifier: MIT
  5. package cmd
  6. import (
  7. "context"
  8. "crypto/ecdsa"
  9. "crypto/elliptic"
  10. "crypto/rand"
  11. "crypto/rsa"
  12. "crypto/x509"
  13. "crypto/x509/pkix"
  14. "encoding/pem"
  15. "fmt"
  16. "log"
  17. "math/big"
  18. "net"
  19. "os"
  20. "strings"
  21. "time"
  22. "github.com/urfave/cli/v3"
  23. )
  24. // cmdCert represents the available cert sub-command.
  25. func cmdCert() *cli.Command {
  26. return &cli.Command{
  27. Name: "cert",
  28. Usage: "Generate self-signed certificate",
  29. Description: `Generate a self-signed X.509 certificate for a TLS server.
  30. Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
  31. Action: runCert,
  32. Flags: []cli.Flag{
  33. &cli.StringFlag{
  34. Name: "host",
  35. Usage: "Comma-separated hostnames and IPs to generate a certificate for",
  36. Required: true,
  37. },
  38. &cli.StringFlag{
  39. Name: "ecdsa-curve",
  40. Value: "",
  41. Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
  42. },
  43. &cli.IntFlag{
  44. Name: "rsa-bits",
  45. Value: 3072,
  46. Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
  47. },
  48. &cli.StringFlag{
  49. Name: "start-date",
  50. Value: "",
  51. Usage: "Creation date formatted as Jan 1 15:04:05 2011",
  52. },
  53. &cli.DurationFlag{
  54. Name: "duration",
  55. Value: 365 * 24 * time.Hour,
  56. Usage: "Duration that certificate is valid for",
  57. },
  58. &cli.BoolFlag{
  59. Name: "ca",
  60. Usage: "whether this cert should be its own Certificate Authority",
  61. },
  62. &cli.StringFlag{
  63. Name: "out",
  64. Value: "cert.pem",
  65. Usage: "Path to the file where there certificate will be saved",
  66. },
  67. &cli.StringFlag{
  68. Name: "keyout",
  69. Value: "key.pem",
  70. Usage: "Path to the file where there certificate key will be saved",
  71. },
  72. },
  73. }
  74. }
  75. func publicKey(priv any) any {
  76. switch k := priv.(type) {
  77. case *rsa.PrivateKey:
  78. return &k.PublicKey
  79. case *ecdsa.PrivateKey:
  80. return &k.PublicKey
  81. default:
  82. return nil
  83. }
  84. }
  85. func pemBlockForKey(priv any) *pem.Block {
  86. switch k := priv.(type) {
  87. case *rsa.PrivateKey:
  88. return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
  89. case *ecdsa.PrivateKey:
  90. b, err := x509.MarshalECPrivateKey(k)
  91. if err != nil {
  92. log.Fatalf("Unable to marshal ECDSA private key: %v", err)
  93. }
  94. return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
  95. default:
  96. return nil
  97. }
  98. }
  99. func runCert(_ context.Context, c *cli.Command) error {
  100. var priv any
  101. var err error
  102. switch c.String("ecdsa-curve") {
  103. case "":
  104. priv, err = rsa.GenerateKey(rand.Reader, c.Int("rsa-bits"))
  105. case "P224":
  106. priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
  107. case "P256":
  108. priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  109. case "P384":
  110. priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
  111. case "P521":
  112. priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
  113. default:
  114. err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
  115. }
  116. if err != nil {
  117. return fmt.Errorf("failed to generate private key: %w", err)
  118. }
  119. var notBefore time.Time
  120. if startDate := c.String("start-date"); startDate != "" {
  121. notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
  122. if err != nil {
  123. return fmt.Errorf("failed to parse creation date %w", err)
  124. }
  125. } else {
  126. notBefore = time.Now()
  127. }
  128. notAfter := notBefore.Add(c.Duration("duration"))
  129. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  130. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  131. if err != nil {
  132. return fmt.Errorf("failed to generate serial number: %w", err)
  133. }
  134. template := x509.Certificate{
  135. SerialNumber: serialNumber,
  136. Subject: pkix.Name{
  137. Organization: []string{"Acme Co"},
  138. CommonName: "Gitea",
  139. },
  140. NotBefore: notBefore,
  141. NotAfter: notAfter,
  142. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  143. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  144. BasicConstraintsValid: true,
  145. }
  146. hosts := strings.SplitSeq(c.String("host"), ",")
  147. for h := range hosts {
  148. if ip := net.ParseIP(h); ip != nil {
  149. template.IPAddresses = append(template.IPAddresses, ip)
  150. } else {
  151. template.DNSNames = append(template.DNSNames, h)
  152. }
  153. }
  154. if c.Bool("ca") {
  155. template.IsCA = true
  156. template.KeyUsage |= x509.KeyUsageCertSign
  157. }
  158. derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
  159. if err != nil {
  160. return fmt.Errorf("failed to create certificate: %w", err)
  161. }
  162. certOut, err := os.Create(c.String("out"))
  163. if err != nil {
  164. return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
  165. }
  166. err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  167. if err != nil {
  168. return fmt.Errorf("failed to encode certificate: %w", err)
  169. }
  170. err = certOut.Close()
  171. if err != nil {
  172. return fmt.Errorf("failed to write cert: %w", err)
  173. }
  174. fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out"))
  175. keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
  176. if err != nil {
  177. return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
  178. }
  179. err = pem.Encode(keyOut, pemBlockForKey(priv))
  180. if err != nil {
  181. return fmt.Errorf("failed to encode key: %w", err)
  182. }
  183. err = keyOut.Close()
  184. if err != nil {
  185. return fmt.Errorf("failed to write key: %w", err)
  186. }
  187. fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout"))
  188. return nil
  189. }