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