linkerd2/pkg/tls/codec.go

141 lines
3.4 KiB
Go

package tls
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"reflect"
)
// === ENCODE ===
// EncodeCertificatesPEM encodes the collection of provided certificates as
// a text blob of PEM-encoded certificates.
func EncodeCertificatesPEM(crts ...*x509.Certificate) string {
buf := bytes.Buffer{}
for _, c := range crts {
encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: c.Raw})
}
return buf.String()
}
// EncodePrivateKeyPEM encodes the provided key as PEM-encoded text
func EncodePrivateKeyPEM(k *ecdsa.PrivateKey) ([]byte, error) {
der, err := x509.MarshalECPrivateKey(k)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: der}), nil
}
// EncodePrivateKeyP8 encodes the provided key as PEM-encoded text
func EncodePrivateKeyP8(k *ecdsa.PrivateKey) []byte {
p8, err := x509.MarshalPKCS8PrivateKey(k)
if err != nil {
panic("ECDSA keys must be encodeable as PKCS8")
}
return p8
}
func encode(buf *bytes.Buffer, blk *pem.Block) {
if err := pem.Encode(buf, blk); err != nil {
panic("encoding to memory must not fail")
}
}
// === DECODE ===
// DecodePEMKey parses a PEM-encoded private key from the named path.
func DecodePEMKey(txt string) (GenericPrivateKey, error) {
block, _ := pem.Decode([]byte(txt))
if block == nil {
return nil, errors.New("not PEM-encoded")
}
switch block.Type {
case "EC PRIVATE KEY":
k, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKeyEC{k}, nil
case "RSA PRIVATE KEY":
k, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKeyRSA{k}, nil
case "PRIVATE KEY":
k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
if ec, ok := k.(*ecdsa.PrivateKey); ok {
return privateKeyEC{ec}, nil
}
if rsa, ok := k.(*rsa.PrivateKey); ok {
return privateKeyRSA{rsa}, nil
}
return nil, fmt.Errorf(
"unsupported PKCS#8 encoded private key type: '%s', linkerd2 only supports ECDSA and RSA private keys",
reflect.TypeOf(k))
default:
return nil, fmt.Errorf("unsupported block type: '%s'", block.Type)
}
}
// DecodePEMCertificates parses a string containing PEM-encoded certificates.
func DecodePEMCertificates(txt string) (certs []*x509.Certificate, err error) {
buf := []byte(txt)
for len(buf) > 0 {
var c *x509.Certificate
c, buf, err = decodeCertificatePEM(buf)
if err != nil {
return
}
if c == nil {
continue // not a CERTIFICATE, skip
}
certs = append(certs, c)
}
return
}
// CertificatesToPool converts a slice of certificates into a cert pool
func CertificatesToPool(certs []*x509.Certificate) *x509.CertPool {
pool := x509.NewCertPool()
for _, c := range certs {
pool.AddCert(c)
}
return pool
}
// DecodePEMCertPool parses a string containing PE-encoded certificates into a CertPool.
func DecodePEMCertPool(txt string) (*x509.CertPool, error) {
certs, err := DecodePEMCertificates(txt)
if err != nil {
return nil, err
}
if len(certs) == 0 {
return nil, errors.New("no certificates found")
}
return CertificatesToPool(certs), nil
}
func decodeCertificatePEM(crtb []byte) (*x509.Certificate, []byte, error) {
block, crtb := pem.Decode(crtb)
if block == nil {
return nil, crtb, errors.New("not a PEM certificate")
}
if block.Type != "CERTIFICATE" {
return nil, nil, nil
}
c, err := x509.ParseCertificate(block.Bytes)
return c, crtb, err
}