x509ca.go 3.7 KB
Newer Older
ale's avatar
ale committed
1 2 3
package main

import (
ale's avatar
ale committed
4
	"context"
ale's avatar
ale committed
5 6 7 8 9 10 11 12
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
ale's avatar
ale committed
13
	"os"
ale's avatar
ale committed
14
	"syscall"
ale's avatar
ale committed
15 16

	"github.com/google/subcommands"
ale's avatar
ale committed
17 18
)

19 20
const filesModifiedExitStatus = 10

ale's avatar
ale committed
21
var (
22
	useCustomExitStatus = flag.Bool("use-exit-status", false, fmt.Sprintf("Use a custom exit status (%d) to indicate that certificates have been modified", filesModifiedExitStatus))
ale's avatar
ale committed
23 24 25
)

const (
ale's avatar
ale committed
26 27 28
	pemCertificateType        = "CERTIFICATE"
	pemECPrivateKeyType       = "EC PRIVATE KEY"
	pemCertificateRequestType = "CERTIFICATE REQUEST"
ale's avatar
ale committed
29 30 31
)

// MarshalCertificate encodes a certificate in PEM format.
ale's avatar
ale committed
32 33 34 35 36 37 38
func MarshalCertificate(cert *x509.Certificate) []byte {
	return pem.EncodeToMemory(&pem.Block{Type: pemCertificateType, Bytes: cert.Raw})
}

// MarshalCertificateRequest encodes a csr in PEM format.
func MarshalCertificateRequest(csr *x509.CertificateRequest) []byte {
	return pem.EncodeToMemory(&pem.Block{Type: pemCertificateRequestType, Bytes: csr.Raw})
ale's avatar
ale committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
}

// MarshalPrivateKey encodes a private key in PEM format.
func MarshalPrivateKey(priv *ecdsa.PrivateKey) ([]byte, error) {
	der, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return nil, err
	}
	data := pem.EncodeToMemory(&pem.Block{Type: pemECPrivateKeyType, Bytes: der})
	return data, nil
}

// UnmarshalCertificate reads a single X509 certificate encoded in PEM
// format from the given data.
func UnmarshalCertificate(data []byte) (*x509.Certificate, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, errors.New("no PEM block found in input")
	}
	if block.Type != pemCertificateType {
		return nil, errors.New("encoded object is not a certificate")
	}
	certs, err := x509.ParseCertificates(block.Bytes)
	if err != nil {
		return nil, err
	}
	return certs[0], nil
}

ale's avatar
ale committed
68 69 70 71 72 73 74 75 76 77 78 79
// UnmarshalCertificateRequest reads a PEM-encoded CSR.
func UnmarshalCertificateRequest(data []byte) (*x509.CertificateRequest, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, errors.New("no PEM block found in input")
	}
	if block.Type != pemCertificateRequestType {
		return nil, errors.New("encoded object is not a certificate request")
	}
	return x509.ParseCertificateRequest(block.Bytes)
}

ale's avatar
ale committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
// UnmarshalPrivateKey reads a PEM-encoded private key.
func UnmarshalPrivateKey(data []byte) (*ecdsa.PrivateKey, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, errors.New("no PEM block found in input")
	}
	if block.Type != pemECPrivateKeyType {
		return nil, fmt.Errorf("encoded object is not an EC private key (%s)", block.Type)
	}
	return x509.ParseECPrivateKey(block.Bytes)
}

func loadCertificate(path string) (*x509.Certificate, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return UnmarshalCertificate(data)
}

func loadPrivateKey(path string) (*ecdsa.PrivateKey, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return UnmarshalPrivateKey(data)
}

func saveCertificate(cert *x509.Certificate, path string) error {
ale's avatar
ale committed
109
	return ioutil.WriteFile(path, MarshalCertificate(cert), 0644)
ale's avatar
ale committed
110 111 112 113 114 115 116 117 118 119
}

func savePrivateKey(pkey *ecdsa.PrivateKey, path string) error {
	data, err := MarshalPrivateKey(pkey)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path, data, 0600)
}

ale's avatar
ale committed
120 121 122 123
func init() {
	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(subcommands.FlagsCommand(), "")
	subcommands.Register(subcommands.CommandsCommand(), "")
ale's avatar
ale committed
124 125
}

ale's avatar
ale committed
126 127
func exitStatus(changed, checkOnly bool) subcommands.ExitStatus {
	if !checkOnly && *useCustomExitStatus && changed {
128 129 130 131 132
		return subcommands.ExitStatus(filesModifiedExitStatus)
	}
	return subcommands.ExitSuccess
}

ale's avatar
ale committed
133 134 135
func main() {
	log.SetFlags(0)
	flag.Parse()
ale's avatar
ale committed
136 137
	syscall.Umask(077)
	os.Exit(int(subcommands.Execute(context.Background())))
ale's avatar
ale committed
138
}