2014-09-23 01:30:58 +04:00
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
2016-12-01 10:28:43 +03:00
// Copyright 2016 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-09-23 01:30:58 +04:00
package cmd
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"log"
"math/big"
"net"
"os"
"strings"
"time"
2023-07-21 12:28:19 +03:00
"github.com/urfave/cli/v2"
2014-09-23 01:30:58 +04:00
)
2016-11-04 14:42:18 +03:00
// CmdCert represents the available cert sub-command.
2023-07-21 12:28:19 +03:00
var CmdCert = & cli . Command {
2014-09-23 01:30:58 +04:00
Name : "cert" ,
Usage : "Generate self-signed certificate" ,
2016-11-04 14:42:18 +03:00
Description : ` Generate a self - signed X .509 certificate for a TLS server .
2014-09-23 01:30:58 +04:00
Outputs to ' cert . pem ' and ' key . pem ' and will overwrite existing files . ` ,
Action : runCert ,
Flags : [ ] cli . Flag {
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2016-11-10 01:18:22 +03:00
Name : "host" ,
Value : "" ,
Usage : "Comma-separated hostnames and IPs to generate a certificate for" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2016-11-10 01:18:22 +03:00
Name : "ecdsa-curve" ,
Value : "" ,
Usage : "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . IntFlag {
2016-11-10 01:18:22 +03:00
Name : "rsa-bits" ,
2023-08-28 03:53:16 +03:00
Value : 3072 ,
2016-11-10 01:18:22 +03:00
Usage : "Size of RSA key to generate. Ignored if --ecdsa-curve is set" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2016-11-10 01:18:22 +03:00
Name : "start-date" ,
Value : "" ,
Usage : "Creation date formatted as Jan 1 15:04:05 2011" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . DurationFlag {
2016-11-10 01:18:22 +03:00
Name : "duration" ,
Value : 365 * 24 * time . Hour ,
Usage : "Duration that certificate is valid for" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . BoolFlag {
2016-11-10 01:18:22 +03:00
Name : "ca" ,
Usage : "whether this cert should be its own Certificate Authority" ,
} ,
2014-09-23 01:30:58 +04:00
} ,
}
2023-07-04 21:36:08 +03:00
func publicKey ( priv any ) any {
2014-09-23 01:30:58 +04:00
switch k := priv . ( type ) {
case * rsa . PrivateKey :
return & k . PublicKey
case * ecdsa . PrivateKey :
return & k . PublicKey
default :
return nil
}
}
2023-07-04 21:36:08 +03:00
func pemBlockForKey ( priv any ) * pem . Block {
2014-09-23 01:30:58 +04:00
switch k := priv . ( type ) {
case * rsa . PrivateKey :
return & pem . Block { Type : "RSA PRIVATE KEY" , Bytes : x509 . MarshalPKCS1PrivateKey ( k ) }
case * ecdsa . PrivateKey :
b , err := x509 . MarshalECPrivateKey ( k )
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Unable to marshal ECDSA private key: %v" , err )
2014-09-23 01:30:58 +04:00
}
return & pem . Block { Type : "EC PRIVATE KEY" , Bytes : b }
default :
return nil
}
}
2018-01-13 01:16:49 +03:00
func runCert ( c * cli . Context ) error {
if err := argsSet ( c , "host" ) ; err != nil {
return err
2014-09-23 01:30:58 +04:00
}
2023-07-04 21:36:08 +03:00
var priv any
2014-09-23 01:30:58 +04:00
var err error
2018-01-13 01:16:49 +03:00
switch c . String ( "ecdsa-curve" ) {
2014-09-23 01:30:58 +04:00
case "" :
2018-01-13 01:16:49 +03:00
priv , err = rsa . GenerateKey ( rand . Reader , c . Int ( "rsa-bits" ) )
2014-09-23 01:30:58 +04:00
case "P224" :
priv , err = ecdsa . GenerateKey ( elliptic . P224 ( ) , rand . Reader )
case "P256" :
priv , err = ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
case "P384" :
priv , err = ecdsa . GenerateKey ( elliptic . P384 ( ) , rand . Reader )
case "P521" :
priv , err = ecdsa . GenerateKey ( elliptic . P521 ( ) , rand . Reader )
default :
2018-01-13 01:16:49 +03:00
log . Fatalf ( "Unrecognized elliptic curve: %q" , c . String ( "ecdsa-curve" ) )
2014-09-23 01:30:58 +04:00
}
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to generate private key: %v" , err )
2014-09-23 01:30:58 +04:00
}
var notBefore time . Time
2018-01-13 01:16:49 +03:00
if startDate := c . String ( "start-date" ) ; startDate != "" {
notBefore , err = time . Parse ( "Jan 2 15:04:05 2006" , startDate )
2014-09-23 01:30:58 +04:00
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to parse creation date: %v" , err )
2014-09-23 01:30:58 +04:00
}
2018-01-13 01:16:49 +03:00
} else {
notBefore = time . Now ( )
2014-09-23 01:30:58 +04:00
}
2018-01-13 01:16:49 +03:00
notAfter := notBefore . Add ( c . Duration ( "duration" ) )
2014-09-23 01:30:58 +04:00
serialNumberLimit := new ( big . Int ) . Lsh ( big . NewInt ( 1 ) , 128 )
serialNumber , err := rand . Int ( rand . Reader , serialNumberLimit )
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to generate serial number: %v" , err )
2014-09-23 01:30:58 +04:00
}
template := x509 . Certificate {
SerialNumber : serialNumber ,
Subject : pkix . Name {
Organization : [ ] string { "Acme Co" } ,
2023-01-09 16:01:00 +03:00
CommonName : "Forgejo" ,
2014-09-23 01:30:58 +04:00
} ,
NotBefore : notBefore ,
NotAfter : notAfter ,
KeyUsage : x509 . KeyUsageKeyEncipherment | x509 . KeyUsageDigitalSignature ,
ExtKeyUsage : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageServerAuth } ,
BasicConstraintsValid : true ,
}
2018-01-13 01:16:49 +03:00
hosts := strings . Split ( c . String ( "host" ) , "," )
2014-09-23 01:30:58 +04:00
for _ , h := range hosts {
if ip := net . ParseIP ( h ) ; ip != nil {
template . IPAddresses = append ( template . IPAddresses , ip )
} else {
template . DNSNames = append ( template . DNSNames , h )
}
}
2018-01-13 01:16:49 +03:00
if c . Bool ( "ca" ) {
2014-09-23 01:30:58 +04:00
template . IsCA = true
template . KeyUsage |= x509 . KeyUsageCertSign
}
derBytes , err := x509 . CreateCertificate ( rand . Reader , & template , & template , publicKey ( priv ) , priv )
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to create certificate: %v" , err )
2014-09-23 01:30:58 +04:00
}
certOut , err := os . Create ( "cert.pem" )
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to open cert.pem for writing: %v" , err )
2014-09-23 01:30:58 +04:00
}
2019-06-12 22:41:28 +03:00
err = pem . Encode ( certOut , & pem . Block { Type : "CERTIFICATE" , Bytes : derBytes } )
if err != nil {
log . Fatalf ( "Failed to encode certificate: %v" , err )
}
err = certOut . Close ( )
if err != nil {
log . Fatalf ( "Failed to write cert: %v" , err )
}
2014-09-23 01:30:58 +04:00
log . Println ( "Written cert.pem" )
2022-01-20 20:46:10 +03:00
keyOut , err := os . OpenFile ( "key.pem" , os . O_WRONLY | os . O_CREATE | os . O_TRUNC , 0 o600 )
2014-09-23 01:30:58 +04:00
if err != nil {
2017-01-29 23:13:57 +03:00
log . Fatalf ( "Failed to open key.pem for writing: %v" , err )
2014-09-23 01:30:58 +04:00
}
2019-06-12 22:41:28 +03:00
err = pem . Encode ( keyOut , pemBlockForKey ( priv ) )
if err != nil {
log . Fatalf ( "Failed to encode key: %v" , err )
}
err = keyOut . Close ( )
if err != nil {
log . Fatalf ( "Failed to write key: %v" , err )
}
2014-09-23 01:30:58 +04:00
log . Println ( "Written key.pem" )
2016-05-12 21:32:28 +03:00
return nil
2014-09-23 01:30:58 +04:00
}