github.com/outbrain/consul@v1.4.5/agent/connect/parsing.go (about) 1 package connect 2 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/sha1" 7 "crypto/sha256" 8 "crypto/x509" 9 "encoding/pem" 10 "fmt" 11 "strings" 12 ) 13 14 // ParseCert parses the x509 certificate from a PEM-encoded value. 15 func ParseCert(pemValue string) (*x509.Certificate, error) { 16 // The _ result below is not an error but the remaining PEM bytes. 17 block, _ := pem.Decode([]byte(pemValue)) 18 if block == nil { 19 return nil, fmt.Errorf("no PEM-encoded data found") 20 } 21 22 if block.Type != "CERTIFICATE" { 23 return nil, fmt.Errorf("first PEM-block should be CERTIFICATE type") 24 } 25 26 return x509.ParseCertificate(block.Bytes) 27 } 28 29 // CalculateCertFingerprint parses the x509 certificate from a PEM-encoded value 30 // and calculates the SHA-1 fingerprint. 31 func CalculateCertFingerprint(pemValue string) (string, error) { 32 // The _ result below is not an error but the remaining PEM bytes. 33 block, _ := pem.Decode([]byte(pemValue)) 34 if block == nil { 35 return "", fmt.Errorf("no PEM-encoded data found") 36 } 37 38 if block.Type != "CERTIFICATE" { 39 return "", fmt.Errorf("first PEM-block should be CERTIFICATE type") 40 } 41 42 hash := sha1.Sum(block.Bytes) 43 return HexString(hash[:]), nil 44 } 45 46 // ParseSigner parses a crypto.Signer from a PEM-encoded key. The private key 47 // is expected to be the first block in the PEM value. 48 func ParseSigner(pemValue string) (crypto.Signer, error) { 49 // The _ result below is not an error but the remaining PEM bytes. 50 block, _ := pem.Decode([]byte(pemValue)) 51 if block == nil { 52 return nil, fmt.Errorf("no PEM-encoded data found") 53 } 54 55 switch block.Type { 56 case "EC PRIVATE KEY": 57 return x509.ParseECPrivateKey(block.Bytes) 58 59 case "RSA PRIVATE KEY": 60 return x509.ParsePKCS1PrivateKey(block.Bytes) 61 62 case "PRIVATE KEY": 63 signer, err := x509.ParsePKCS8PrivateKey(block.Bytes) 64 if err != nil { 65 return nil, err 66 } 67 pk, ok := signer.(crypto.Signer) 68 if !ok { 69 return nil, fmt.Errorf("private key is not a valid format") 70 } 71 72 return pk, nil 73 74 default: 75 return nil, fmt.Errorf("unknown PEM block type for signing key: %s", block.Type) 76 } 77 } 78 79 // ParseCSR parses a CSR from a PEM-encoded value. The certificate request 80 // must be the the first block in the PEM value. 81 func ParseCSR(pemValue string) (*x509.CertificateRequest, error) { 82 // The _ result below is not an error but the remaining PEM bytes. 83 block, _ := pem.Decode([]byte(pemValue)) 84 if block == nil { 85 return nil, fmt.Errorf("no PEM-encoded data found") 86 } 87 88 if block.Type != "CERTIFICATE REQUEST" { 89 return nil, fmt.Errorf("first PEM-block should be CERTIFICATE REQUEST type") 90 } 91 92 return x509.ParseCertificateRequest(block.Bytes) 93 } 94 95 // KeyId returns a x509 KeyId from the given signing key. The key must be 96 // an *ecdsa.PublicKey currently, but may support more types in the future. 97 func KeyId(raw interface{}) ([]byte, error) { 98 switch raw.(type) { 99 case *ecdsa.PublicKey: 100 default: 101 return nil, fmt.Errorf("invalid key type: %T", raw) 102 } 103 104 // This is not standard; RFC allows any unique identifier as long as they 105 // match in subject/authority chains but suggests specific hashing of DER 106 // bytes of public key including DER tags. 107 bs, err := x509.MarshalPKIXPublicKey(raw) 108 if err != nil { 109 return nil, err 110 } 111 112 // String formatted 113 kID := sha256.Sum256(bs) 114 return []byte(strings.Replace(fmt.Sprintf("% x", kID), " ", ":", -1)), nil 115 } 116 117 // HexString returns a standard colon-separated hex value for the input 118 // byte slice. This should be used with cert serial numbers and so on. 119 func HexString(input []byte) string { 120 return strings.Replace(fmt.Sprintf("% x", input), " ", ":", -1) 121 }