diff options
Diffstat (limited to 'certificate.go')
-rw-r--r-- | certificate.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/certificate.go b/certificate.go new file mode 100644 index 0000000..b6fa252 --- /dev/null +++ b/certificate.go @@ -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 +} |