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.
2014-09-23 01:30:58 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
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"
2016-08-30 15:49:54 +03:00
"github.com/urfave/cli"
2014-09-23 01:30:58 +04:00
)
2016-11-04 14:42:18 +03:00
// CmdCert represents the available cert sub-command.
2014-09-23 01:30:58 +04:00
var CmdCert = cli . Command {
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 {
2016-11-10 01:18:22 +03:00
cli . StringFlag {
Name : "host" ,
Value : "" ,
Usage : "Comma-separated hostnames and IPs to generate a certificate for" ,
} ,
cli . StringFlag {
Name : "ecdsa-curve" ,
Value : "" ,
Usage : "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521" ,
} ,
cli . IntFlag {
Name : "rsa-bits" ,
Value : 2048 ,
Usage : "Size of RSA key to generate. Ignored if --ecdsa-curve is set" ,
} ,
cli . StringFlag {
Name : "start-date" ,
Value : "" ,
Usage : "Creation date formatted as Jan 1 15:04:05 2011" ,
} ,
cli . DurationFlag {
Name : "duration" ,
Value : 365 * 24 * time . Hour ,
Usage : "Duration that certificate is valid for" ,
} ,
cli . BoolFlag {
Name : "ca" ,
Usage : "whether this cert should be its own Certificate Authority" ,
} ,
2014-09-23 01:30:58 +04:00
} ,
}
func publicKey ( priv interface { } ) interface { } {
switch k := priv . ( type ) {
case * rsa . PrivateKey :
return & k . PublicKey
case * ecdsa . PrivateKey :
return & k . PublicKey
default :
return nil
}
}
func pemBlockForKey ( priv interface { } ) * pem . Block {
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
}
var priv interface { }
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" } ,
2016-12-21 15:13:17 +03:00
CommonName : "Gitea" ,
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" )
keyOut , err := os . OpenFile ( "key.pem" , os . O_WRONLY | os . O_CREATE | os . O_TRUNC , 0600 )
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
}