2015-03-15 20:06:47 +01:00
|
|
|
package pki
|
|
|
|
|
|
|
|
import (
|
2015-03-25 20:36:21 +01:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
2016-09-18 17:39:29 +02:00
|
|
|
"io"
|
2015-03-25 20:36:21 +01:00
|
|
|
"math/big"
|
|
|
|
"net"
|
|
|
|
"time"
|
2015-03-15 20:06:47 +01:00
|
|
|
)
|
|
|
|
|
2015-03-25 20:29:10 +01:00
|
|
|
// labels used in the pem file format to mark certificate sign requests and certificates
|
2015-03-16 17:10:46 +01:00
|
|
|
const (
|
2015-03-25 20:36:21 +01:00
|
|
|
PemLabelCertificateRequest = "CERTIFICATE REQUEST"
|
|
|
|
PemLabelCertificate = "CERTIFICATE"
|
2015-03-16 17:10:46 +01:00
|
|
|
)
|
2015-03-15 20:06:47 +01:00
|
|
|
|
|
|
|
type (
|
2015-03-25 20:36:21 +01:00
|
|
|
// Use CertificateData to fill in the minimum data you need to create a certificate
|
|
|
|
// sign request.
|
|
|
|
CertificateData struct {
|
|
|
|
Subject pkix.Name
|
|
|
|
|
|
|
|
DNSNames []string
|
|
|
|
EmailAddresses []string
|
|
|
|
IPAddresses []net.IP
|
|
|
|
}
|
|
|
|
|
|
|
|
// Certificate is an alias on the x509.Certificate to add some methods.
|
|
|
|
Certificate x509.Certificate
|
|
|
|
// CertificateRequest is an alias on the x509.CertificateRequest to add some methods.
|
|
|
|
CertificateRequest x509.CertificateRequest
|
|
|
|
|
|
|
|
// CertificateOptions is used to provide the necessary information to create
|
|
|
|
// a certificate from a certificate sign request.
|
|
|
|
CertificateOptions struct {
|
|
|
|
SerialNumber *big.Int
|
|
|
|
NotBefore time.Time
|
|
|
|
NotAfter time.Time // Validity bounds.
|
|
|
|
IsCA bool
|
|
|
|
// how many sub ca are allowed between this ca and the end/final certificate
|
|
|
|
// if it is -1, then no limit will be set
|
2015-03-30 20:44:31 +02:00
|
|
|
CALength int
|
|
|
|
KeyUsage x509.KeyUsage // for what can the certificate be used
|
|
|
|
KeyExtendedUsage []x509.ExtKeyUsage // extended usage for the certificate
|
2015-04-01 21:17:42 +02:00
|
|
|
CRLUrls []string
|
2015-03-25 20:36:21 +01:00
|
|
|
}
|
2015-03-15 20:06:47 +01:00
|
|
|
)
|
|
|
|
|
2015-03-25 20:29:10 +01:00
|
|
|
// Create a new set of certificate data.
|
2015-03-15 21:38:04 +01:00
|
|
|
func NewCertificateData() *CertificateData {
|
2015-03-25 20:36:21 +01:00
|
|
|
return &CertificateData{Subject: pkix.Name{}}
|
2015-03-15 21:38:04 +01:00
|
|
|
}
|
|
|
|
|
2015-03-15 20:45:35 +01:00
|
|
|
// 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) {
|
2015-03-25 20:36:21 +01:00
|
|
|
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)
|
2015-03-15 20:45:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load a certificate sign request from its asn1 representation.
|
|
|
|
func LoadCertificateSignRequest(raw []byte) (*CertificateRequest, error) {
|
2015-03-25 20:36:21 +01:00
|
|
|
csr, err := x509.ParseCertificateRequest(raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return (*CertificateRequest)(csr), nil
|
2015-03-15 20:45:35 +01:00
|
|
|
}
|
|
|
|
|
2017-05-12 15:27:44 +02:00
|
|
|
// ToPem returns a pem.Block representing the CertificateRequest.
|
|
|
|
func (c *CertificateRequest) ToPem() (pem.Block, error) {
|
|
|
|
return pem.Block{Type: PemLabelCertificateRequest, Bytes: c.Raw}, nil
|
|
|
|
}
|
|
|
|
|
2015-03-15 20:45:35 +01:00
|
|
|
// Return the certificate sign request as a pem block.
|
2016-09-18 17:39:29 +02:00
|
|
|
func (c *CertificateRequest) MarshalPem() (io.WriterTo, error) {
|
2017-05-12 15:27:44 +02:00
|
|
|
if block, err := c.ToPem(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
return marshalledPemBlock(pem.EncodeToMemory(&block)), nil
|
|
|
|
}
|
2015-03-15 20:45:35 +01:00
|
|
|
}
|
|
|
|
|
2015-03-15 21:38:04 +01:00
|
|
|
// 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.
|
2015-03-16 16:48:42 +01:00
|
|
|
// Please also see the certificate options struct for information on mandatory fields.
|
2015-03-15 21:38:04 +01:00
|
|
|
// For more information, please read http://golang.org/pkg/crypto/x509/#CreateCertificate
|
2015-03-16 16:48:42 +01:00
|
|
|
func (c *CertificateRequest) ToCertificate(private_key PrivateKey,
|
2015-03-25 20:36:21 +01:00
|
|
|
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
|
2015-03-30 20:44:31 +02:00
|
|
|
template.ExtKeyUsage = cert_opts.KeyExtendedUsage
|
2015-04-01 21:17:42 +02:00
|
|
|
template.CRLDistributionPoints = cert_opts.CRLUrls
|
2015-03-25 20:36:21 +01:00
|
|
|
template.IsCA = cert_opts.IsCA
|
|
|
|
if cert_opts.IsCA {
|
|
|
|
template.BasicConstraintsValid = true
|
|
|
|
}
|
|
|
|
if cert_opts.CALength >= 0 {
|
|
|
|
template.MaxPathLen = cert_opts.CALength
|
|
|
|
template.MaxPathLenZero = true
|
|
|
|
template.BasicConstraintsValid = true
|
|
|
|
}
|
|
|
|
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)
|
2015-03-15 21:38:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load a certificate from its asn1 representation.
|
|
|
|
func LoadCertificate(raw []byte) (*Certificate, error) {
|
2015-03-25 20:36:21 +01:00
|
|
|
cert, err := x509.ParseCertificate(raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return (*Certificate)(cert), nil
|
2015-03-15 20:45:35 +01:00
|
|
|
}
|
2015-03-16 16:48:42 +01:00
|
|
|
|
2015-03-16 17:10:46 +01:00
|
|
|
// marshal the certificate to a pem block
|
2016-09-29 21:52:25 +02:00
|
|
|
func (c *Certificate) MarshalPem() (io.WriterTo, error) {
|
2017-05-12 15:27:44 +02:00
|
|
|
if block, err := c.ToPem(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
return marshalledPemBlock(pem.EncodeToMemory(&block)), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToPem returns the pem block of the certificate.
|
|
|
|
func (c *Certificate) ToPem() (pem.Block, error) {
|
|
|
|
return pem.Block{Type: PemLabelCertificate, Bytes: c.Raw}, nil
|
2015-03-16 17:10:46 +01:00
|
|
|
}
|
|
|
|
|
2015-03-25 20:29:10 +01:00
|
|
|
// Check if the certificate options have the required fields set.
|
2015-03-16 16:48:42 +01:00
|
|
|
func (co *CertificateOptions) Valid() error {
|
2015-03-25 20:36:21 +01:00
|
|
|
if co.SerialNumber == nil {
|
|
|
|
return fmt.Errorf("No serial number set!")
|
|
|
|
}
|
|
|
|
return nil
|
2015-03-16 16:48:42 +01:00
|
|
|
}
|