0
0
Fork 0

Merge branch 'certificate'

This adds the API to generate certificates in the same way certificate
sign requests were built.

It works but still lacks some features and fields.
This commit is contained in:
Gibheer 2015-03-16 17:11:28 +01:00
commit 2c43111aec
2 changed files with 172 additions and 0 deletions

126
certificate.go Normal file
View File

@ -0,0 +1,126 @@
package pki
import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"time"
)
const (
PemLabelCertificateRequest = "CERTIFICATE REQUEST"
PemLabelCertificate = "CERTIFICATE"
)
type (
CertificateData struct {
Subject pkix.Name
DNSNames []string
EmailAddresses []string
IPAddresses []net.IP
}
Certificate x509.Certificate
CertificateRequest x509.CertificateRequest
CertificateOptions struct {
SerialNumber *big.Int
NotBefore time.Time
NotAfter time.Time // Validity bounds.
KeyUsage x509.KeyUsage
}
)
func NewCertificateData() *CertificateData {
return &CertificateData{Subject: pkix.Name{}}
}
// Create a certificate sign request from the input data and the private key of
// the request creator.
func (c *CertificateData) ToCertificateRequest(private_key PrivateKey) (*CertificateRequest, error) {
csr := &x509.CertificateRequest{}
csr.Subject = c.Subject
csr.DNSNames = c.DNSNames
csr.IPAddresses = c.IPAddresses
csr.EmailAddresses = c.EmailAddresses
csr_asn1, err := x509.CreateCertificateRequest(rand.Reader, csr, private_key.PrivateKey())
if err != nil { return nil, err }
return LoadCertificateSignRequest(csr_asn1)
}
// Load a certificate sign request from its asn1 representation.
func LoadCertificateSignRequest(raw []byte) (*CertificateRequest, error) {
csr, err := x509.ParseCertificateRequest(raw)
if err != nil { return nil, err }
return (*CertificateRequest)(csr), nil
}
// Return the certificate sign request as a pem block.
func (c *CertificateRequest) MarshalPem() (marshalledPemBlock, error) {
block := &pem.Block{Type: PemLabelCertificateRequest, Bytes: c.Raw}
return pem.EncodeToMemory(block), nil
}
// Convert the certificate sign request to a certificate using the private key
// of the signer and the certificate of the signer.
// If the certificate is null, the sign request will be used to sign itself.
// Please also see the certificate options struct for information on mandatory fields.
// For more information, please read http://golang.org/pkg/crypto/x509/#CreateCertificate
func (c *CertificateRequest) ToCertificate(private_key PrivateKey,
cert_opts CertificateOptions, ca *Certificate) (*Certificate, error) {
if err := cert_opts.Valid(); err != nil { return nil, err }
template := &x509.Certificate{}
template.Subject = c.Subject
template.DNSNames = c.DNSNames
template.IPAddresses = c.IPAddresses
template.EmailAddresses = c.EmailAddresses
// if no ca is given, we have to set IsCA to self sign
if ca == nil {
template.IsCA = true
}
template.NotBefore = cert_opts.NotBefore
template.NotAfter = cert_opts.NotAfter
template.KeyUsage = cert_opts.KeyUsage
template.SerialNumber = cert_opts.SerialNumber
var cert_asn1 []byte
var err error
// if we have no ca which can sign the cert, a self signed cert is wanted
// (or isn't it? Maybe we should split creation of the template? But that would be ugly)
if ca == nil {
cert_asn1, err = x509.CreateCertificate(rand.Reader, template, template, c.PublicKey, private_key.PrivateKey())
} else {
cert_asn1, err = x509.CreateCertificate(rand.Reader, template, (*x509.Certificate)(ca), c.PublicKey, private_key.PrivateKey())
}
if err != nil { return nil, err }
return LoadCertificate(cert_asn1)
}
// Load a certificate from its asn1 representation.
func LoadCertificate(raw []byte) (*Certificate, error) {
cert, err := x509.ParseCertificate(raw)
if err != nil { return nil, err }
return (*Certificate)(cert), nil
}
// marshal the certificate to a pem block
func (c *Certificate) MarshalPem() (marshalledPemBlock, error) {
block := &pem.Block{Type: PemLabelCertificate, Bytes: c.Raw}
return pem.EncodeToMemory(block), nil
}
func (co *CertificateOptions) Valid() error {
if co.SerialNumber == nil { return fmt.Errorf("No serial number set!") }
return nil
}

46
certificate_test.go Normal file
View File

@ -0,0 +1,46 @@
package pki
import (
"crypto/elliptic"
// "crypto/x509"
"crypto/x509/pkix"
"math/big"
"reflect"
"testing"
)
var (
TestCertificateData = CertificateData{
Subject: pkix.Name{CommonName: "foobar"},
DNSNames: []string{"foo.bar", "example.com"},
}
)
func TestCertificateCreation(t *testing.T) {
pk, err := NewPrivateKeyEcdsa(elliptic.P224())
if err != nil { t.Errorf("cert: creating private key failed: %s", err) }
csr, err := TestCertificateData.ToCertificateRequest(pk)
if err != nil { t.Errorf("cert: creating csr failed: %s", err) }
cert_opts := CertificateOptions{
// KeyUsage: x509.KeyUsageEncipherOnly | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
SerialNumber: big.NewInt(1),
}
cert, err := csr.ToCertificate(pk, cert_opts, nil)
if err != nil { t.Errorf("cert: creating cert failed: %s", err) }
if !fieldsAreSame(TestCertificateData, cert) {
t.Errorf("cert: Fields are not the same")
}
}
func fieldsAreSame(data CertificateData, cert *Certificate) bool {
if data.Subject.CommonName != cert.Subject.CommonName { return false }
if !reflect.DeepEqual(data.Subject.Country, cert.Subject.Country) { return false }
if !reflect.DeepEqual(data.DNSNames, cert.DNSNames) { return false }
if !reflect.DeepEqual(data.IPAddresses, cert.IPAddresses) { return false }
if !reflect.DeepEqual(data.EmailAddresses, cert.EmailAddresses) { return false }
return true
}