forked from hashicorp/consul
-
Notifications
You must be signed in to change notification settings - Fork 0
/
csr.go
145 lines (126 loc) · 4.16 KB
/
csr.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
144
145
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package connect
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"net"
"net/url"
"strings"
)
// SigAlgoForKey returns the preferred x509.SignatureAlgorithm for a given key
// based on it's type. If the key type is not supported we return
// ECDSAWithSHA256 on the basis that it will fail anyway and we've already type
// checked keys by the time we call this in general.
func SigAlgoForKey(key crypto.Signer) x509.SignatureAlgorithm {
if _, ok := key.(*rsa.PrivateKey); ok {
return x509.SHA256WithRSA
}
// We default to ECDSA but don't bother detecting invalid key types as we do
// that in lots of other places and it will fail anyway if we try to sign with
// an incompatible type.
return x509.ECDSAWithSHA256
}
// SigAlgoForKeyType returns the preferred x509.SignatureAlgorithm for a given
// key type string from configuration or an existing cert. If the key type is
// not supported we return ECDSAWithSHA256 on the basis that it will fail anyway
// and we've already type checked config by the time we call this in general.
func SigAlgoForKeyType(keyType string) x509.SignatureAlgorithm {
switch keyType {
case "rsa":
return x509.SHA256WithRSA
case "ec":
fallthrough
default:
return x509.ECDSAWithSHA256
}
}
// CreateCSR returns a CSR to sign the given service with SAN entries
// along with the PEM-encoded private key for this certificate.
func CreateCSR(uri CertURI, privateKey crypto.Signer,
dnsNames []string, ipAddresses []net.IP, extensions ...pkix.Extension) (string, error) {
// Drop everything after the ':' from the name when constructing the DNS SANs.
uniqueNames := make(map[string]struct{})
formattedDNSNames := make([]string, 0)
for _, host := range dnsNames {
hostSegments := strings.Split(host, ":")
if len(hostSegments) == 0 || hostSegments[0] == "" {
continue
}
formattedHost := hostSegments[0]
if _, ok := uniqueNames[formattedHost]; !ok {
formattedDNSNames = append(formattedDNSNames, formattedHost)
uniqueNames[formattedHost] = struct{}{}
}
}
template := &x509.CertificateRequest{
URIs: []*url.URL{uri.URI()},
SignatureAlgorithm: SigAlgoForKey(privateKey),
ExtraExtensions: extensions,
DNSNames: formattedDNSNames,
IPAddresses: ipAddresses,
}
HackSANExtensionForCSR(template)
// Create the CSR itself
var csrBuf bytes.Buffer
bs, err := x509.CreateCertificateRequest(rand.Reader, template, privateKey)
if err != nil {
return "", err
}
err = pem.Encode(&csrBuf, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: bs})
if err != nil {
return "", err
}
return csrBuf.String(), nil
}
// CreateCSR returns a CA CSR to sign the given service along with the PEM-encoded
// private key for this certificate.
func CreateCACSR(uri CertURI, privateKey crypto.Signer) (string, error) {
ext, err := CreateCAExtension()
if err != nil {
return "", err
}
return CreateCSR(uri, privateKey, nil, nil, ext)
}
// CreateCAExtension creates a pkix.Extension for the x509 Basic Constraints
// IsCA field ()
func CreateCAExtension() (pkix.Extension, error) {
type basicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional"`
}
basicCon := basicConstraints{IsCA: true, MaxPathLen: 0}
bitstr, err := asn1.Marshal(basicCon)
if err != nil {
return pkix.Extension{}, err
}
return pkix.Extension{
Id: []int{2, 5, 29, 19}, // from x509 package
Critical: true,
Value: bitstr,
}, nil
}
// InvalidCSRError returns an error with the given fmt.Sprintf-formatted message
// indicating certificate signing failed because the user supplied an invalid CSR.
//
// See: IsInvalidCSRError.
func InvalidCSRError(format string, args ...interface{}) error {
return invalidCSRError{fmt.Sprintf(format, args...)}
}
// IsInvalidCSRError returns whether the given error indicates that certificate
// signing failed because the user supplied an invalid CSR.
func IsInvalidCSRError(err error) bool {
_, ok := err.(invalidCSRError)
return ok
}
type invalidCSRError struct {
s string
}
func (e invalidCSRError) Error() string { return e.s }