mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 16:13:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package utils
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/rsa"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"encoding/pem"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"math/big"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/agl/ed25519"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| 	"github.com/theupdateframework/notary"
 | |
| 	"github.com/theupdateframework/notary/tuf/data"
 | |
| )
 | |
| 
 | |
| // CanonicalKeyID returns the ID of the public bytes version of a TUF key.
 | |
| // On regular RSA/ECDSA TUF keys, this is just the key ID.  On X509 RSA/ECDSA
 | |
| // TUF keys, this is the key ID of the public key part of the key in the leaf cert
 | |
| func CanonicalKeyID(k data.PublicKey) (string, error) {
 | |
| 	if k == nil {
 | |
| 		return "", errors.New("public key is nil")
 | |
| 	}
 | |
| 	switch k.Algorithm() {
 | |
| 	case data.ECDSAx509Key, data.RSAx509Key:
 | |
| 		return X509PublicKeyID(k)
 | |
| 	default:
 | |
| 		return k.ID(), nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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")
 | |
| }
 | |
| 
 | |
| // X509PublicKeyID returns a public key ID as a string, given a
 | |
| // data.PublicKey that contains an X509 Certificate
 | |
| func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
 | |
| 	// Note that this only loads the first certificate from the public key
 | |
| 	cert, err := LoadCertFromPEM(certPubKey.Public())
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	pubKeyBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	var key data.PublicKey
 | |
| 	switch certPubKey.Algorithm() {
 | |
| 	case data.ECDSAx509Key:
 | |
| 		key = data.NewECDSAPublicKey(pubKeyBytes)
 | |
| 	case data.RSAx509Key:
 | |
| 		key = data.NewRSAPublicKey(pubKeyBytes)
 | |
| 	}
 | |
| 
 | |
| 	return key.ID(), nil
 | |
| }
 | |
| 
 | |
| func parseLegacyPrivateKey(block *pem.Block, passphrase string) (data.PrivateKey, error) {
 | |
| 	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
 | |
| 	}
 | |
| 
 | |
| 	switch block.Type {
 | |
| 	case "RSA PRIVATE KEY":
 | |
| 		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":
 | |
| 		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.
 | |
| 		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)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
 | |
| // supports PKCS#8 as well as RSA/ECDSA (PKCS#1) only in non-FIPS mode and
 | |
| // attempts to decrypt using the passphrase, if encrypted.
 | |
| func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, error) {
 | |
| 	return parsePEMPrivateKey(pemBytes, passphrase, notary.FIPSEnabled())
 | |
| }
 | |
| 
 | |
| func parsePEMPrivateKey(pemBytes []byte, passphrase string, fips bool) (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", "EC PRIVATE KEY", "ED25519 PRIVATE KEY":
 | |
| 		if fips {
 | |
| 			return nil, fmt.Errorf("%s not supported in FIPS mode", block.Type)
 | |
| 		}
 | |
| 		return parseLegacyPrivateKey(block, passphrase)
 | |
| 	case "ENCRYPTED PRIVATE KEY", "PRIVATE KEY":
 | |
| 		if passphrase == "" {
 | |
| 			return ParsePKCS8ToTufKey(block.Bytes, nil)
 | |
| 		}
 | |
| 		return ParsePKCS8ToTufKey(block.Bytes, []byte(passphrase))
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("unsupported key type %q", block.Type)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // CertToPEM is a 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
 | |
| }
 | |
| 
 | |
| // CertChainToPEM is a utility function returns a PEM encoded chain of x509 Certificates, in the order they are passed
 | |
| func CertChainToPEM(certChain []*x509.Certificate) ([]byte, error) {
 | |
| 	var pemBytes bytes.Buffer
 | |
| 	for _, cert := range certChain {
 | |
| 		if err := pem.Encode(&pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	return pemBytes.Bytes(), nil
 | |
| }
 | |
| 
 | |
| // 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) []*x509.Certificate {
 | |
| 	var intCerts []*x509.Certificate
 | |
| 	for _, cert := range certs {
 | |
| 		if cert.IsCA {
 | |
| 			intCerts = append(intCerts, cert)
 | |
| 		}
 | |
| 	}
 | |
| 	return intCerts
 | |
| }
 | |
| 
 | |
| // ParsePEMPublicKey returns a data.PublicKey from a PEM encoded public key or certificate.
 | |
| func ParsePEMPublicKey(pubKeyBytes []byte) (data.PublicKey, error) {
 | |
| 	pemBlock, _ := pem.Decode(pubKeyBytes)
 | |
| 	if pemBlock == nil {
 | |
| 		return nil, errors.New("no valid public key found")
 | |
| 	}
 | |
| 
 | |
| 	switch pemBlock.Type {
 | |
| 	case "CERTIFICATE":
 | |
| 		cert, err := x509.ParseCertificate(pemBlock.Bytes)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("could not parse provided certificate: %v", err)
 | |
| 		}
 | |
| 		err = ValidateCertificate(cert, true)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("invalid certificate: %v", err)
 | |
| 		}
 | |
| 		return CertToKey(cert), nil
 | |
| 	case "PUBLIC KEY":
 | |
| 		keyType, err := keyTypeForPublicKey(pemBlock.Bytes)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return data.NewPublicKey(keyType, pemBlock.Bytes), nil
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("unsupported PEM block type %q, expected CERTIFICATE or PUBLIC KEY", pemBlock.Type)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func keyTypeForPublicKey(pubKeyBytes []byte) (string, error) {
 | |
| 	pub, err := x509.ParsePKIXPublicKey(pubKeyBytes)
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("unable to parse pem encoded public key: %v", err)
 | |
| 	}
 | |
| 	switch pub.(type) {
 | |
| 	case *ecdsa.PublicKey:
 | |
| 		return data.ECDSAKey, nil
 | |
| 	case *rsa.PublicKey:
 | |
| 		return data.RSAKey, nil
 | |
| 	}
 | |
| 	return "", fmt.Errorf("unknown public key format")
 | |
| }
 | |
| 
 | |
| // ValidateCertificate returns an error if the certificate is not valid for notary
 | |
| // Currently this is only ensuring the public key has a large enough modulus if RSA,
 | |
| // using a non SHA1 signature algorithm, and an optional time expiry check
 | |
| func ValidateCertificate(c *x509.Certificate, checkExpiry bool) error {
 | |
| 	if (c.NotBefore).After(c.NotAfter) {
 | |
| 		return fmt.Errorf("certificate validity window is invalid")
 | |
| 	}
 | |
| 	// Can't have SHA1 sig algorithm
 | |
| 	if c.SignatureAlgorithm == x509.SHA1WithRSA || c.SignatureAlgorithm == x509.DSAWithSHA1 || c.SignatureAlgorithm == x509.ECDSAWithSHA1 {
 | |
| 		return fmt.Errorf("certificate with CN %s uses invalid SHA1 signature algorithm", c.Subject.CommonName)
 | |
| 	}
 | |
| 	// If we have an RSA key, make sure it's long enough
 | |
| 	if c.PublicKeyAlgorithm == x509.RSA {
 | |
| 		rsaKey, ok := c.PublicKey.(*rsa.PublicKey)
 | |
| 		if !ok {
 | |
| 			return fmt.Errorf("unable to parse RSA public key")
 | |
| 		}
 | |
| 		if rsaKey.N.BitLen() < notary.MinRSABitSize {
 | |
| 			return fmt.Errorf("RSA bit length is too short")
 | |
| 		}
 | |
| 	}
 | |
| 	if checkExpiry {
 | |
| 		now := time.Now()
 | |
| 		tomorrow := now.AddDate(0, 0, 1)
 | |
| 		// Give one day leeway on creation "before" time, check "after" against today
 | |
| 		if (tomorrow).Before(c.NotBefore) || now.After(c.NotAfter) {
 | |
| 			return data.ErrCertExpired{CN: c.Subject.CommonName}
 | |
| 		}
 | |
| 		// If this certificate is expiring within 6 months, put out a warning
 | |
| 		if (c.NotAfter).Before(time.Now().AddDate(0, 6, 0)) {
 | |
| 			logrus.Warnf("certificate with CN %s is near expiry", c.Subject.CommonName)
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GenerateKey returns a new private key using the provided algorithm or an
 | |
| // error detailing why the key could not be generated
 | |
| func GenerateKey(algorithm string) (data.PrivateKey, error) {
 | |
| 	switch algorithm {
 | |
| 	case data.ECDSAKey:
 | |
| 		return GenerateECDSAKey(rand.Reader)
 | |
| 	case data.ED25519Key:
 | |
| 		return GenerateED25519Key(rand.Reader)
 | |
| 	}
 | |
| 	return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm)
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 
 | |
| 	pubKey := data.NewRSAPublicKey(rsaPubBytes)
 | |
| 	return data.NewRSAPrivateKey(pubKey, rsaPrivBytes)
 | |
| }
 | |
| 
 | |
| // GenerateECDSAKey generates an ECDSA Private key and returns a TUF PrivateKey
 | |
| func GenerateECDSAKey(random io.Reader) (data.PrivateKey, error) {
 | |
| 	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(random)
 | |
| 	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 ED25519 key with keyID: %s", tufPrivKey.ID())
 | |
| 
 | |
| 	return tufPrivKey, nil
 | |
| }
 | |
| 
 | |
| // ECDSAToPrivateKey converts an ecdsa.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)
 | |
| 	}
 | |
| 
 | |
| 	pubKey := data.NewECDSAPublicKey(ecdsaPubBytes)
 | |
| 	return data.NewECDSAPrivateKey(pubKey, ecdsaPrivKeyBytes)
 | |
| }
 | |
| 
 | |
| // 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")
 | |
| 	}
 | |
| 
 | |
| 	pubKey := data.NewED25519PublicKey(privKeyBytes[:ed25519.PublicKeySize])
 | |
| 	return data.NewED25519PrivateKey(*pubKey, privKeyBytes)
 | |
| }
 | |
| 
 | |
| // ExtractPrivateKeyAttributes extracts role and gun values from private key bytes
 | |
| func ExtractPrivateKeyAttributes(pemBytes []byte) (data.RoleName, data.GUN, error) {
 | |
| 	return extractPrivateKeyAttributes(pemBytes, notary.FIPSEnabled())
 | |
| }
 | |
| 
 | |
| func extractPrivateKeyAttributes(pemBytes []byte, fips bool) (data.RoleName, data.GUN, error) {
 | |
| 	block, _ := pem.Decode(pemBytes)
 | |
| 	if block == nil {
 | |
| 		return "", "", errors.New("PEM block is empty")
 | |
| 	}
 | |
| 
 | |
| 	switch block.Type {
 | |
| 	case "RSA PRIVATE KEY", "EC PRIVATE KEY", "ED25519 PRIVATE KEY":
 | |
| 		if fips {
 | |
| 			return "", "", fmt.Errorf("%s not supported in FIPS mode", block.Type)
 | |
| 		}
 | |
| 	case "PRIVATE KEY", "ENCRYPTED PRIVATE KEY":
 | |
| 		// do nothing for PKCS#8 keys
 | |
| 	default:
 | |
| 		return "", "", errors.New("unknown key format")
 | |
| 	}
 | |
| 	return data.RoleName(block.Headers["role"]), data.GUN(block.Headers["gun"]), nil
 | |
| }
 | |
| 
 | |
| // ConvertPrivateKeyToPKCS8 converts a data.PrivateKey to PKCS#8 Format
 | |
| func ConvertPrivateKeyToPKCS8(key data.PrivateKey, role data.RoleName, gun data.GUN, passphrase string) ([]byte, error) {
 | |
| 	var (
 | |
| 		err       error
 | |
| 		der       []byte
 | |
| 		blockType = "PRIVATE KEY"
 | |
| 	)
 | |
| 
 | |
| 	if passphrase == "" {
 | |
| 		der, err = ConvertTUFKeyToPKCS8(key, nil)
 | |
| 	} else {
 | |
| 		blockType = "ENCRYPTED PRIVATE KEY"
 | |
| 		der, err = ConvertTUFKeyToPKCS8(key, []byte(passphrase))
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("unable to convert to PKCS8 key")
 | |
| 	}
 | |
| 
 | |
| 	headers := make(map[string]string)
 | |
| 	if role != "" {
 | |
| 		headers["role"] = role.String()
 | |
| 	}
 | |
| 
 | |
| 	if gun != "" {
 | |
| 		headers["gun"] = gun.String()
 | |
| 	}
 | |
| 
 | |
| 	return pem.EncodeToMemory(&pem.Block{Bytes: der, Type: blockType, Headers: headers}), nil
 | |
| }
 | |
| 
 | |
| // CertToKey transforms a single input certificate into its corresponding
 | |
| // PublicKey
 | |
| func CertToKey(cert *x509.Certificate) data.PublicKey {
 | |
| 	block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
 | |
| 	pemdata := pem.EncodeToMemory(&block)
 | |
| 
 | |
| 	switch cert.PublicKeyAlgorithm {
 | |
| 	case x509.RSA:
 | |
| 		return data.NewRSAx509PublicKey(pemdata)
 | |
| 	case x509.ECDSA:
 | |
| 		return data.NewECDSAx509PublicKey(pemdata)
 | |
| 	default:
 | |
| 		logrus.Debugf("Unknown key type parsed from certificate: %v", cert.PublicKeyAlgorithm)
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // CertsToKeys transforms each of the input certificate chains into its corresponding
 | |
| // PublicKey
 | |
| func CertsToKeys(leafCerts map[string]*x509.Certificate, intCerts map[string][]*x509.Certificate) map[string]data.PublicKey {
 | |
| 	keys := make(map[string]data.PublicKey)
 | |
| 	for id, leafCert := range leafCerts {
 | |
| 		if key, err := CertBundleToKey(leafCert, intCerts[id]); err == nil {
 | |
| 			keys[key.ID()] = key
 | |
| 		}
 | |
| 	}
 | |
| 	return keys
 | |
| }
 | |
| 
 | |
| // CertBundleToKey creates a TUF key from a leaf certs and a list of
 | |
| // intermediates
 | |
| func CertBundleToKey(leafCert *x509.Certificate, intCerts []*x509.Certificate) (data.PublicKey, error) {
 | |
| 	certBundle := []*x509.Certificate{leafCert}
 | |
| 	certBundle = append(certBundle, intCerts...)
 | |
| 	certChainPEM, err := CertChainToPEM(certBundle)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	var newKey data.PublicKey
 | |
| 	// Use the leaf cert's public key algorithm for typing
 | |
| 	switch leafCert.PublicKeyAlgorithm {
 | |
| 	case x509.RSA:
 | |
| 		newKey = data.NewRSAx509PublicKey(certChainPEM)
 | |
| 	case x509.ECDSA:
 | |
| 		newKey = data.NewECDSAx509PublicKey(certChainPEM)
 | |
| 	default:
 | |
| 		logrus.Debugf("Unknown key type parsed from certificate: %v", leafCert.PublicKeyAlgorithm)
 | |
| 		return nil, x509.ErrUnsupportedAlgorithm
 | |
| 	}
 | |
| 	return newKey, nil
 | |
| }
 | |
| 
 | |
| // NewCertificate returns an X509 Certificate following a template, given a Common Name and validity interval.
 | |
| func NewCertificate(commonName string, startTime, endTime time.Time) (*x509.Certificate, error) {
 | |
| 	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)
 | |
| 	}
 | |
| 
 | |
| 	return &x509.Certificate{
 | |
| 		SerialNumber: serialNumber,
 | |
| 		Subject: pkix.Name{
 | |
| 			CommonName: commonName,
 | |
| 		},
 | |
| 		NotBefore: startTime,
 | |
| 		NotAfter:  endTime,
 | |
| 
 | |
| 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
 | |
| 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
 | |
| 		BasicConstraintsValid: true,
 | |
| 	}, nil
 | |
| }
 | 
