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