1package connect
2
3import (
4	"crypto"
5	"crypto/ecdsa"
6	"crypto/rsa"
7	"crypto/sha1"
8	"crypto/sha256"
9	"crypto/x509"
10	"encoding/hex"
11	"encoding/pem"
12	"fmt"
13	"math/big"
14	"strings"
15)
16
17// ParseCert parses the x509 certificate from a PEM-encoded value.
18func ParseCert(pemValue string) (*x509.Certificate, error) {
19	// The _ result below is not an error but the remaining PEM bytes.
20	block, _ := pem.Decode([]byte(pemValue))
21	if block == nil {
22		return nil, fmt.Errorf("no PEM-encoded data found")
23	}
24
25	if block.Type != "CERTIFICATE" {
26		return nil, fmt.Errorf("first PEM-block should be CERTIFICATE type")
27	}
28
29	return x509.ParseCertificate(block.Bytes)
30}
31
32// ParseLeafCerts parses all of the x509 certificates from a PEM-encoded value
33// under the assumption that the first cert is a leaf (non-CA) cert and the
34// rest are intermediate CA certs.
35//
36// If no certificates are found this returns an error.
37func ParseLeafCerts(pemValue string) (*x509.Certificate, *x509.CertPool, error) {
38	certs, err := parseCerts(pemValue)
39	if err != nil {
40		return nil, nil, err
41	}
42
43	leaf := certs[0]
44	if leaf.IsCA {
45		return nil, nil, fmt.Errorf("first PEM-block should be a leaf cert")
46	}
47
48	intermediates := x509.NewCertPool()
49	for _, cert := range certs[1:] {
50		if !cert.IsCA {
51			return nil, nil, fmt.Errorf("found an unexpected leaf cert after the first PEM-block")
52		}
53		intermediates.AddCert(cert)
54	}
55
56	return leaf, intermediates, nil
57}
58
59// ParseCerts parses the all x509 certificates from a PEM-encoded value.
60// The first returned cert is a leaf cert and any other ones are intermediates.
61//
62// If no certificates are found this returns an error.
63func parseCerts(pemValue string) ([]*x509.Certificate, error) {
64	var out []*x509.Certificate
65
66	rest := []byte(pemValue)
67	for {
68		// The _ result below is not an error but the remaining PEM bytes.
69		block, remaining := pem.Decode(rest)
70		if block == nil {
71			break
72		}
73		rest = remaining
74
75		if block.Type != "CERTIFICATE" {
76			return nil, fmt.Errorf("PEM-block should be CERTIFICATE type")
77		}
78
79		cert, err := x509.ParseCertificate(block.Bytes)
80		if err != nil {
81			return nil, err
82		}
83		out = append(out, cert)
84	}
85
86	if len(out) == 0 {
87		return nil, fmt.Errorf("no PEM-encoded data found")
88	}
89
90	return out, nil
91}
92
93// CalculateCertFingerprint parses the x509 certificate from a PEM-encoded value
94// and calculates the SHA-1 fingerprint.
95func CalculateCertFingerprint(pemValue string) (string, error) {
96	// The _ result below is not an error but the remaining PEM bytes.
97	block, _ := pem.Decode([]byte(pemValue))
98	if block == nil {
99		return "", fmt.Errorf("no PEM-encoded data found")
100	}
101
102	if block.Type != "CERTIFICATE" {
103		return "", fmt.Errorf("first PEM-block should be CERTIFICATE type")
104	}
105
106	hash := sha1.Sum(block.Bytes)
107	return HexString(hash[:]), nil
108}
109
110// ParseSigner parses a crypto.Signer from a PEM-encoded key. The private key
111// is expected to be the first block in the PEM value.
112func ParseSigner(pemValue string) (crypto.Signer, error) {
113	// The _ result below is not an error but the remaining PEM bytes.
114	block, _ := pem.Decode([]byte(pemValue))
115	if block == nil {
116		return nil, fmt.Errorf("no PEM-encoded data found")
117	}
118
119	switch block.Type {
120	case "EC PRIVATE KEY":
121		return x509.ParseECPrivateKey(block.Bytes)
122
123	case "RSA PRIVATE KEY":
124		return x509.ParsePKCS1PrivateKey(block.Bytes)
125
126	case "PRIVATE KEY":
127		signer, err := x509.ParsePKCS8PrivateKey(block.Bytes)
128		if err != nil {
129			return nil, err
130		}
131		pk, ok := signer.(crypto.Signer)
132		if !ok {
133			return nil, fmt.Errorf("private key is not a valid format")
134		}
135
136		return pk, nil
137
138	default:
139		return nil, fmt.Errorf("unknown PEM block type for signing key: %s", block.Type)
140	}
141}
142
143// ParseCSR parses a CSR from a PEM-encoded value. The certificate request
144// must be the the first block in the PEM value.
145func ParseCSR(pemValue string) (*x509.CertificateRequest, error) {
146	// The _ result below is not an error but the remaining PEM bytes.
147	block, _ := pem.Decode([]byte(pemValue))
148	if block == nil {
149		return nil, fmt.Errorf("no PEM-encoded data found")
150	}
151
152	if block.Type != "CERTIFICATE REQUEST" {
153		return nil, fmt.Errorf("first PEM-block should be CERTIFICATE REQUEST type")
154	}
155
156	return x509.ParseCertificateRequest(block.Bytes)
157}
158
159// KeyId returns a x509 KeyId from the given signing key. The key must be
160// an *ecdsa.PublicKey currently, but may support more types in the future.
161func KeyId(raw interface{}) ([]byte, error) {
162	switch raw.(type) {
163	case *ecdsa.PublicKey:
164	case *rsa.PublicKey:
165	default:
166		return nil, fmt.Errorf("invalid key type: %T", raw)
167	}
168
169	// This is not standard; RFC allows any unique identifier as long as they
170	// match in subject/authority chains but suggests specific hashing of DER
171	// bytes of public key including DER tags.
172	bs, err := x509.MarshalPKIXPublicKey(raw)
173	if err != nil {
174		return nil, err
175	}
176
177	kID := sha256.Sum256(bs)
178	return kID[:], nil
179}
180
181// EncodeSerialNumber encodes the given serial number as a colon-hex encoded
182// string.
183func EncodeSerialNumber(serial *big.Int) string {
184	return HexString(serial.Bytes())
185}
186
187// EncodeSigningKeyID encodes the given AuthorityKeyId or SubjectKeyId into a
188// colon-hex encoded string suitable for using as a SigningKeyID value.
189func EncodeSigningKeyID(keyID []byte) string { return HexString(keyID) }
190
191// HexString returns a standard colon-separated hex value for the input
192// byte slice. This should be used with cert serial numbers and so on.
193func HexString(input []byte) string {
194	return strings.Replace(fmt.Sprintf("% x", input), " ", ":", -1)
195}
196
197// IsHexString returns true if the input is the output of HexString(). Meant
198// for use in tests.
199func IsHexString(input []byte) bool {
200	s := string(input)
201	if strings.Count(s, ":") < 5 { // 5 is arbitrary
202		return false
203	}
204
205	s = strings.ReplaceAll(s, ":", "")
206	_, err := hex.DecodeString(s)
207	return err == nil
208}
209
210// KeyInfoFromCert returns the key type and key bit length for the key used by
211// the certificate.
212func KeyInfoFromCert(cert *x509.Certificate) (keyType string, keyBits int, err error) {
213	switch k := cert.PublicKey.(type) {
214	case *ecdsa.PublicKey:
215		return "ec", k.Curve.Params().BitSize, nil
216	case *rsa.PublicKey:
217		return "rsa", k.N.BitLen(), nil
218	default:
219		return "", 0, fmt.Errorf("unsupported key type")
220	}
221}
222