324 lines
9.9 KiB
Go
324 lines
9.9 KiB
Go
package main
|
|
|
|
// This file contains all code to create certificates.
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gibheer/pki"
|
|
)
|
|
|
|
var (
|
|
// the possible valid key usages to check against the commandline
|
|
ValidKeyUsages = map[string]x509.KeyUsage{
|
|
"digitalsignature": x509.KeyUsageDigitalSignature,
|
|
"contentcommitment": x509.KeyUsageContentCommitment,
|
|
"keyencipherment": x509.KeyUsageKeyEncipherment,
|
|
"dataencipherment": x509.KeyUsageDataEncipherment,
|
|
"keyagreement": x509.KeyUsageKeyAgreement,
|
|
"certsign": x509.KeyUsageCertSign,
|
|
"crlsign": x509.KeyUsageCRLSign,
|
|
"encipheronly": x509.KeyUsageEncipherOnly,
|
|
"decipheronly": x509.KeyUsageDecipherOnly,
|
|
}
|
|
// the valid extended key usages, to check against the commandline
|
|
ValidExtKeyUsages = map[string]x509.ExtKeyUsage{
|
|
"any": x509.ExtKeyUsageAny,
|
|
"serverauth": x509.ExtKeyUsageServerAuth,
|
|
"clientauth": x509.ExtKeyUsageClientAuth,
|
|
"codesigning": x509.ExtKeyUsageCodeSigning,
|
|
"emailprotection": x509.ExtKeyUsageEmailProtection,
|
|
"ipsecendsystem": x509.ExtKeyUsageIPSECEndSystem,
|
|
"ipsectunnel": x509.ExtKeyUsageIPSECTunnel,
|
|
"ipsecuser": x509.ExtKeyUsageIPSECUser,
|
|
"timestamping": x509.ExtKeyUsageTimeStamping,
|
|
"ocspsigning": x509.ExtKeyUsageOCSPSigning,
|
|
"microsoftservergatedcrypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
|
"netscapeservergatedcrypto": x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
|
}
|
|
|
|
CmdCreateCert = &Command{
|
|
Use: "create-cert",
|
|
Short: "create a certificate from a sign request",
|
|
Long: "Create a certificate based on a certificate sign request.",
|
|
Example: "create-cert -private-key=foo.ecdsa -csr=foo.csr",
|
|
Run: create_cert,
|
|
}
|
|
// certificate specific creation stuff
|
|
FlagCertificateGeneration pki.CertificateOptions
|
|
)
|
|
|
|
type (
|
|
// holds all certificate related flags, which need parsing afterwards
|
|
certiticateRequestRawFlags struct {
|
|
manual struct {
|
|
serialNumber string // the serial number for the cert
|
|
commonName string // the common name used in the cert
|
|
dnsNames string // all alternative names in the certificate (comma separated list)
|
|
ipAddresses string // all IP addresses in the certificate (comma separated list)
|
|
emailAddresses string // alternative email addresses
|
|
}
|
|
automatic struct {
|
|
Country string // the country names which should end up in the cert (comma separated list)
|
|
Organization string // the organization names (comma separated list)
|
|
OrganizationalUnit string // the organizational units (comma separated list)
|
|
Locality string // the city or locality (comma separated list)
|
|
Province string // the province name (comma separated list)
|
|
StreetAddress string // the street addresses of the organization (comma separated list)
|
|
PostalCode string // the postal codes of the locality
|
|
}
|
|
}
|
|
|
|
// the raw flags collected through flags
|
|
certGenerationRaw struct {
|
|
serial int64
|
|
notBefore string
|
|
notAfter string
|
|
isCA bool
|
|
length int
|
|
caPath string // path to the ca file if isCA is false
|
|
keyUsage string // comma separated list of key usages
|
|
extKeyUsage string // comma separated list of extended key usages
|
|
crlUrl string // comma separated list of crl urls
|
|
}
|
|
)
|
|
|
|
// add flag to load certificate flags
|
|
func InitFlagCert(cmd *Command) {
|
|
cmd.Flags().Int64Var(&flagContainer.certGeneration.serial, "serial", 0, "serial number of all certificates")
|
|
cmd.Flags().BoolVar(&flagContainer.certGeneration.isCA, "is-ca", false, "check if the resulting certificate should be a ca")
|
|
cmd.Flags().IntVar(
|
|
&flagContainer.certGeneration.
|
|
length,
|
|
"length",
|
|
0,
|
|
"the number of certificates allowed in the chain between this cert and the end certificate",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&flagContainer.certGeneration.notBefore,
|
|
"not-before",
|
|
time.Now().Format(time.RFC3339),
|
|
"time before the certificate is not valid in RFC3339 format (default now)",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&flagContainer.certGeneration.notAfter,
|
|
"not-after",
|
|
time.Now().Add(time.Duration(180*24*time.Hour)).Format(time.RFC3339),
|
|
"time after which the certificate is not valid in RFC3339 format (default now + 180 days)",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&flagContainer.certGeneration.keyUsage,
|
|
"key-usage", "",
|
|
"comma separated list of key usages",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&flagContainer.certGeneration.extKeyUsage,
|
|
"ext-key-usage", "",
|
|
"comma separated list of extended key usage flags",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&flagContainer.certGeneration.crlUrl,
|
|
"crl-url", "",
|
|
"comma separated list where crl lists can be found",
|
|
)
|
|
}
|
|
|
|
// create a certificate
|
|
func create_cert(cmd *Command, args []string) {
|
|
err := checkFlags(checkPrivateKey, checkOutput, checkCSR, checkCertFlags, checkFlagsEither(checkIsCA, checkCA))
|
|
if err != nil {
|
|
crash_with_help(cmd, ErrorFlagInput, "Flags invalid: %s", err)
|
|
}
|
|
|
|
var cert *pki.Certificate
|
|
if FlagCertificateGeneration.IsCA && FlagCertificate == nil {
|
|
cert, err = FlagCertificateSignRequest.ToCertificate(
|
|
FlagPrivateKey,
|
|
FlagCertificateGeneration,
|
|
nil,
|
|
)
|
|
} else if !FlagCertificateGeneration.IsCA && FlagCertificate != nil {
|
|
cert, err = FlagCertificateSignRequest.ToCertificate(
|
|
FlagPrivateKey,
|
|
FlagCertificateGeneration,
|
|
FlagCertificate,
|
|
)
|
|
} else {
|
|
crash_with_help(cmd, ErrorFlagInput, "Usage of 'is-ca' and 'ca' is invalid.")
|
|
}
|
|
|
|
if err != nil {
|
|
crash_with_help(cmd, ErrorProgram, "Error generating certificate: %s", err)
|
|
}
|
|
pem_block, err := cert.MarshalPem()
|
|
if err != nil {
|
|
crash_with_help(cmd, ErrorProgram, "Error when marshalling to pem: %s", err)
|
|
}
|
|
_, err = pem_block.WriteTo(FlagOutput)
|
|
if err != nil {
|
|
crash_with_help(cmd, ErrorProgram, "Could not write to output: %s", err)
|
|
}
|
|
}
|
|
|
|
// parse the certificate data
|
|
func checkCertFlags() error {
|
|
FlagCertificateGeneration.IsCA = flagContainer.certGeneration.isCA
|
|
FlagCertificateGeneration.CALength = flagContainer.certGeneration.length
|
|
FlagCertificateGeneration.SerialNumber = big.NewInt(flagContainer.certGeneration.serial)
|
|
|
|
var err error
|
|
if notbefore := flagContainer.certGeneration.notBefore; notbefore != "" {
|
|
FlagCertificateGeneration.NotBefore, err = parseTimeRFC3339(notbefore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if notafter := flagContainer.certGeneration.notAfter; notafter != "" {
|
|
FlagCertificateGeneration.NotAfter, err = parseTimeRFC3339(notafter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := convertCertKeyUsage(); err != nil {
|
|
return err
|
|
}
|
|
if err := convertCertExtKeyUsage(); err != nil {
|
|
return err
|
|
}
|
|
if err := convertCertCrlUrl(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// check if the flag is enabled to make the certificate a ca
|
|
func checkIsCA() error {
|
|
if FlagCertificateGeneration.IsCA {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Not selected to be a CA")
|
|
}
|
|
|
|
// parse the key usage string
|
|
func convertCertKeyUsage() error {
|
|
if keyUstr := flagContainer.certGeneration.keyUsage; keyUstr != "" {
|
|
keyUarr := strings.Split(keyUstr, ",")
|
|
var keyUresult x509.KeyUsage
|
|
for _, usage := range keyUarr {
|
|
if value, ok := ValidKeyUsages[strings.ToLower(usage)]; ok {
|
|
keyUresult = keyUresult | value
|
|
} else {
|
|
return fmt.Errorf("unsupported key usage '%s'", usage)
|
|
}
|
|
}
|
|
FlagCertificateGeneration.KeyUsage = keyUresult
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parse the extended key usage flags
|
|
func convertCertExtKeyUsage() error {
|
|
if eKeyUstr := flagContainer.certGeneration.extKeyUsage; eKeyUstr != "" {
|
|
eKeyUarr := strings.Split(eKeyUstr, ",")
|
|
eKeyUResult := make([]x509.ExtKeyUsage, 0)
|
|
for _, usage := range eKeyUarr {
|
|
if value, ok := ValidExtKeyUsages[strings.ToLower(usage)]; ok {
|
|
eKeyUResult = append(eKeyUResult, value)
|
|
} else {
|
|
return fmt.Errorf("unsupported extended key usage '%s'", usage)
|
|
}
|
|
}
|
|
FlagCertificateGeneration.KeyExtendedUsage = eKeyUResult
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parse the crl urls
|
|
func convertCertCrlUrl() error {
|
|
if str := flagContainer.certGeneration.crlUrl; str != "" {
|
|
FlagCertificateGeneration.CRLUrls = strings.Split(str, ",")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// add flag to load a certificate
|
|
func InitFlagCA(cmd *Command) {
|
|
cmd.Flags().StringVar(&flagContainer.caPath, "ca", "", "path to the certificate authority")
|
|
}
|
|
|
|
// parse the certificate authority
|
|
func checkCA() error {
|
|
rest, err := ioutil.ReadFile(flagContainer.caPath)
|
|
if err != nil {
|
|
return fmt.Errorf("Error reading certificate authority: %s", err)
|
|
}
|
|
|
|
var ca_asn1 []byte
|
|
var block *pem.Block
|
|
for len(rest) > 0 {
|
|
block, rest = pem.Decode(rest)
|
|
if block != nil && block.Type == pki.PemLabelCertificate {
|
|
ca_asn1 = block.Bytes
|
|
break
|
|
}
|
|
}
|
|
if len(ca_asn1) == 0 {
|
|
return fmt.Errorf("No certificate in '%s' found.", flagContainer.caPath)
|
|
}
|
|
|
|
ca, err := pki.LoadCertificate(ca_asn1)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid certificate: %s", err)
|
|
}
|
|
FlagCertificate = ca
|
|
return nil
|
|
}
|
|
|
|
// add flag to load certificate sign request
|
|
func InitFlagCSR(cmd *Command) {
|
|
cmd.Flags().StringVar(&flagContainer.signRequestPath, "csr", "", "path to the certificate sign request")
|
|
}
|
|
|
|
// parse the certificate sign request
|
|
func checkCSR() error {
|
|
rest, err := ioutil.ReadFile(flagContainer.signRequestPath)
|
|
if err != nil {
|
|
return fmt.Errorf("Error reading certificate sign request: %s", err)
|
|
}
|
|
|
|
var csr_asn1 []byte
|
|
var block *pem.Block
|
|
for len(rest) > 0 {
|
|
block, rest = pem.Decode(rest)
|
|
if block.Type == pki.PemLabelCertificateRequest {
|
|
csr_asn1 = block.Bytes
|
|
break
|
|
}
|
|
}
|
|
if len(csr_asn1) == 0 {
|
|
return fmt.Errorf(
|
|
"No certificate sign request found in %s",
|
|
flagContainer.signRequestPath,
|
|
)
|
|
}
|
|
|
|
csr, err := pki.LoadCertificateSignRequest(csr_asn1)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid certificate sign request: %s", err)
|
|
}
|
|
FlagCertificateSignRequest = csr
|
|
return nil
|
|
}
|
|
|
|
// parse the string as a RFC3339 time
|
|
func parseTimeRFC3339(tr string) (time.Time, error) {
|
|
return time.Parse(time.RFC3339, tr)
|
|
}
|