/
certifier.go
139 lines (123 loc) · 4.77 KB
/
certifier.go
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
package client
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
client2 "github.com/plgd-dev/kit/security/certManager/acme/client"
"github.com/go-acme/lego/certificate"
"github.com/plgd-dev/kit/security/generateCertificate"
"golang.org/x/crypto/ocsp"
)
// Constants for OCSP must staple
var (
tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05}
)
type certifier struct {
c *client2.Certifier
deviceID string
privateKey crypto.PrivateKey
}
func getCsr(deviceID string, domains []string, mustStaple bool, privateKey crypto.PrivateKey) (*x509.CertificateRequest, error) {
template, err := generateCertificate.NewIdentityCSRTemplate(deviceID)
if err != nil {
return nil, err
}
template.DNSNames = domains
if mustStaple {
template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
Id: tlsFeatureExtensionOID,
Value: ocspMustStapleFeature,
})
}
raw, err := x509.CreateCertificateRequest(rand.Reader, template, privateKey)
if err != nil {
return nil, err
}
return x509.ParseCertificateRequest(raw)
}
// Obtain tries to obtain a single certificate using all domains passed into it.
//
// This function will never return a partial certificate.
// If one domain in the list fails, the whole certificate will fail.
func (c *certifier) Obtain(request certificate.ObtainRequest) (*certificate.Resource, error) {
privateKey := request.PrivateKey
var err error
var privateKeyBuf []byte
if privateKey == nil {
ecPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
privateKeyBuf, err = x509.MarshalECPrivateKey(ecPrivateKey)
if err != nil {
return nil, err
}
privateKey = ecPrivateKey
privateKeyBuf = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privateKeyBuf})
}
csr, err := getCsr(c.deviceID, request.Domains, request.MustStaple, privateKey)
if err != nil {
return nil, err
}
res, err := c.ObtainForCSR(*csr, request.Bundle)
if err != nil {
return nil, err
}
res.PrivateKey = privateKeyBuf
return res, nil
}
// ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
//
// The domains are inferred from the SubjectAltNames, if any.
// The private key for this CSR is not required.
//
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
//
// This function will never return a partial certificate.
// If one domain in the list fails, the whole certificate will fail.
func (c *certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*certificate.Resource, error) {
return c.c.ObtainForCSR(csr, bundle)
}
// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
func (c *certifier) Revoke(cert []byte) error {
return c.c.Revoke(cert)
}
// Renew takes a Resource and tries to renew the certificate.
//
// If the renewal process succeeds, the new certificate will ge returned in a new CertResource.
// Please be aware that this function will return a new certificate in ANY case that is not an error.
// If the server does not provide us with a new cert on a GET request to the CertURL
// this function will start a new-cert flow where a new certificate gets generated.
//
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
//
// For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
func (c *certifier) Renew(certRes certificate.Resource, bundle, mustStaple bool) (*certificate.Resource, error) {
return c.c.Renew(certRes, bundle, mustStaple)
}
// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response,
// the parsed response, and an error, if any.
//
// The returned []byte can be passed directly into the OCSPStaple property of a tls.Certificate.
// If the bundle only contains the issued certificate,
// this function will try to get the issuer certificate from the IssuingCertificateURL in the certificate.
//
// If the []byte and/or ocsp.Response return values are nil, the OCSP status may be assumed OCSPUnknown.
func (c *certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) {
return c.c.GetOCSP(bundle)
}
// Get attempts to fetch the certificate at the supplied URL.
// The URL is the same as what would normally be supplied at the Resource's CertURL.
//
// The returned Resource will not have the PrivateKey and CSR fields populated as these will not be available.
//
// If bundle is true, the Certificate field in the returned Resource includes the issuer certificate.
func (c *certifier) Get(url string, bundle bool) (*certificate.Resource, error) {
return c.c.Get(url, bundle)
}