docs/trustmanager/x509utils.go

485 lines
13 KiB
Go

package trustmanager
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"net/url"
"time"
"github.com/Sirupsen/logrus"
"github.com/agl/ed25519"
"github.com/endophage/gotuf/data"
)
// GetCertFromURL tries to get a X509 certificate given a HTTPS URL
func GetCertFromURL(urlStr string) (*x509.Certificate, error) {
url, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
// Check if we are adding via HTTPS
if url.Scheme != "https" {
return nil, errors.New("only HTTPS URLs allowed")
}
// Download the certificate and write to directory
resp, err := http.Get(url.String())
if err != nil {
return nil, err
}
// Copy the content to certBytes
defer resp.Body.Close()
certBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Try to extract the first valid PEM certificate from the bytes
cert, err := LoadCertFromPEM(certBytes)
if err != nil {
return nil, err
}
return cert, nil
}
// CertToPEM is an utility function returns a PEM encoded x509 Certificate
func CertToPEM(cert *x509.Certificate) []byte {
pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
return pemCert
}
// LoadCertFromPEM returns the first certificate found in a bunch of bytes or error
// if nothing is found. Taken from https://golang.org/src/crypto/x509/cert_pool.go#L85.
func LoadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
for len(pemBytes) > 0 {
var block *pem.Block
block, pemBytes = pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("no certificates found in PEM data")
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
continue
}
return cert, nil
}
return nil, errors.New("no certificates found in PEM data")
}
// FingerprintCert returns a TUF compliant fingerprint for a X509 Certificate
func FingerprintCert(cert *x509.Certificate) (string, error) {
certID, err := fingerprintCert(cert)
if err != nil {
return "", err
}
return string(certID), nil
}
func fingerprintCert(cert *x509.Certificate) (CertID, error) {
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
pemdata := pem.EncodeToMemory(&block)
var keyType data.KeyAlgorithm
switch cert.PublicKeyAlgorithm {
case x509.RSA:
keyType = data.RSAx509Key
case x509.ECDSA:
keyType = data.ECDSAx509Key
default:
return "", fmt.Errorf("got Unknown key type while fingerprinting certificate")
}
// Create new TUF Key so we can compute the TUF-compliant CertID
tufKey := data.NewPublicKey(keyType, pemdata)
return CertID(tufKey.ID()), nil
}
// loadCertsFromDir receives a store AddCertFromFile for each certificate found
func loadCertsFromDir(s *X509FileStore) {
certFiles := s.fileStore.ListFiles(true)
for _, c := range certFiles {
s.AddCertFromFile(c)
}
}
// LoadCertFromFile loads the first certificate from the file provided. The
// data is expected to be PEM Encoded and contain one of more certificates
// with PEM type "CERTIFICATE"
func LoadCertFromFile(filename string) (*x509.Certificate, error) {
certs, err := LoadCertBundleFromFile(filename)
if err != nil {
return nil, err
}
return certs[0], nil
}
// LoadCertBundleFromFile loads certificates from the []byte provided. The
// data is expected to be PEM Encoded and contain one of more certificates
// with PEM type "CERTIFICATE"
func LoadCertBundleFromFile(filename string) ([]*x509.Certificate, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return LoadCertBundleFromPEM(b)
}
// LoadCertBundleFromPEM loads certificates from the []byte provided. The
// data is expected to be PEM Encoded and contain one of more certificates
// with PEM type "CERTIFICATE"
func LoadCertBundleFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
certificates := []*x509.Certificate{}
var block *pem.Block
block, pemBytes = pem.Decode(pemBytes)
for ; block != nil; block, pemBytes = pem.Decode(pemBytes) {
if block.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certificates = append(certificates, cert)
} else {
return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
}
}
if len(certificates) == 0 {
return nil, fmt.Errorf("no valid certificates found")
}
return certificates, nil
}
// GetLeafCerts parses a list of x509 Certificates and returns all of them
// that aren't CA
func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate {
var leafCerts []*x509.Certificate
for _, cert := range certs {
if cert.IsCA {
continue
}
leafCerts = append(leafCerts, cert)
}
return leafCerts
}
// GetIntermediateCerts parses a list of x509 Certificates and returns all of the
// ones marked as a CA, to be used as intermediates
func GetIntermediateCerts(certs []*x509.Certificate) (intCerts []*x509.Certificate) {
for _, cert := range certs {
if !cert.IsCA {
continue
}
intCerts = append(intCerts, cert)
}
return intCerts
}
// ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
// only supports RSA (PKCS#1) and attempts to decrypt using the passphrase, if encrypted.
func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("no valid private key found")
}
switch block.Type {
case "RSA PRIVATE KEY":
var privKeyBytes []byte
var err error
if x509.IsEncryptedPEMBlock(block) {
privKeyBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
if err != nil {
return nil, errors.New("could not decrypt private key")
}
} else {
privKeyBytes = block.Bytes
}
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(privKeyBytes)
if err != nil {
return nil, fmt.Errorf("could not parse DER encoded key: %v", err)
}
tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey)
if err != nil {
return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err)
}
return tufRSAPrivateKey, nil
case "EC PRIVATE KEY":
var privKeyBytes []byte
var err error
if x509.IsEncryptedPEMBlock(block) {
privKeyBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
if err != nil {
return nil, errors.New("could not decrypt private key")
}
} else {
privKeyBytes = block.Bytes
}
ecdsaPrivKey, err := x509.ParseECPrivateKey(privKeyBytes)
if err != nil {
return nil, fmt.Errorf("could not parse DER encoded private key: %v", err)
}
tufECDSAPrivateKey, err := ECDSAToPrivateKey(ecdsaPrivKey)
if err != nil {
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
}
return tufECDSAPrivateKey, nil
case "ED25519 PRIVATE KEY":
// We serialize ED25519 keys by concatenating the private key
// to the public key and encoding with PEM. See the
// ED25519ToPrivateKey function.
var privKeyBytes []byte
var err error
if x509.IsEncryptedPEMBlock(block) {
privKeyBytes, err = x509.DecryptPEMBlock(block, []byte(passphrase))
if err != nil {
return nil, errors.New("could not decrypt private key")
}
} else {
privKeyBytes = block.Bytes
}
tufECDSAPrivateKey, err := ED25519ToPrivateKey(privKeyBytes)
if err != nil {
return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err)
}
return tufECDSAPrivateKey, nil
default:
return nil, fmt.Errorf("unsupported key type %q", block.Type)
}
}
// GenerateRSAKey generates an RSA private key and returns a TUF PrivateKey
func GenerateRSAKey(random io.Reader, bits int) (data.PrivateKey, error) {
rsaPrivKey, err := rsa.GenerateKey(random, bits)
if err != nil {
return nil, fmt.Errorf("could not generate private key: %v", err)
}
tufPrivKey, err := RSAToPrivateKey(rsaPrivKey)
if err != nil {
return nil, err
}
logrus.Debugf("generated RSA key with keyID: %s", tufPrivKey.ID())
return tufPrivKey, nil
}
// RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (data.PrivateKey, error) {
// Get a DER-encoded representation of the PublicKey
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal public key: %v", err)
}
// Get a DER-encoded representation of the PrivateKey
rsaPrivBytes := x509.MarshalPKCS1PrivateKey(rsaPrivKey)
return data.NewPrivateKey(data.RSAKey, rsaPubBytes, rsaPrivBytes), nil
}
// GenerateECDSAKey generates an ECDSA private key and returns a TUF PrivateKey
func GenerateECDSAKey(random io.Reader) (data.PrivateKey, error) {
// TODO(diogo): For now hardcode P256. There were timming attacks on the other
// curves, but I can't seem to find the issue.
ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), random)
if err != nil {
return nil, err
}
tufPrivKey, err := ECDSAToPrivateKey(ecdsaPrivKey)
if err != nil {
return nil, err
}
logrus.Debugf("generated ECDSA key with keyID: %s", tufPrivKey.ID())
return tufPrivKey, nil
}
// GenerateED25519Key generates an ED25519 private key and returns a TUF
// PrivateKey. The serialization format we use is just the public key bytes
// followed by the private key bytes
func GenerateED25519Key(random io.Reader) (data.PrivateKey, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
var serialized [ed25519.PublicKeySize + ed25519.PrivateKeySize]byte
copy(serialized[:], pub[:])
copy(serialized[ed25519.PublicKeySize:], priv[:])
tufPrivKey, err := ED25519ToPrivateKey(serialized[:])
if err != nil {
return nil, err
}
logrus.Debugf("generated EDDSA key with keyID: %s", tufPrivKey.ID())
return tufPrivKey, nil
}
// ECDSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey) (data.PrivateKey, error) {
// Get a DER-encoded representation of the PublicKey
ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal public key: %v", err)
}
// Get a DER-encoded representation of the PrivateKey
ecdsaPrivKeyBytes, err := x509.MarshalECPrivateKey(ecdsaPrivKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal private key: %v", err)
}
return data.NewPrivateKey(data.ECDSAKey, ecdsaPubBytes, ecdsaPrivKeyBytes), nil
}
// ED25519ToPrivateKey converts a serialized ED25519 key to a TUF
// data.PrivateKey type
func ED25519ToPrivateKey(privKeyBytes []byte) (data.PrivateKey, error) {
if len(privKeyBytes) != ed25519.PublicKeySize+ed25519.PrivateKeySize {
return nil, errors.New("malformed ed25519 private key")
}
return data.NewPrivateKey(data.ED25519Key, privKeyBytes[:ed25519.PublicKeySize], privKeyBytes), nil
}
func blockType(algorithm data.KeyAlgorithm) (string, error) {
switch algorithm {
case data.RSAKey:
return "RSA PRIVATE KEY", nil
case data.ECDSAKey:
return "EC PRIVATE KEY", nil
case data.ED25519Key:
return "ED25519 PRIVATE KEY", nil
default:
return "", fmt.Errorf("algorithm %s not supported", algorithm)
}
}
// KeyToPEM returns a PEM encoded key from a Private Key
func KeyToPEM(privKey data.PrivateKey) ([]byte, error) {
blockType, err := blockType(privKey.Algorithm())
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: blockType, Bytes: privKey.Private()}), nil
}
// EncryptPrivateKey returns an encrypted PEM key given a Privatekey
// and a passphrase
func EncryptPrivateKey(key data.PrivateKey, passphrase string) ([]byte, error) {
blockType, err := blockType(key.Algorithm())
if err != nil {
return nil, err
}
password := []byte(passphrase)
cipherType := x509.PEMCipherAES256
encryptedPEMBlock, err := x509.EncryptPEMBlock(rand.Reader,
blockType,
key.Private(),
password,
cipherType)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(encryptedPEMBlock), nil
}
// CertsToKeys transforms each of the input certificates into it's corresponding
// PublicKey
func CertsToKeys(certs []*x509.Certificate) map[string]data.PublicKey {
keys := make(map[string]data.PublicKey)
for _, cert := range certs {
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
pemdata := pem.EncodeToMemory(&block)
var keyType data.KeyAlgorithm
switch cert.PublicKeyAlgorithm {
case x509.RSA:
keyType = data.RSAx509Key
case x509.ECDSA:
keyType = data.ECDSAx509Key
default:
logrus.Debugf("unknown certificate type found, ignoring")
}
// Create new the appropriate PublicKey
newKey := data.NewPublicKey(keyType, pemdata)
keys[newKey.ID()] = newKey
}
return keys
}
// NewCertificate returns an X509 Certificate following a template, given a GUN.
func NewCertificate(gun string) (*x509.Certificate, error) {
notBefore := time.Now()
notAfter := notBefore.Add(time.Hour * 24 * 365 * 2)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, fmt.Errorf("failed to generate new certificate: %v", err)
}
// TODO(diogo): Currently hard coding organization to be the gun. Revisit.
return &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: gun,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
BasicConstraintsValid: true,
}, nil
}