2022-06-27 08:27:57 +00:00
package commands
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/utils"
)
func newCryptoCmd ( ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
2022-10-17 10:51:59 +00:00
Use : cmdUseCrypto ,
2022-06-27 08:27:57 +00:00
Short : cmdAutheliaCryptoShort ,
Long : cmdAutheliaCryptoLong ,
Example : cmdAutheliaCryptoExample ,
Args : cobra . NoArgs ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmd . AddCommand (
2022-10-17 10:51:59 +00:00
newCryptoRandCmd ( ) ,
2022-06-27 08:27:57 +00:00
newCryptoCertificateCmd ( ) ,
2022-10-17 10:51:59 +00:00
newCryptoHashCmd ( ) ,
2022-06-27 08:27:57 +00:00
newCryptoPairCmd ( ) ,
)
return cmd
}
2022-10-17 10:51:59 +00:00
func newCryptoRandCmd ( ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
Use : cmdUseRand ,
Short : cmdAutheliaCryptoRandShort ,
Long : cmdAutheliaCryptoRandLong ,
Example : cmdAutheliaCryptoRandExample ,
Args : cobra . NoArgs ,
RunE : func ( cmd * cobra . Command , args [ ] string ) ( err error ) {
useCharSet , useCharacters := cmd . Flags ( ) . Changed ( cmdFlagNameCharSet ) , cmd . Flags ( ) . Changed ( cmdFlagNameCharacters )
if useCharSet && useCharacters {
return fmt . Errorf ( "flags '--%s' and '--%s' are mutually exclusive, only one may be specified" , cmdFlagNameCharSet , cmdFlagNameCharacters )
}
var (
charset string
n int
)
if n , err = cmd . Flags ( ) . GetInt ( cmdFlagNameLength ) ; err != nil {
return err
}
if n < 1 {
return fmt . Errorf ( "length must be at least 1" )
}
switch {
case useCharSet , ! useCharSet && ! useCharacters :
var c string
if c , err = cmd . Flags ( ) . GetString ( cmdFlagNameCharSet ) ; err != nil {
return err
}
switch c {
case "ascii" :
charset = utils . CharSetASCII
case "alphanumeric" :
charset = utils . CharSetAlphaNumeric
case "alphabetic" :
charset = utils . CharSetAlphabetic
case "numeric-hex" :
charset = utils . CharSetNumericHex
case "numeric" :
charset = utils . CharSetNumeric
default :
return fmt . Errorf ( "invalid charset '%s', must be one of 'ascii', 'alphanumeric', 'alphabetic', 'numeric', or 'numeric-hex'" , c )
}
case useCharacters :
if charset , err = cmd . Flags ( ) . GetString ( cmdFlagNameCharacters ) ; err != nil {
return err
}
}
fmt . Printf ( "Random Value: %s\n" , utils . RandomString ( n , charset , true ) )
return nil
} ,
DisableAutoGenTag : true ,
}
cmd . Flags ( ) . StringP ( cmdFlagNameCharSet , "c" , "alphanumeric" , "Sets the charset for the output, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', and 'numeric-hex'" )
cmd . Flags ( ) . String ( cmdFlagNameCharacters , "" , "Sets the explicit characters for the random output" )
cmd . Flags ( ) . IntP ( cmdFlagNameLength , "n" , 80 , "Sets the length of the random output" )
return cmd
}
2022-06-27 08:27:57 +00:00
func newCryptoCertificateCmd ( ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
Use : cmdUseCertificate ,
Short : cmdAutheliaCryptoCertificateShort ,
Long : cmdAutheliaCryptoCertificateLong ,
Example : cmdAutheliaCryptoCertificateExample ,
Args : cobra . NoArgs ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmd . AddCommand (
newCryptoCertificateSubCmd ( cmdUseRSA ) ,
newCryptoCertificateSubCmd ( cmdUseECDSA ) ,
newCryptoCertificateSubCmd ( cmdUseEd25519 ) ,
)
return cmd
}
func newCryptoCertificateSubCmd ( use string ) ( cmd * cobra . Command ) {
2022-10-17 10:51:59 +00:00
useFmt := fmtCryptoCertificateUse ( use )
2022-06-27 08:27:57 +00:00
cmd = & cobra . Command {
Use : use ,
Short : fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateSubShort , useFmt ) ,
Long : fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateSubLong , useFmt , useFmt ) ,
2022-10-17 10:51:59 +00:00
Example : fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateSubExample , use ) ,
2022-06-27 08:27:57 +00:00
Args : cobra . NoArgs ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmd . AddCommand ( newCryptoGenerateCmd ( cmdUseCertificate , use ) , newCryptoCertificateRequestCmd ( use ) )
return cmd
}
func newCryptoCertificateRequestCmd ( algorithm string ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
Use : cmdUseRequest ,
Args : cobra . NoArgs ,
RunE : cryptoCertificateRequestRunE ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmdFlagsCryptoPrivateKey ( cmd )
cmdFlagsCryptoCertificateCommon ( cmd )
cmdFlagsCryptoCertificateRequest ( cmd )
2022-10-17 10:51:59 +00:00
algorithmFmt := fmtCryptoCertificateUse ( algorithm )
2022-06-27 08:27:57 +00:00
cmd . Short = fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateGenerateRequestShort , algorithmFmt , cryptoCertCSROut )
cmd . Long = fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateGenerateRequestLong , algorithmFmt , cryptoCertCSROut , algorithmFmt , cryptoCertCSROut )
switch algorithm {
case cmdUseRSA :
cmd . Example = cmdAutheliaCryptoCertificateRSARequestExample
cmdFlagsCryptoPrivateKeyRSA ( cmd )
case cmdUseECDSA :
cmd . Example = cmdAutheliaCryptoCertificateECDSARequestExample
cmdFlagsCryptoPrivateKeyECDSA ( cmd )
case cmdUseEd25519 :
cmd . Example = cmdAutheliaCryptoCertificateEd25519RequestExample
cmdFlagsCryptoPrivateKeyEd25519 ( cmd )
}
return cmd
}
func newCryptoPairCmd ( ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
Use : cmdUsePair ,
Short : cmdAutheliaCryptoPairShort ,
Long : cmdAutheliaCryptoPairLong ,
Example : cmdAutheliaCryptoPairExample ,
Args : cobra . NoArgs ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmd . AddCommand (
newCryptoPairSubCmd ( cmdUseRSA ) ,
newCryptoPairSubCmd ( cmdUseECDSA ) ,
newCryptoPairSubCmd ( cmdUseEd25519 ) ,
)
return cmd
}
func newCryptoPairSubCmd ( use string ) ( cmd * cobra . Command ) {
var (
example , useFmt string
)
2022-10-17 10:51:59 +00:00
useFmt = fmtCryptoCertificateUse ( use )
2022-06-27 08:27:57 +00:00
switch use {
case cmdUseRSA :
example = cmdAutheliaCryptoPairRSAExample
case cmdUseECDSA :
example = cmdAutheliaCryptoPairECDSAExample
case cmdUseEd25519 :
example = cmdAutheliaCryptoPairEd25519Example
}
cmd = & cobra . Command {
Use : use ,
Short : fmt . Sprintf ( cmdAutheliaCryptoPairSubShort , useFmt ) ,
Long : fmt . Sprintf ( cmdAutheliaCryptoPairSubLong , useFmt , useFmt ) ,
Example : example ,
Args : cobra . NoArgs ,
RunE : cryptoGenerateRunE ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmd . AddCommand ( newCryptoGenerateCmd ( cmdUsePair , use ) )
return cmd
}
func newCryptoGenerateCmd ( category , algorithm string ) ( cmd * cobra . Command ) {
cmd = & cobra . Command {
Use : cmdUseGenerate ,
Args : cobra . NoArgs ,
RunE : cryptoGenerateRunE ,
2022-09-01 02:24:47 +00:00
DisableAutoGenTag : true ,
2022-06-27 08:27:57 +00:00
}
cmdFlagsCryptoPrivateKey ( cmd )
2022-10-17 10:51:59 +00:00
algorithmFmt := fmtCryptoCertificateUse ( algorithm )
2022-06-27 08:27:57 +00:00
switch category {
case cmdUseCertificate :
cmdFlagsCryptoCertificateCommon ( cmd )
cmdFlagsCryptoCertificateGenerate ( cmd )
cmd . Short = fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateGenerateRequestShort , algorithmFmt , cryptoCertPubCertOut )
cmd . Long = fmt . Sprintf ( fmtCmdAutheliaCryptoCertificateGenerateRequestLong , algorithmFmt , cryptoCertPubCertOut , algorithmFmt , cryptoCertPubCertOut )
switch algorithm {
case cmdUseRSA :
cmd . Example = cmdAutheliaCryptoCertificateRSAGenerateExample
cmdFlagsCryptoPrivateKeyRSA ( cmd )
case cmdUseECDSA :
cmd . Example = cmdAutheliaCryptoCertificateECDSAGenerateExample
cmdFlagsCryptoPrivateKeyECDSA ( cmd )
case cmdUseEd25519 :
cmd . Example = cmdAutheliaCryptoCertificateEd25519GenerateExample
cmdFlagsCryptoPrivateKeyEd25519 ( cmd )
}
case cmdUsePair :
cmdFlagsCryptoPairGenerate ( cmd )
cmd . Short = fmt . Sprintf ( fmtCmdAutheliaCryptoPairGenerateShort , algorithmFmt )
cmd . Long = fmt . Sprintf ( fmtCmdAutheliaCryptoPairGenerateLong , algorithmFmt , algorithmFmt )
switch algorithm {
case cmdUseRSA :
cmd . Example = cmdAutheliaCryptoPairRSAGenerateExample
cmdFlagsCryptoPrivateKeyRSA ( cmd )
case cmdUseECDSA :
cmd . Example = cmdAutheliaCryptoPairECDSAGenerateExample
cmdFlagsCryptoPrivateKeyECDSA ( cmd )
case cmdUseEd25519 :
cmd . Example = cmdAutheliaCryptoPairEd25519GenerateExample
cmdFlagsCryptoPrivateKeyEd25519 ( cmd )
}
}
return cmd
}
func cryptoGenerateRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
var (
2022-10-05 05:05:23 +00:00
privateKey any
2022-06-27 08:27:57 +00:00
)
if privateKey , err = cryptoGenPrivateKeyFromCmd ( cmd ) ; err != nil {
return err
}
if cmd . Parent ( ) . Parent ( ) . Use == cmdUseCertificate {
return cryptoCertificateGenerateRunE ( cmd , args , privateKey )
}
return cryptoPairGenerateRunE ( cmd , args , privateKey )
}
func cryptoCertificateRequestRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
var (
2022-10-05 05:05:23 +00:00
privateKey any
2022-06-27 08:27:57 +00:00
)
if privateKey , err = cryptoGenPrivateKeyFromCmd ( cmd ) ; err != nil {
return err
}
var (
template * x509 . CertificateRequest
csr [ ] byte
privateKeyPath , csrPath string
)
if template , err = cryptoGetCSRFromCmd ( cmd ) ; err != nil {
return err
}
b := strings . Builder { }
b . WriteString ( "Generating Certificate Request\n\n" )
b . WriteString ( "Subject:\n" )
b . WriteString ( fmt . Sprintf ( "\tCommon Name: %s, Organization: %s, Organizational Unit: %s\n" , template . Subject . CommonName , template . Subject . Organization , template . Subject . OrganizationalUnit ) )
b . WriteString ( fmt . Sprintf ( "\tCountry: %v, Province: %v, Street Address: %v, Postal Code: %v, Locality: %v\n\n" , template . Subject . Country , template . Subject . Province , template . Subject . StreetAddress , template . Subject . PostalCode , template . Subject . Locality ) )
b . WriteString ( "Properties:\n" )
b . WriteString ( fmt . Sprintf ( "\tSignature Algorithm: %s, Public Key Algorithm: %s" , template . SignatureAlgorithm , template . PublicKeyAlgorithm ) )
switch k := privateKey . ( type ) {
case * rsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( ", Bits: %d" , k . N . BitLen ( ) ) )
case * ecdsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( ", Elliptic Curve: %s" , k . Curve . Params ( ) . Name ) )
}
b . WriteString ( fmt . Sprintf ( "\n\tSubject Alternative Names: %s\n\n" , strings . Join ( cryptoSANsToString ( template . DNSNames , template . IPAddresses ) , ", " ) ) )
if privateKeyPath , csrPath , err = cryptoGetWritePathsFromCmd ( cmd ) ; err != nil {
return err
}
b . WriteString ( "Output Paths:\n" )
b . WriteString ( fmt . Sprintf ( "\tPrivate Key: %s\n" , privateKeyPath ) )
b . WriteString ( fmt . Sprintf ( "\tCertificate Request: %s\n\n" , csrPath ) )
fmt . Print ( b . String ( ) )
b . Reset ( )
if csr , err = x509 . CreateCertificateRequest ( rand . Reader , template , privateKey ) ; err != nil {
return fmt . Errorf ( "failed to create certificate request: %w" , err )
}
if privateKeyPath , csrPath , err = cryptoGetWritePathsFromCmd ( cmd ) ; err != nil {
return err
}
if err = utils . WriteKeyToPEM ( privateKey , privateKeyPath , false ) ; err != nil {
return err
}
if err = utils . WriteCertificateBytesToPEM ( csr , csrPath , true ) ; err != nil {
return err
}
return nil
}
2022-10-05 05:05:23 +00:00
func cryptoCertificateGenerateRunE ( cmd * cobra . Command , _ [ ] string , privateKey any ) ( err error ) {
2022-06-27 08:27:57 +00:00
var (
template , caCertificate , parent * x509 . Certificate
2022-10-05 05:05:23 +00:00
publicKey , caPrivateKey , signatureKey any
2022-06-27 08:27:57 +00:00
)
if publicKey = utils . PublicKeyFromPrivateKey ( privateKey ) ; publicKey == nil {
return fmt . Errorf ( "failed to obtain public key from private key" )
}
if caPrivateKey , caCertificate , err = cryptoGetCAFromCmd ( cmd ) ; err != nil {
return err
}
signatureKey = privateKey
if caPrivateKey != nil {
signatureKey = caPrivateKey
}
if template , err = cryptoGetCertificateFromCmd ( cmd ) ; err != nil {
return err
}
b := strings . Builder { }
b . WriteString ( "Generating Certificate\n\n" )
b . WriteString ( fmt . Sprintf ( "\tSerial: %x\n\n" , template . SerialNumber ) )
switch caCertificate {
case nil :
parent = template
b . WriteString ( "Signed By:\n\tSelf-Signed\n" )
default :
parent = caCertificate
b . WriteString ( fmt . Sprintf ( "Signed By:\n\t%s\n" , caCertificate . Subject . CommonName ) )
b . WriteString ( fmt . Sprintf ( "\tSerial: %x, Expires: %s\n" , caCertificate . SerialNumber , caCertificate . NotAfter . Format ( time . RFC3339 ) ) )
}
b . WriteString ( "\nSubject:\n" )
b . WriteString ( fmt . Sprintf ( "\tCommon Name: %s, Organization: %s, Organizational Unit: %s\n" , template . Subject . CommonName , template . Subject . Organization , template . Subject . OrganizationalUnit ) )
b . WriteString ( fmt . Sprintf ( "\tCountry: %v, Province: %v, Street Address: %v, Postal Code: %v, Locality: %v\n\n" , template . Subject . Country , template . Subject . Province , template . Subject . StreetAddress , template . Subject . PostalCode , template . Subject . Locality ) )
b . WriteString ( "Properties:\n" )
b . WriteString ( fmt . Sprintf ( "\tNot Before: %s, Not After: %s\n" , template . NotBefore . Format ( time . RFC3339 ) , template . NotAfter . Format ( time . RFC3339 ) ) )
b . WriteString ( fmt . Sprintf ( "\tCA: %v, CSR: %v, Signature Algorithm: %s, Public Key Algorithm: %s" , template . IsCA , false , template . SignatureAlgorithm , template . PublicKeyAlgorithm ) )
switch k := privateKey . ( type ) {
case * rsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( ", Bits: %d" , k . N . BitLen ( ) ) )
case * ecdsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( ", Elliptic Curve: %s" , k . Curve . Params ( ) . Name ) )
}
b . WriteString ( fmt . Sprintf ( "\n\tSubject Alternative Names: %s\n\n" , strings . Join ( cryptoSANsToString ( template . DNSNames , template . IPAddresses ) , ", " ) ) )
var (
privateKeyPath , certificatePath string
certificate [ ] byte
)
if privateKeyPath , certificatePath , err = cryptoGetWritePathsFromCmd ( cmd ) ; err != nil {
return err
}
b . WriteString ( "Output Paths:\n" )
b . WriteString ( fmt . Sprintf ( "\tPrivate Key: %s\n" , privateKeyPath ) )
b . WriteString ( fmt . Sprintf ( "\tCertificate: %s\n\n" , certificatePath ) )
fmt . Print ( b . String ( ) )
b . Reset ( )
if certificate , err = x509 . CreateCertificate ( rand . Reader , template , parent , publicKey , signatureKey ) ; err != nil {
return fmt . Errorf ( "failed to create certificate: %w" , err )
}
if err = utils . WriteKeyToPEM ( privateKey , privateKeyPath , false ) ; err != nil {
return err
}
if err = utils . WriteCertificateBytesToPEM ( certificate , certificatePath , false ) ; err != nil {
return err
}
return nil
}
2022-10-05 05:05:23 +00:00
func cryptoPairGenerateRunE ( cmd * cobra . Command , _ [ ] string , privateKey any ) ( err error ) {
2022-06-27 08:27:57 +00:00
var (
privateKeyPath , publicKeyPath string
pkcs8 bool
)
if pkcs8 , err = cmd . Flags ( ) . GetBool ( cmdFlagNamePKCS8 ) ; err != nil {
return err
}
if privateKeyPath , publicKeyPath , err = cryptoGetWritePathsFromCmd ( cmd ) ; err != nil {
return err
}
b := strings . Builder { }
b . WriteString ( "Generating key pair\n\n" )
switch k := privateKey . ( type ) {
case * rsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( "\tAlgorithm: RSA-%d %d bits\n\n" , k . Size ( ) , k . N . BitLen ( ) ) )
case * ecdsa . PrivateKey :
b . WriteString ( fmt . Sprintf ( "\tAlgorithm: ECDSA Curve %s\n\n" , k . Curve . Params ( ) . Name ) )
case ed25519 . PrivateKey :
b . WriteString ( "\tAlgorithm: Ed25519\n\n" )
}
b . WriteString ( "Output Paths:\n" )
b . WriteString ( fmt . Sprintf ( "\tPrivate Key: %s\n" , privateKeyPath ) )
b . WriteString ( fmt . Sprintf ( "\tPublic Key: %s\n\n" , publicKeyPath ) )
fmt . Print ( b . String ( ) )
b . Reset ( )
if err = utils . WriteKeyToPEM ( privateKey , privateKeyPath , pkcs8 ) ; err != nil {
return err
}
2022-10-05 05:05:23 +00:00
var publicKey any
2022-06-27 08:27:57 +00:00
if publicKey = utils . PublicKeyFromPrivateKey ( privateKey ) ; publicKey == nil {
return fmt . Errorf ( "failed to obtain public key from private key" )
}
if err = utils . WriteKeyToPEM ( publicKey , publicKeyPath , pkcs8 ) ; err != nil {
return err
}
return nil
}