aboutsummaryrefslogtreecommitdiff
path: root/certificate.go
blob: d065ab277f2bc617d0899237bdc581c31d1bbf54 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package pki

import (
  "crypto/rand"
  "crypto/x509"
  "crypto/x509/pkix"
  "encoding/pem"
  "fmt"
  "math/big"
  "net"
  "time"
)

// labels used in the pem file format to mark certificate sign requests and certificates
const (
  PemLabelCertificateRequest = "CERTIFICATE REQUEST"
  PemLabelCertificate        = "CERTIFICATE"
)

type (
  // 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
    CALength            int
    KeyUsage            x509.KeyUsage
  }
)

// Create a new set of certificate data.
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.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)
}

// 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
}

// Check if the certificate options have the required fields set.
func (co *CertificateOptions) Valid() error {
  if co.SerialNumber == nil { return fmt.Errorf("No serial number set!") }
  return nil
}