0
0
Fork 0

finally add certificate sign request generation

This adds finally a way to create certificate sign requests. There are
still some options missing, but it is coming together.

With the next step, the ccertificate data container will probably be put
into the pki library.
This commit is contained in:
Gibheer 2015-03-05 21:37:52 +01:00
parent 2954be520d
commit 52102b0f24
3 changed files with 151 additions and 13 deletions

28
certificate_data.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"crypto/x509"
"crypto/x509/pkix"
"net"
)
type (
certificateData struct {
Subject pkix.Name
DnsNames []string
EmailAddresses []string
IpAddresses []net.IP
}
)
func (c *certificateData) GenerateCSR() *x509.CertificateRequest {
csr := &x509.CertificateRequest{}
csr.Subject = c.Subject
csr.DNSNames = c.DnsNames
csr.IPAddresses = c.IpAddresses
csr.EmailAddresses = c.EmailAddresses
return csr
}

115
flags.go
View File

@ -5,11 +5,15 @@ package main
import (
"crypto/elliptic"
"crypto/x509/pkix"
"encoding/base64"
"flag"
"fmt"
"io"
"net"
"os"
"reflect"
"strings"
"github.com/gibheer/pki"
)
@ -26,17 +30,22 @@ var (
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
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
}
}
// a container go gather all incoming flags for further processing
@ -64,9 +73,10 @@ type (
Output io.WriteCloser
// signature from the args
Signature []byte
// private key specific stuff
PrivateKeyGenerationFlags privateKeyGenerationFlags
// a certificate filled with the parameters
CertificateData certificateData
}
privateKeyGenerationFlags struct {
@ -253,3 +263,84 @@ func (f *Flags) parseSignature() error {
if err != nil { return err }
return nil
}
// add the certificate fields to the flags
func (f *Flags) AddCertificateFields() {
f.check_list = append(f.check_list, f.parseCertificateFields)
f.flagset.StringVar(
&f.flag_container.certificateFlags.manual.serialNumber,
"serial", "1", "unique serial number of the CA");
f.flagset.StringVar(
&f.flag_container.certificateFlags.manual.commonName,
"common-name", "", "common name of the entity to certify");
f.flagset.StringVar(
&f.flag_container.certificateFlags.manual.dnsNames,
"dns-names", "", "comma separated list of alternative fqdn entries for the entity");
f.flagset.StringVar(
&f.flag_container.certificateFlags.manual.emailAddresses,
"email-address", "", "comma separated list of alternative email entries for the entity");
f.flagset.StringVar(
&f.flag_container.certificateFlags.manual.ipAddresses,
"ip-address", "", "comma separated list of alternative ip entries for the entity");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.Country,
"country", "", "comma separated list of countries the entitiy resides in");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.Organization,
"organization", "", "comma separated list of organizations the entity belongs to");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.OrganizationalUnit,
"organization-unit", "", "comma separated list of organization units or departments the entity belongs to");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.Locality,
"locality", "", "comma separated list of localities or cities the entity resides in");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.Province,
"province", "", "comma separated list of provinces the entity resides in");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.StreetAddress,
"street-address", "", "comma separated list of street addresses the entity resides in");
f.flagset.StringVar(
&f.flag_container.certificateFlags.automatic.PostalCode,
"postal-code", "", "comma separated list of postal codes of the localities");
}
// parse the certificate fields into a raw certificate
func (f *Flags) parseCertificateFields() error {
f.Flags.CertificateData = certificateData{Subject: pkix.Name{}}
// convert the automatic flags
container_type := reflect.ValueOf(&f.flag_container.certificateFlags.automatic).Elem()
cert_data_type := reflect.ValueOf(&f.Flags.CertificateData.Subject).Elem()
for _, field := range []string{"Country", "Organization", "OrganizationalUnit",
"Locality", "Province", "StreetAddress", "PostalCode"} {
field_value := container_type.FieldByName(field).String()
if field_value == "" { continue }
target := cert_data_type.FieldByName(field)
target.Set(reflect.ValueOf(strings.Split(field_value, ",")))
}
// convert the manual flags
data := &f.Flags.CertificateData
raw_data := f.flag_container.certificateFlags.manual
data.Subject.SerialNumber = raw_data.serialNumber
data.Subject.CommonName = raw_data.commonName
if raw_data.dnsNames != "" {
data.DnsNames = strings.Split(raw_data.dnsNames, ",")
}
if raw_data.emailAddresses != "" {
data.EmailAddresses = strings.Split(raw_data.emailAddresses, ",")
}
if raw_data.ipAddresses == "" { return nil }
raw_ips := strings.Split(raw_data.ipAddresses, ",")
data.IpAddresses = make([]net.IP, len(raw_ips))
for i, ip := range raw_ips {
data.IpAddresses[i] = net.ParseIP(ip)
if data.IpAddresses[i] == nil {
return fmt.Errorf("'%s' is not a valid IP", ip)
}
}
return nil
}

21
main.go
View File

@ -2,7 +2,10 @@ package main
import (
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
@ -25,7 +28,7 @@ func main() {
case "create-public": create_public_key()
case "sign-input": sign_input()
case "verify-signature": verify_input()
// case "create-cert-sign": create_sign_request()
case "create-cert-sign": create_sign_request()
// case "sign-request": sign_request()
case "help": print_modules()
// case "info": info_on_file()
@ -115,6 +118,22 @@ func verify_input() {
os.Exit(1)
}
// create a certificate sign request
func create_sign_request() {
fs := NewFlags("create-cert-sign")
fs.AddPrivateKey()
fs.AddOutput()
fs.AddCertificateFields()
fs.Parse(program_args())
csrt := fs.Flags.CertificateData.GenerateCSR()
csr, err := x509.CreateCertificateRequest(rand.Reader, csrt, fs.Flags.PrivateKey.PrivateKey())
if err != nil { crash_with_help(2, "Could not create certificate sign request: %s", err) }
pem_block := &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr}
err = pem.Encode(fs.Flags.Output, pem_block)
if err != nil { crash_with_help(2, "Encoding didn't work: %s", err) }
}
// print the module help
func print_modules() {
fmt.Printf(`Usage: %s command args