-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsign.go
More file actions
171 lines (151 loc) · 5.06 KB
/
Copy pathsign.go
File metadata and controls
171 lines (151 loc) · 5.06 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package certkit
import (
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
"math/big"
"time"
)
// ErrCAKeyMismatch indicates the signing CA private key does not match the CA certificate.
var (
ErrCAKeyMismatch = errors.New("CA key does not match CA certificate")
errSelfSignedSignerNil = errors.New("creating self-signed certificate: signer is required")
errSignCSRNilCSR = errors.New("signing CSR: CSR is required")
errSignCSRNilCACert = errors.New("signing CSR: CA certificate is required")
errSignCSRNilCAKey = errors.New("signing CSR: CA key is required")
)
// SelfSignedInput contains parameters for self-signed certificate generation.
type SelfSignedInput struct {
// Signer is the private key used to sign the certificate.
Signer crypto.Signer
// Subject is the distinguished name for the certificate.
Subject pkix.Name
// Days is the validity period in days (default: 3650).
Days int
// IsCA sets the CA basic constraint.
IsCA bool
}
// CreateSelfSigned generates a self-signed certificate from the given input.
// The certificate is created with KeyUsageCertSign and KeyUsageCRLSign when
// IsCA is true, and KeyUsageDigitalSignature otherwise.
func CreateSelfSigned(input SelfSignedInput) (*x509.Certificate, error) {
if input.Signer == nil {
return nil, errSelfSignedSignerNil
}
days := input.Days
if days <= 0 {
days = 3650
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, fmt.Errorf("generating serial number: %w", err)
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: serial,
Subject: input.Subject,
NotBefore: now,
NotAfter: now.Add(time.Duration(days) * 24 * time.Hour),
IsCA: input.IsCA,
BasicConstraintsValid: true,
}
if input.IsCA {
template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
} else {
template.KeyUsage = x509.KeyUsageDigitalSignature
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, input.Signer.Public(), input.Signer)
if err != nil {
return nil, fmt.Errorf("creating self-signed certificate: %w", err)
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
return nil, fmt.Errorf("parsing created certificate: %w", err)
}
return cert, nil
}
// SignCSRInput contains parameters for signing a CSR with a CA.
type SignCSRInput struct {
// CSR is the certificate signing request to sign.
CSR *x509.CertificateRequest
// CACert is the CA certificate used as the issuer.
CACert *x509.Certificate
// CAKey is the CA's private key used to sign.
CAKey crypto.Signer
// Days is the validity period in days (default: 365).
Days int
// CopySANs copies DNS names, IP addresses, email addresses, and URIs
// from the CSR to the issued certificate.
CopySANs bool
}
// SignCSR signs a certificate signing request using the provided CA certificate
// and key. Returns the issued certificate.
func SignCSR(input SignCSRInput) (*x509.Certificate, error) {
if input.CSR == nil {
return nil, errSignCSRNilCSR
}
if input.CACert == nil {
return nil, errSignCSRNilCACert
}
if input.CAKey == nil {
return nil, errSignCSRNilCAKey
}
caKeyMatches, err := KeyMatchesCert(input.CAKey, input.CACert)
if err != nil {
return nil, fmt.Errorf("validating CA certificate and key: %w", err)
}
if !caKeyMatches {
return nil, fmt.Errorf("validating CA certificate and key: %w", ErrCAKeyMismatch)
}
if err := input.CSR.CheckSignature(); err != nil {
return nil, fmt.Errorf("verifying CSR signature: %w", err)
}
days := input.Days
if days <= 0 {
days = 365
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, fmt.Errorf("generating serial number: %w", err)
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: serial,
Subject: input.CSR.Subject,
NotBefore: now,
NotAfter: now.Add(time.Duration(days) * 24 * time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
if input.CopySANs {
template.DNSNames = input.CSR.DNSNames
template.IPAddresses = input.CSR.IPAddresses
template.EmailAddresses = input.CSR.EmailAddresses
template.URIs = input.CSR.URIs
// Preserve OtherName SANs via raw extensions
for _, ext := range input.CSR.Extensions {
if ext.Id.Equal(oidSubjectAltName) {
template.ExtraExtensions = append(template.ExtraExtensions, ext)
// When using ExtraExtensions for SANs, nil out the typed fields
// to avoid Go generating a duplicate SAN extension.
template.DNSNames = nil
template.IPAddresses = nil
template.EmailAddresses = nil
template.URIs = nil
break
}
}
}
certDER, err := x509.CreateCertificate(rand.Reader, template, input.CACert, input.CSR.PublicKey, input.CAKey)
if err != nil {
return nil, fmt.Errorf("signing CSR: %w", err)
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
return nil, fmt.Errorf("parsing issued certificate: %w", err)
}
return cert, nil
}