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  }