0
0
Fork 0

redesign cli

This is a major rebuilding of the CLI. The library part is split out
into pkilib and the cli handles only the communication with the user,
I/O and the library.
The API will still look the same, but the code should be much better to
grasp. Instead of repeating everything, more will be grouped together
and reused.
This commit is contained in:
Gibheer 2015-02-15 01:34:25 +01:00
parent 2f9126dc6a
commit 16eb14db9f
10 changed files with 282 additions and 700 deletions

View File

@ -1,118 +0,0 @@
package main
// create a sign request needed for the final certificate
import (
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"flag"
"io"
"net"
"os"
"reflect"
"regexp"
)
type (
SignFlags struct {
PrivateKeyPath string // path to the private key
Output string // path where to store the CSR
BaseAttributes pkix.Name
DNSNames []string // alternative names to the BaseAttributes.CommonName
IPAddresses []net.IP // alternative IP addresses
private_key crypto.Signer
output_stream io.Writer // the output stream for the CSR
}
)
var (
COMMA_SPLIT = regexp.MustCompile(`,[[:space:]]?`)
)
// create a sign request with a private key
func create_sign_request() {
flags := parse_sign_flags()
flags.private_key = load_private_key(flags.PrivateKeyPath)
stream, err := open_output_stream(flags.Output)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
}
defer stream.Close()
flags.output_stream = stream
if err = create_csr(flags); err != nil {
fmt.Fprintln(os.Stderr, "Error when generating CSR: ", err)
os.Exit(3)
}
}
// parse the flags to create a certificate sign request
func parse_sign_flags() SignFlags {
dns_names := "" // string to hold the alternative names
ips := "" // string to hold the alternative ips
var container struct {
Country, Organization, OrganizationalUnit, Locality, Province,
StreetAddress, PostalCode string
}
flags := SignFlags{}
fs := flag.NewFlagSet("create-cert-sign", flag.ExitOnError)
fs.StringVar(&flags.PrivateKeyPath, "private-key", "", "path to the private key file")
fs.StringVar(&flags.Output, "output", "STDOUT", "path where the generated csr should be stored")
flags.BaseAttributes = pkix.Name{}
fs.StringVar(&flags.BaseAttributes.CommonName, "common-name", "", "the name of the resource")
fs.StringVar(&flags.BaseAttributes.SerialNumber, "serial", "1", "serial number for the request")
fs.StringVar(&dns_names, "names", "", "alternative names (comma separated)")
fs.StringVar(&ips, "ips", "", "alternative IPs (comma separated)")
fs.StringVar(&container.Country, "country", "", "country of the organization")
fs.StringVar(&container.Organization, "organization", "", "the name of the organization")
fs.StringVar(&container.OrganizationalUnit, "org-unit", "", "the organizational unit")
fs.StringVar(&container.Locality, "city", "", "the city or locality")
fs.StringVar(&container.Province, "province", "", "the province")
fs.StringVar(&container.StreetAddress, "street", "", "the street address for the cert")
fs.StringVar(&container.PostalCode, "postal-code", "", "the postal code of the city")
fs.Parse(os.Args[2:])
// convert array flags to config structs
if dns_names != "" {
flags.DNSNames = COMMA_SPLIT.Split(dns_names, -1)
}
if ips != "" {
tmp_ips := COMMA_SPLIT.Split(ips, -1)
for _, sip := range tmp_ips {
flags.IPAddresses = append(flags.IPAddresses, net.ParseIP(sip))
}
}
container_type := reflect.ValueOf(container)
config_type := reflect.ValueOf(&flags.BaseAttributes).Elem()
for i := 0; i < container_type.NumField(); i++ {
field := container_type.Field(i)
new_field := config_type.FieldByName(container_type.Type().Field(i).Name)
new_field.Set(reflect.ValueOf(COMMA_SPLIT.Split(field.String(), -1)))
}
return flags
}
// generate the csr and print into flags.output_stream
func create_csr(flags SignFlags) (error) {
csr_template := &x509.CertificateRequest{
Subject: flags.BaseAttributes,
DNSNames: flags.DNSNames,
IPAddresses: flags.IPAddresses,
}
csr_raw, err := x509.CreateCertificateRequest(rand.Reader, csr_template, flags.private_key)
if err != nil { return err }
block := &pem.Block{Type: TypeLabelCSR, Bytes: csr_raw}
pem.Encode(flags.output_stream, block)
return nil
}

View File

@ -1,84 +0,0 @@
package main
import (
"bytes"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"net"
"testing"
)
type CSRTest struct {
ShouldBe []string
Set func(*SignFlags)
Fetch func(*x509.CertificateRequest) []string
}
const (
RAW_PRIVATE_KEY = `-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEFkAEFc5264Yo7Xo+yj3ZwaqdffTphGT3/8Q+pvi4ULmXaFiGoTkR5X
lKnlRUEp0I4Ra9U7GjLDtFLwTaLzdXuUT6AHBgUrgQQAI6GBiQOBhgAEAdW0usq0
zEzvhR0u5ZSbOXRzg+TbICZGfOLy9KpKfz6I6suFOAO7f3fwDNOqMfyYUhtenMz7
T/BKArg+v58UWHrwAb/UeI4l+OMOoMHYtNNO4nAjTdyY8yFSFY5syzKEYIBzUoLM
VSfuxBk5ZS2J478X1Vxacq03keDeAY43Oc80XBih
-----END EC PRIVATE KEY-----`
)
func SetupTest() (*SignFlags, *bytes.Buffer) {
p, _ := pem.Decode([]byte(RAW_PRIVATE_KEY))
buf := bytes.NewBuffer(make([]byte, 0))
flags := &SignFlags{
private_key: load_private_key_ecdsa(p),
output_stream: buf,
}
return flags, buf
}
func TestCSRGeneration(t *testing.T) {
tests := []CSRTest {
{
[]string{"foo"},
func(f *SignFlags) { f.BaseAttributes.CommonName = "foo" },
func(c *x509.CertificateRequest) []string { return []string{c.Subject.CommonName} },
}, {
[]string{"foo.com", "bar.com", "baz.com"},
func(f *SignFlags) { f.DNSNames = []string{ "foo.com", "bar.com", "baz.com" }},
func(c *x509.CertificateRequest) []string { return c.DNSNames },
},
{
[]string{"127.0.0.1", "192.168.0.1"},
func(f *SignFlags) { f.IPAddresses = []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("192.168.0.1") }},
func(c *x509.CertificateRequest) []string {
res := make([]string, 0)
for _, ip := range c.IPAddresses {
res = append(res, ip.String())
}
return res
},
},
}
for _, test := range tests {
flags, io := SetupTest()
test.Set(flags)
create_csr(*flags)
res, _ := ioutil.ReadAll(io)
raw, _ := pem.Decode(res)
csr, _ := x509.ParseCertificateRequest(raw.Bytes)
if !diff(test.ShouldBe, test.Fetch(csr)) {
t.Logf("Expected: %v\nbut got: %v", test.ShouldBe, test.Fetch(csr))
t.Fail()
}
}
}
func diff(a, b []string) bool {
if len(a) != len(b) { return false }
for i, e := range a {
if e != b[i] { return false }
}
return true
}

168
flags.go Normal file
View File

@ -0,0 +1,168 @@
package main
// This file handles the complete parameter assignment, as some parameters are
// often used by multiple functions.
import (
"crypto/elliptic"
"flag"
"fmt"
"io"
"os"
"github.com/gibheer/pkilib"
)
const (
RsaLowerLength = 2048
RsaUpperLength = 16384
)
var (
EcdsaCurves = []int{224, 256, 384, 521}
)
type (
// holds all certificate related flags, which need parsing afterwards
certFlagsContainer struct {
serialNumber int // 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)
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
}
// a container go gather all incoming flags for further processing
paramContainer struct {
outputPath string // path to output whatever is generated
cryptType string // type of something (private key)
length int // the length of something (private key)
privateKeyPath string // path to the private key
publicKeyPath string // path to the public key
signRequestPath string // path to the certificate sign request
certificateFlags *certFlagsContainer // container for certificate related flags
}
// a container for the refined flags
flagSet struct {
PrivateKey pkilib.PrivateKey
Output io.WriteCloser
// private key specific stuff
PrivateKeyGenerationFlags privateKeyGenerationFlags
}
privateKeyGenerationFlags struct {
Type string // type of the private key (rsa, ecdsa)
Curve elliptic.Curve // curve for ecdsa
Size int // bitsize for rsa
}
Flags struct {
flagset *flag.FlagSet // the flagset reference for printing the help
flag_container *paramContainer
Flags *flagSet // the end result of the flag setting
check_list []flagCheck // list of all checks
}
flagCheck func()(error)
)
// create a new flag handler with the name of the subfunction
func NewFlags(method_name string) *Flags {
return &Flags{
Flags: &flagSet{},
flagset: flag.NewFlagSet(method_name, flag.ExitOnError),
check_list: make([]flagCheck, 0),
flag_container: &paramContainer{},
}
}
// check all parameters for validity
func (f *Flags) Parse(options []string) error {
f.flagset.Parse(options)
for _, check := range f.check_list {
// TODO handle error in a betetr way (output specific help, not command help)
if err := check(); err != nil { return err }
}
return nil
}
// add the private key option to the requested flags
func (f *Flags) AddPrivateKey() {
f.check_list = append(f.check_list, f.parsePrivateKey)
f.flagset.StringVar(&f.flag_container.privateKeyPath, "private-key", "", "path to the private key")
}
// check the private key flag and load the private key
func (f *Flags) parsePrivateKey() error {
// check permissions of private key file
info, err := os.Stat(f.flag_container.privateKeyPath)
if err != nil { return fmt.Errorf("Error reading private key: %s", err) }
if info.Mode().Perm().String()[4:] != "------" {
return fmt.Errorf("private key file modifyable by others!")
}
pk, err := ReadPrivateKeyFile(f.flag_container.privateKeyPath)
if err != nil { return fmt.Errorf("Error reading private key: %s", err) }
f.Flags.PrivateKey = pk
return nil
}
// add the output parameter to the checklist
func (f *Flags) AddOutput() {
f.check_list = append(f.check_list, f.parseOutput)
f.flagset.StringVar(&f.flag_container.outputPath, "output", "STDOUT", "path to the output or STDOUT")
}
// parse the output parameter and open the file handle
func (f *Flags) parseOutput() error {
if f.flag_container.outputPath == "STDOUT" {
f.Flags.Output = os.Stdout
return nil
}
var err error
f.Flags.Output, err = os.OpenFile(
f.flag_container.outputPath,
os.O_WRONLY | os.O_APPEND | os.O_CREATE, // do not kill users files!
0600,
)
if err != nil { return err }
return nil
}
// This function adds the private key generation flags.
func (f *Flags) AddPrivateKeyGenerationFlags() {
f.check_list = append(f.check_list, f.parsePrivateKeyGenerationFlags)
f.flagset.StringVar(&f.flag_container.cryptType, "type", "ecdsa", "the type of the private key (ecdsa, rsa)")
f.flagset.IntVar(
&f.flag_container.length,
"length", 521,
fmt.Sprintf("%d - %d for rsa; %v for ecdsa", RsaLowerLength, RsaUpperLength, EcdsaCurves),
)
}
func (f *Flags) parsePrivateKeyGenerationFlags() error {
pk_type := f.flag_container.cryptType
f.Flags.PrivateKeyGenerationFlags.Type = pk_type
switch pk_type {
case "ecdsa":
switch f.flag_container.length {
case 224: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P224()
case 256: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P256()
case 384: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P384()
case 521: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P521()
default: return fmt.Errorf("Curve %d unknown!", f.flag_container.length)
}
case "rsa": f.Flags.PrivateKeyGenerationFlags.Size = f.flag_container.length
default: return fmt.Errorf("Type %s is unknown!", pk_type)
}
return nil
}

41
io.go Normal file
View File

@ -0,0 +1,41 @@
package main
// handle all io and de/encoding of data
import (
"encoding/pem"
"errors"
"io/ioutil"
)
var (
ErrBlockNotFound = errors.New("block not found")
)
// load a pem section from a file
func readSectionFromFile(path, btype string) ([]byte, error) {
raw, err := readFile(path)
if err != nil { return raw, err }
return decodeSection(raw, btype)
}
// read a file completely and report possible errors
func readFile(path string) ([]byte, error) {
raw, err := ioutil.ReadFile(path)
if err != nil { return EmptyByteArray, err }
return raw, nil
}
// decode a pem encoded file and search for the specified section
func decodeSection(data []byte, btype string) ([]byte, error) {
rest := data
for len(rest) > 0 {
var block *pem.Block
block, rest = pem.Decode(rest)
if block.Type == btype {
return block.Bytes, nil
}
}
return EmptyByteArray, ErrBlockNotFound
}

91
main.go
View File

@ -2,69 +2,74 @@ package main
import (
"fmt"
"io"
"os"
"path/filepath"
)
const (
RsaLowerLength = 2048
RsaUpperLength = 4096
TypeLabelRSA = "RSA PRIVATE KEY"
TypeLabelECDSA = "EC PRIVATE KEY"
TypeLabelCSR = "CERTIFICATE REQUEST"
TypeLabelPubKey = "PUBLIC KEY"
"github.com/gibheer/pkilib"
)
var (
EcdsaLength = []int{224, 256, 384, 521}
EmptyByteArray = make([]byte, 0)
)
//const (
// RsaLowerLength = 2048
// RsaUpperLength = 4096
// TypeLabelRSA = "RSA PRIVATE KEY"
// TypeLabelECDSA = "EC PRIVATE KEY"
// TypeLabelCSR = "CERTIFICATE REQUEST"
// TypeLabelPubKey = "PUBLIC KEY"
//)
//
//var (
// EcdsaLength = []int{224, 256, 384, 521}
//)
//
func main() {
if len(os.Args) == 1 {
crash_with_help(1, "No module selected!")
}
switch os.Args[1] {
case "create-private": create_private_key()
case "create-cert-sign": create_sign_request()
case "create-public": create_public_key()
case "help": print_modules()
case "info": info_on_file()
case "sign-request": sign_request()
case "sign-input": sign_input()
case "verify-signature": verify_signature()
// case "create-cert-sign": create_sign_request()
// case "help": print_modules()
// case "info": info_on_file()
// case "sign-request": sign_request()
// case "sign-input": sign_input()
// case "verify-signature": verify_signature()
default: crash_with_help(1, "Command not supported!")
}
}
// get information on file (private key, sign request, certificate, ...)
func info_on_file() {}
// sign a certificate request to create a new certificate
func sign_request() {}
// create a private key
func create_private_key() {
fs := NewFlags("create-private")
fs.AddOutput()
fs.AddPrivateKeyGenerationFlags()
err := fs.Parse(program_args())
if err != nil { crash_with_help(1, fmt.Sprintf("%s", err)) }
// open stream for given path
func open_output_stream(path string) (io.WriteCloser, error) {
switch path {
case "STDOUT": return os.Stdout, nil
case "STDERR": return os.Stderr, nil
default: return open_stream(path, os.O_WRONLY | os.O_CREATE | os.O_TRUNC)
var pk pkilib.Pemmer
switch fs.Flags.PrivateKeyGenerationFlags.Type {
case "ecdsa": pk, err = pkilib.NewPrivateKeyEcdsa(fs.Flags.PrivateKeyGenerationFlags.Curve)
case "rsa": pk, err = pkilib.NewPrivateKeyRsa(fs.Flags.PrivateKeyGenerationFlags.Size)
}
if err != nil { crash_with_help(2, fmt.Sprintf("%s", err)) }
marsh_pem, err := pk.MarshalPem()
if err != nil { crash_with_help(2, fmt.Sprintf("%s", err)) }
_, err = marsh_pem.WriteTo(fs.Flags.Output)
if err != nil { crash_with_help(2, fmt.Sprintf("%s", err)) }
}
func open_input_stream(path string) (io.ReadCloser, error) {
switch path {
case "STDIN": return os.Stdin, nil
default: return open_stream(path, os.O_RDONLY)
}
}
// create a public key derived from a private key
func create_public_key() {
fs := NewFlags("create-public")
fs.AddPrivateKey()
err := fs.Parse(program_args())
if err != nil { crash_with_help(1, fmt.Sprintf("%s", err)) }
func open_stream(path string, flags int) (io.ReadWriteCloser, error) {
var err error
output_stream, err := os.OpenFile(path, flags, 0600)
if err != nil {
return nil, err
}
return output_stream, nil
fmt.Println(fs.Flags.PrivateKey.Public())
}
// print the module help
@ -76,15 +81,21 @@ where 'command' is one of:
create-cert-sign create a new certificate sign request
help show this help
info get info on a file
sign sign a certificate request
sign-request sign a certificate request
sign-input sign a message with a private key
verify-signature verify a signature
`, filepath.Base(os.Args[0]))
fmt.Println()
}
// crash and provide a helpful message
func crash_with_help(code int, message string) {
fmt.Fprintln(os.Stderr, message)
print_modules()
os.Exit(code)
}
// return the arguments to the program
func program_args() []string {
return os.Args[2:]
}

View File

@ -1,145 +1,33 @@
package main
// generate an ecdsa or rsa private key
import (
"crypto"
"crypto/elliptic"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"errors"
"github.com/gibheer/pkilib"
)
type (
CreateFlags struct {
CryptType string // rsa or ecdsa
CryptLength int // the bit length
Output string // a path or stream to output the private key to
output_stream io.WriteCloser // the actual stream to the output
}
const (
TypeLabelRSA = "RSA PRIVATE KEY"
TypeLabelECDSA = "EC PRIVATE KEY"
)
// create a new private key
func create_private_key() {
flags := parse_create_flags()
var (
ErrNoPKFound = errors.New("no private key found")
)
var err error
flags.output_stream, err = open_output_stream(flags.Output)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
// Read the private key from the path and try to figure out which type of key it
// might be.
func ReadPrivateKeyFile(path string) (pkilib.PrivateKey, error) {
raw_pk, err := readSectionFromFile(path, TypeLabelECDSA)
if err == nil {
pk, err := pkilib.LoadPrivateKeyEcdsa(raw_pk)
if err != nil { return nil, err }
return pk, nil
}
defer flags.output_stream.Close()
switch flags.CryptType {
case "rsa": create_private_key_rsa(flags)
case "ecdsa": create_private_key_ecdsa(flags)
default: crash_with_help(2, fmt.Sprintf("%s not supported!", flags.CryptType))
raw_pk, err = readSectionFromFile(path, TypeLabelRSA)
if err == nil {
pk, err := pkilib.LoadPrivateKeyRsa(raw_pk)
if err != nil { return nil, err }
return pk, nil
}
}
// generate a rsa private key
func create_private_key_rsa(flags CreateFlags) {
if flags.CryptLength < 2048 {
crash_with_help(2, "Length is smaller than 2048!")
}
priv, err := rsa.GenerateKey( rand.Reader, flags.CryptLength)
if err != nil {
fmt.Fprintln(os.Stderr, "Error: ", err)
os.Exit(3)
}
marshal := x509.MarshalPKCS1PrivateKey(priv)
block := &pem.Block{Type: TypeLabelRSA, Bytes: marshal}
pem.Encode(flags.output_stream, block)
}
// generate a ecdsa private key
func create_private_key_ecdsa(flags CreateFlags) {
var curve elliptic.Curve
switch flags.CryptLength {
case 224: curve = elliptic.P224()
case 256: curve = elliptic.P256()
case 384: curve = elliptic.P384()
case 521: curve = elliptic.P521()
default: crash_with_help(2, "Unsupported crypt length!")
}
priv, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
fmt.Fprintln(os.Stderr, "Error: ", err)
os.Exit(3)
}
marshal, err := x509.MarshalECPrivateKey(priv)
if err != nil {
crash_with_help(2, fmt.Sprintf("Problems marshalling the private key: %s", err))
}
block := &pem.Block{Type: TypeLabelECDSA, Bytes: marshal}
pem.Encode(flags.output_stream, block)
}
// parse the flags to create a private key
func parse_create_flags() CreateFlags {
flags := CreateFlags{}
fs := flag.NewFlagSet("create-private", flag.ExitOnError)
fs.StringVar(&flags.CryptType, "type", "ecdsa", "which type to use to encrypt key (rsa, ecdsa)")
fs.IntVar(&flags.CryptLength, "length", 521, fmt.Sprintf(
"%i - %i for rsa; %v for ecdsa", RsaLowerLength, RsaUpperLength, EcdsaLength,))
fs.StringVar(&flags.Output, "output", "STDOUT", "filename to store the private key")
fs.Parse(os.Args[2:])
return flags
}
// load the private key stored at `path`
func load_private_key(path string) crypto.Signer {
if path == "" {
crash_with_help(2, "No path to private key supplied!")
}
file, err := os.Open(path)
if err != nil {
crash_with_help(3, fmt.Sprintf("Error when opening private key: %s", err))
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
crash_with_help(3, fmt.Sprintf("Error when reading private key: %s", err))
}
block, _ := pem.Decode(data)
if block.Type == TypeLabelRSA {
return load_private_key_rsa(block)
} else if block.Type == TypeLabelECDSA {
return load_private_key_ecdsa(block)
} else {
crash_with_help(2, "No valid private key file! Only RSA and ECDSA keys are allowed!")
return nil
}
}
// parse rsa private key
func load_private_key_rsa(block *pem.Block) crypto.Signer {
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
}
return key
}
// parse ecdsa private key
func load_private_key_ecdsa(block *pem.Block) crypto.Signer {
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
}
return key
return nil, ErrNoPKFound
}

View File

@ -1,50 +0,0 @@
package main
// create a public key from a private key
import (
"crypto/x509"
"encoding/pem"
"flag"
"fmt"
"io"
"os"
)
type (
PublicKeyFlags struct {
PrivateKeyPath string
Output string
output_stream io.WriteCloser // the actual stream to the output
}
)
func create_public_key() {
var err error
flags := parse_public_key_flags()
flags.output_stream, err = open_output_stream(flags.Output)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
}
defer flags.output_stream.Close()
priv_key := load_private_key(flags.PrivateKeyPath)
marshal, err := x509.MarshalPKIXPublicKey(priv_key.Public())
if err != nil {
crash_with_help(2, fmt.Sprintf("Problems marshalling the public key: %s", err))
}
block := &pem.Block{Type: TypeLabelPubKey, Bytes: marshal}
pem.Encode(flags.output_stream, block)
}
func parse_public_key_flags() PublicKeyFlags {
flags := PublicKeyFlags{}
fs := flag.NewFlagSet("create-public", flag.ExitOnError)
fs.StringVar(&flags.PrivateKeyPath, "private-key", "", "path to the private key file")
fs.StringVar(&flags.Output, "output", "STDOUT", "path where the generated public key should be stored")
fs.Parse(os.Args[2:])
return flags
}

View File

@ -1,103 +0,0 @@
package main
import (
"crypto"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
// "crypto/ecdsa"
// "crypto/rsa"
)
type (
SignInputFlags struct {
Message string // the message to sign
MessageStream string // the message stream to sign
PrivateKeyPath string // path to the private key
Output string // a path or stream to output the private key to
Format string // the format of the output
private_key crypto.Signer
output_stream io.Writer // the output stream for the CSR
input_stream io.Reader // the input stream to read the message from
}
)
func sign_input() {
flags := parse_sign_input_flags()
if flags.Message != "" && flags.MessageStream != "" {
crash_with_help(2, "Only message or message file can be signed!")
}
flags.private_key = load_private_key(flags.PrivateKeyPath)
output_stream, err := open_output_stream(flags.Output)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
}
flags.output_stream = output_stream
defer output_stream.Close()
if flags.MessageStream != "" {
input_stream, err := open_input_stream(flags.MessageStream)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when opening stream %s: %s", flags.MessageStream, err))
}
flags.input_stream = input_stream
defer input_stream.Close()
}
if err := create_signature(flags); err != nil {
fmt.Fprintln(os.Stderr, "Error when creating signature", err)
os.Exit(3)
}
}
func parse_sign_input_flags() SignInputFlags {
flags := SignInputFlags{}
fs := flag.NewFlagSet("sign-input", flag.ExitOnError)
fs.StringVar(&flags.PrivateKeyPath, "private-key", "", "path to the private key file")
fs.StringVar(&flags.Output, "output", "STDOUT", "path where the generated signature should be stored (STDOUT, STDERR, file)")
fs.StringVar(&flags.Message, "message", "", "the message to sign")
fs.StringVar(&flags.MessageStream, "message-stream", "STDIN", "the path to the stream to sign (file, STDIN)")
fs.StringVar(&flags.Format, "format", "base64", "the output format (binary, base64)")
fs.Parse(os.Args[2:])
return flags
}
func create_signature(flags SignInputFlags) error {
var message []byte
var err error
if flags.MessageStream != "" {
message, err = ioutil.ReadAll(flags.input_stream)
if err != nil { return err }
} else {
message = []byte(flags.Message)
}
// compute sha256 of the message
hash := sha256.New()
length, _ := hash.Write(message)
if length != len(message) { return errors.New("Error when creating hash over message!") }
// create signature of the hash using the private key
signature, err := flags.private_key.Sign(
rand.Reader,
hash.Sum([]byte("")),
nil,
)
if err != nil { return err }
if flags.Format == "base64" {
flags.output_stream.Write([]byte(base64.StdEncoding.EncodeToString(signature)))
} else {
flags.output_stream.Write(signature)
}
flags.output_stream.Write([]byte("\n"))
return nil
}

View File

@ -1,10 +0,0 @@
package main
import (
)
// sign a certificate request to create a new certificate
func sign_request() {
}

View File

@ -1,161 +0,0 @@
package main
// verify a signature generated with a private key
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"encoding/base64"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
"strings"
)
type (
VerifySignatureFlags struct {
Message string // the message to sign
MessageStream string // the path to the input stream
PublicKeyPath string // path to the private key
Signature string // a path or stream to output the private key to
SignatureStream string // read signature from an input stream
Format string // the format of the signature
message_stream io.Reader // the message stream
signature_stream io.Reader // the signature stream
}
// struct to load the signature into (which is basically two bigint in byte form)
Signature struct {
R, S *big.Int
}
)
func verify_signature() {
flags := parse_verify_signature_flags()
if flags.SignatureStream == flags.MessageStream &&
( flags.Message == "" && flags.Signature == "") {
crash_with_help(2, "Signature and Message stream can't be the same source!")
}
// open streams
if flags.Message == "" && flags.MessageStream != "" {
message_stream, err := open_input_stream(flags.MessageStream)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when opening stream %s: %s", flags.MessageStream, err))
}
defer message_stream.Close()
flags.message_stream = message_stream
}
if flags.Signature == "" && flags.SignatureStream != "" {
signature_stream, err := open_input_stream(flags.SignatureStream)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when opening stream %s: %s", flags.SignatureStream, err))
}
defer signature_stream.Close()
flags.signature_stream = signature_stream
}
public_key, err := load_public_key_ecdsa(flags.PublicKeyPath)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when loading public key: %s", err))
}
signature, err := load_signature(flags)
if err != nil {
crash_with_help(2, fmt.Sprintf("Error when loading the signature: %s", err))
}
message, err := load_message(flags)
hash := sha256.New()
hash.Write([]byte(message))
success := ecdsa.Verify(public_key, hash.Sum(nil), signature.R, signature.S)
fmt.Println(success)
}
// parse the parameters
func parse_verify_signature_flags() VerifySignatureFlags {
flags := VerifySignatureFlags{}
fs := flag.NewFlagSet("verify-signature", flag.ExitOnError)
fs.StringVar(&flags.PublicKeyPath, "public-key", "", "path to the public key file")
fs.StringVar(&flags.Signature, "signature", "", "path where the signature file can be found")
fs.StringVar(&flags.SignatureStream, "signature-stream", "", "the path to the stream of the signature (file, STDIN)")
fs.StringVar(&flags.Format, "format", "auto", "the input format of the signature (auto, binary, base64)")
fs.StringVar(&flags.Message, "message", "", "the message to validate")
fs.StringVar(&flags.MessageStream, "message-stream", "STDIN", "the path to the stream to validate (file, STDIN)")
fs.Parse(os.Args[2:])
return flags
}
// load the private key from pem file
func load_public_key_ecdsa(path string) (*ecdsa.PublicKey, error) {
public_key_file, err := os.Open(path)
if err != nil { return nil, err }
public_key_pem, err := ioutil.ReadAll(public_key_file)
if err != nil { return nil, err }
public_key_file.Close()
block, _ := pem.Decode(public_key_pem)
if block.Type != TypeLabelPubKey {
return nil, errors.New(fmt.Sprintf("No public key found in %s", path))
}
public_key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { return nil, err }
return public_key.(*ecdsa.PublicKey), nil
}
// parse the signature from asn1 file
func load_signature(flags VerifySignatureFlags) (*Signature, error) {
var signature_raw []byte
var err error
if flags.Message != "" {
signature_raw = []byte(flags.Message)
} else {
signature_raw, err = ioutil.ReadAll(flags.signature_stream)
if err != nil { return nil, err }
}
switch strings.ToLower(flags.Format) {
case "auto":
sig, err := load_signature_base64(signature_raw)
if err != nil {
sig, err = load_signature_binary(signature_raw)
if err != nil { return nil, err }
return sig, nil
}
return sig, nil
case "base64": return load_signature_base64(signature_raw)
case "binary": return load_signature_binary(signature_raw)
default: return nil, errors.New("Unknown format!")
}
}
// convert the signature from base64 into a signature
func load_signature_base64(signature_raw []byte) (*Signature, error) {
asn1_sig, err := base64.StdEncoding.DecodeString(string(signature_raw))
if err != nil { return nil, err }
return load_signature_binary(asn1_sig)
}
// convert the signature from asn1 into a signature
func load_signature_binary(signature_raw []byte) (*Signature, error) {
var signature Signature
_, err := asn1.Unmarshal(signature_raw, &signature)
if err != nil { return nil, err }
return &signature, nil
}
// load the message from a stream or the parameter
func load_message(flags VerifySignatureFlags) (string, error) {
if flags.Message != "" { return flags.Message, nil }
message, err := ioutil.ReadAll(flags.message_stream)
if err != nil { return "", err }
return string(message), nil
}