lasso/cmd/lasso/cmd/gencert.go

125 lines
3.5 KiB
Go

/*
Copyright © 2021 Arsen Musayelyan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"time"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
// gencertCmd represents the gencert command
var gencertCmd = &cobra.Command{
Use: "gencert <cert file> <key file>",
Short: "Generate self-signed TLS certificate for master server",
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 2 {
cmd.Usage()
os.Exit(1)
}
// Generate an ed25519 key from rand reader
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
// Get current time to use for cert creation time
notBefore := time.Now()
// Get expiration time 10 years from now
notAfter := notBefore.Add(time.Hour * 24 * 365 * 10)
// Set limit for serial number
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
// Create random serial number
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
log.Fatal().Err(err).Msg("Failed to generate serial number")
}
// Get local IP
ip, err := localIP()
if err != nil {
log.Fatal().Err(err).Msg("Error getting local IP")
}
// Create x509 certificate template
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"Lasso"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IPAddresses: []net.IP{net.ParseIP(ip)},
}
// Create certificate
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv)
if err != nil {
log.Fatal().Err(err).Msg("Failed to create certificate")
}
// Create certificate output file
certOut, err := os.Create(args[0])
if err != nil {
log.Fatal().Err(err).Msg("Failed to open cert file for writing")
}
// Enode certificate as PEM file
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
log.Fatal().Err(err).Msg("Failed to write data to cert file")
}
// Close certificate file
certOut.Close()
log.Info().Str("path", args[0]).Msg("Wrote cert file")
// Create key output file
keyOut, err := os.OpenFile(args[1], os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatal().Err(err).Msg("Failed to open key file for writing")
return
}
// Encode private key as PKCS8
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
log.Fatal().Err(err).Msg("Unable to marshal private key file")
}
// Encode private key as PEM
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
log.Fatal().Err(err).Msg("Failed to write data to key file")
}
// Close key file
keyOut.Close()
log.Info().Str("path", args[1]).Msg("Wrote key file")
},
}
func init() {
rootCmd.AddCommand(gencertCmd)
}