forked from cloudflare/cfssl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
selfsign.go
143 lines (119 loc) · 3.57 KB
/
selfsign.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
140
141
142
143
// Package selfsign implements certificate selfsigning. This is very
// dangerous and should never be used in production.
package selfsign
import (
"crypto"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"math"
"math/big"
"time"
"github.com/cloudflare/cfssl/config"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/signer"
)
const threeMonths = 2190 * time.Hour
// parseCertificateRequest takes an incoming certificate request and
// builds a certificate template from it.
func parseCertificateRequest(priv crypto.Signer, csrBytes []byte) (template *x509.Certificate, err error) {
csr, err := x509.ParseCertificateRequest(csrBytes)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
return
}
err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
return
}
template = &x509.Certificate{
Subject: csr.Subject,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SignatureAlgorithm: signer.DefaultSigAlgo(priv),
}
return
}
type subjectPublicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString
}
// Sign creates a new self-signed certificate.
func Sign(priv crypto.Signer, csrPEM []byte, profile *config.SigningProfile) ([]byte, error) {
if profile == nil {
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("no profile for self-signing"))
}
p, _ := pem.Decode(csrPEM)
if p == nil || p.Type != "CERTIFICATE REQUEST" {
return nil, cferr.New(cferr.CSRError, cferr.BadRequest)
}
template, err := parseCertificateRequest(priv, p.Bytes)
if err != nil {
return nil, err
}
pub := template.PublicKey
encodedpub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
var subPKI subjectPublicKeyInfo
_, err = asn1.Unmarshal(encodedpub, &subPKI)
if err != nil {
return nil, err
}
pubhash := sha1.New()
pubhash.Write(subPKI.SubjectPublicKey.Bytes)
var (
eku []x509.ExtKeyUsage
ku x509.KeyUsage
expiry time.Duration
crlURL, ocspURL string
)
// The third value returned from Usages is a list of unknown key usages.
// This should be used when validating the profile at load, and isn't used
// here.
ku, eku, _ = profile.Usages()
expiry = profile.Expiry
if ku == 0 && len(eku) == 0 {
err = cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
return nil, err
}
if expiry == 0 {
expiry = threeMonths
}
now := time.Now()
serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.Unknown, err)
return nil, err
}
template.SerialNumber = serialNumber
template.NotBefore = now.Add(-5 * time.Minute).UTC()
template.NotAfter = now.Add(expiry).UTC()
template.KeyUsage = ku
template.ExtKeyUsage = eku
template.BasicConstraintsValid = true
template.IsCA = profile.CAConstraint.IsCA
template.SubjectKeyId = pubhash.Sum(nil)
if ocspURL != "" {
template.OCSPServer = []string{ocspURL}
}
if crlURL != "" {
template.CRLDistributionPoints = []string{crlURL}
}
if len(profile.IssuerURL) != 0 {
template.IssuingCertificateURL = profile.IssuerURL
}
cert, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv)
if err != nil {
return nil, err
}
cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
return cert, nil
}