2015-06-17 16:43:36 +00:00
|
|
|
// Package certutil contains helper functions that are mostly used
|
|
|
|
// with the PKI backend but can be generally useful. Functionality
|
|
|
|
// includes helpers for converting a certificate/private key bundle
|
|
|
|
// between DER and PEM, printing certificate serial numbers, and more.
|
|
|
|
//
|
2017-02-24 16:17:59 +00:00
|
|
|
// Functionality specific to the PKI backend includes some types
|
|
|
|
// and helper methods to make requesting certificates from the
|
|
|
|
// backend easy.
|
2015-06-17 16:43:36 +00:00
|
|
|
package certutil
|
|
|
|
|
|
|
|
import (
|
2016-09-28 00:50:17 +00:00
|
|
|
"bytes"
|
2015-06-17 16:43:36 +00:00
|
|
|
"crypto"
|
2015-12-09 20:41:32 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/rsa"
|
2015-06-17 16:43:36 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
2016-09-16 15:05:43 +00:00
|
|
|
"math/big"
|
2015-06-19 20:06:56 +00:00
|
|
|
"strings"
|
2016-07-28 19:19:27 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/vault/helper/errutil"
|
2015-06-17 16:43:36 +00:00
|
|
|
)
|
|
|
|
|
2015-06-18 10:42:57 +00:00
|
|
|
// Secret is used to attempt to unmarshal a Vault secret
|
|
|
|
// JSON response, as a convenience
|
|
|
|
type Secret struct {
|
|
|
|
Data map[string]interface{} `json:"data"`
|
|
|
|
}
|
|
|
|
|
2015-12-14 18:16:47 +00:00
|
|
|
// PrivateKeyType holds a string representation of the type of private key (ec
|
|
|
|
// or rsa) referenced in CertBundle and ParsedCertBundle. This uses colloquial
|
|
|
|
// names rather than official names, to eliminate confusion
|
|
|
|
type PrivateKeyType string
|
2015-10-05 17:00:45 +00:00
|
|
|
|
2015-12-14 18:16:47 +00:00
|
|
|
//Well-known PrivateKeyTypes
|
2015-06-17 16:43:36 +00:00
|
|
|
const (
|
2015-12-14 18:16:47 +00:00
|
|
|
UnknownPrivateKey PrivateKeyType = ""
|
|
|
|
RSAPrivateKey PrivateKeyType = "rsa"
|
|
|
|
ECPrivateKey PrivateKeyType = "ec"
|
2015-10-05 17:00:45 +00:00
|
|
|
)
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2015-10-05 17:00:45 +00:00
|
|
|
// TLSUsage controls whether the intended usage of a *tls.Config
|
|
|
|
// returned from ParsedCertBundle.GetTLSConfig is for server use,
|
|
|
|
// client use, or both, which affects which values are set
|
|
|
|
type TLSUsage int
|
|
|
|
|
2015-12-14 18:16:47 +00:00
|
|
|
//Well-known TLSUsage types
|
2015-10-05 17:00:45 +00:00
|
|
|
const (
|
2015-06-19 20:06:56 +00:00
|
|
|
TLSUnknown TLSUsage = 0
|
|
|
|
TLSServer TLSUsage = 1 << iota
|
2015-06-17 16:43:36 +00:00
|
|
|
TLSClient
|
|
|
|
)
|
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
//BlockType indicates the serialization format of the key
|
|
|
|
type BlockType string
|
2015-12-11 04:00:17 +00:00
|
|
|
|
|
|
|
//Well-known formats
|
|
|
|
const (
|
2015-12-11 20:43:14 +00:00
|
|
|
PKCS1Block BlockType = "RSA PRIVATE KEY"
|
|
|
|
PKCS8Block BlockType = "PRIVATE KEY"
|
|
|
|
ECBlock BlockType = "EC PRIVATE KEY"
|
2015-12-11 04:00:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
//ParsedPrivateKeyContainer allows common key setting for certs and CSRs
|
2015-11-19 19:15:36 +00:00
|
|
|
type ParsedPrivateKeyContainer interface {
|
2015-12-11 20:43:14 +00:00
|
|
|
SetParsedPrivateKey(crypto.Signer, PrivateKeyType, []byte)
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
// CertBlock contains the DER-encoded certificate and the PEM
|
|
|
|
// block's byte array
|
|
|
|
type CertBlock struct {
|
|
|
|
Certificate *x509.Certificate
|
|
|
|
Bytes []byte
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
// CertBundle contains a key type, a PEM-encoded private key,
|
|
|
|
// a PEM-encoded certificate, and a string-encoded serial number,
|
|
|
|
// returned from a successful Issue request
|
|
|
|
type CertBundle struct {
|
2015-12-14 18:16:47 +00:00
|
|
|
PrivateKeyType PrivateKeyType `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"`
|
|
|
|
Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"`
|
|
|
|
IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"`
|
2016-09-28 00:50:17 +00:00
|
|
|
CAChain []string `json:"ca_chain" structs:"ca_chain" mapstructure:"ca_chain"`
|
2015-12-14 18:16:47 +00:00
|
|
|
PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"`
|
|
|
|
SerialNumber string `json:"serial_number" structs:"serial_number" mapstructure:"serial_number"`
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParsedCertBundle contains a key type, a DER-encoded private key,
|
2015-08-29 13:03:02 +00:00
|
|
|
// and a DER-encoded certificate
|
2015-06-17 16:43:36 +00:00
|
|
|
type ParsedCertBundle struct {
|
2015-12-11 20:43:14 +00:00
|
|
|
PrivateKeyType PrivateKeyType
|
|
|
|
PrivateKeyFormat BlockType
|
2015-11-19 19:15:36 +00:00
|
|
|
PrivateKeyBytes []byte
|
|
|
|
PrivateKey crypto.Signer
|
2015-06-17 16:43:36 +00:00
|
|
|
CertificateBytes []byte
|
|
|
|
Certificate *x509.Certificate
|
2016-09-28 00:50:17 +00:00
|
|
|
CAChain []*CertBlock
|
2016-09-16 15:05:43 +00:00
|
|
|
SerialNumber *big.Int
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-08-29 13:03:02 +00:00
|
|
|
// CSRBundle contains a key type, a PEM-encoded private key,
|
|
|
|
// and a PEM-encoded CSR
|
|
|
|
type CSRBundle struct {
|
2015-12-14 18:16:47 +00:00
|
|
|
PrivateKeyType PrivateKeyType `json:"private_key_type" structs:"private_key_type" mapstructure:"private_key_type"`
|
|
|
|
CSR string `json:"csr" structs:"csr" mapstructure:"csr"`
|
|
|
|
PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"`
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParsedCSRBundle contains a key type, a DER-encoded private key,
|
|
|
|
// and a DER-encoded certificate request
|
|
|
|
type ParsedCSRBundle struct {
|
2015-12-11 20:43:14 +00:00
|
|
|
PrivateKeyType PrivateKeyType
|
2015-11-19 19:15:36 +00:00
|
|
|
PrivateKeyBytes []byte
|
|
|
|
PrivateKey crypto.Signer
|
|
|
|
CSRBytes []byte
|
|
|
|
CSR *x509.CertificateRequest
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
// ToPEMBundle converts a string-based certificate bundle
|
|
|
|
// to a PEM-based string certificate bundle in trust path
|
|
|
|
// order, leaf certificate first
|
|
|
|
func (c *CertBundle) ToPEMBundle() string {
|
|
|
|
var result []string
|
|
|
|
|
|
|
|
if len(c.PrivateKey) > 0 {
|
|
|
|
result = append(result, c.PrivateKey)
|
|
|
|
}
|
|
|
|
if len(c.Certificate) > 0 {
|
|
|
|
result = append(result, c.Certificate)
|
|
|
|
}
|
|
|
|
if len(c.CAChain) > 0 {
|
|
|
|
result = append(result, c.CAChain...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(result, "\n")
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
// ToParsedCertBundle converts a string-based certificate bundle
|
|
|
|
// to a byte-based raw certificate bundle
|
|
|
|
func (c *CertBundle) ToParsedCertBundle() (*ParsedCertBundle, error) {
|
|
|
|
result := &ParsedCertBundle{}
|
|
|
|
var err error
|
|
|
|
var pemBlock *pem.Block
|
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
if len(c.PrivateKey) > 0 {
|
|
|
|
pemBlock, _ = pem.Decode([]byte(c.PrivateKey))
|
|
|
|
if pemBlock == nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Error decoding private key from cert bundle"}
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
2015-12-11 20:43:14 +00:00
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
result.PrivateKeyBytes = pemBlock.Bytes
|
2015-12-11 20:43:14 +00:00
|
|
|
result.PrivateKeyFormat = BlockType(strings.TrimSpace(pemBlock.Type))
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
switch result.PrivateKeyFormat {
|
|
|
|
case ECBlock:
|
2015-12-14 18:16:47 +00:00
|
|
|
result.PrivateKeyType, c.PrivateKeyType = ECPrivateKey, ECPrivateKey
|
2015-12-11 20:43:14 +00:00
|
|
|
case PKCS1Block:
|
2015-12-14 18:16:47 +00:00
|
|
|
c.PrivateKeyType, result.PrivateKeyType = RSAPrivateKey, RSAPrivateKey
|
2015-12-11 20:43:14 +00:00
|
|
|
case PKCS8Block:
|
|
|
|
t, err := getPKCS8Type(pemBlock.Bytes)
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Error getting key type from pkcs#8: %v", err)}
|
2015-12-11 20:43:14 +00:00
|
|
|
}
|
|
|
|
result.PrivateKeyType = t
|
|
|
|
switch t {
|
|
|
|
case ECPrivateKey:
|
2015-12-14 18:16:47 +00:00
|
|
|
c.PrivateKeyType = ECPrivateKey
|
2015-12-11 20:43:14 +00:00
|
|
|
case RSAPrivateKey:
|
2015-12-14 18:16:47 +00:00
|
|
|
c.PrivateKeyType = RSAPrivateKey
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
2015-12-11 20:43:14 +00:00
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unsupported key block type: %s", pemBlock.Type)}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
result.PrivateKey, err = result.getSigner()
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Error getting signer: %s", err)}
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
if len(c.Certificate) > 0 {
|
|
|
|
pemBlock, _ = pem.Decode([]byte(c.Certificate))
|
|
|
|
if pemBlock == nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Error decoding certificate from cert bundle"}
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
|
|
|
result.CertificateBytes = pemBlock.Bytes
|
|
|
|
result.Certificate, err = x509.ParseCertificate(result.CertificateBytes)
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Error encountered parsing certificate bytes from raw bundle"}
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2016-09-28 00:50:17 +00:00
|
|
|
switch {
|
|
|
|
case len(c.CAChain) > 0:
|
|
|
|
for _, cert := range c.CAChain {
|
|
|
|
pemBlock, _ := pem.Decode([]byte(cert))
|
|
|
|
if pemBlock == nil {
|
|
|
|
return nil, errutil.UserError{"Error decoding certificate from cert bundle"}
|
|
|
|
}
|
|
|
|
|
|
|
|
parsedCert, err := x509.ParseCertificate(pemBlock.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errutil.UserError{"Error encountered parsing certificate bytes from raw bundle"}
|
|
|
|
}
|
2015-06-17 16:43:36 +00:00
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
certBlock := &CertBlock{
|
|
|
|
Bytes: pemBlock.Bytes,
|
|
|
|
Certificate: parsedCert,
|
|
|
|
}
|
|
|
|
result.CAChain = append(result.CAChain, certBlock)
|
|
|
|
}
|
|
|
|
|
|
|
|
// For backwards compabitibility
|
|
|
|
case len(c.IssuingCA) > 0:
|
2015-06-17 16:43:36 +00:00
|
|
|
pemBlock, _ = pem.Decode([]byte(c.IssuingCA))
|
|
|
|
if pemBlock == nil {
|
2016-09-28 00:50:17 +00:00
|
|
|
return nil, errutil.UserError{"Error decoding ca certificate from cert bundle"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2016-09-28 00:50:17 +00:00
|
|
|
|
|
|
|
parsedCert, err := x509.ParseCertificate(pemBlock.Bytes)
|
2015-06-17 16:43:36 +00:00
|
|
|
if err != nil {
|
2016-09-28 00:50:17 +00:00
|
|
|
return nil, errutil.UserError{"Error encountered parsing certificate bytes from raw bundle3"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
result.SerialNumber = result.Certificate.SerialNumber
|
|
|
|
|
|
|
|
certBlock := &CertBlock{
|
|
|
|
Bytes: pemBlock.Bytes,
|
|
|
|
Certificate: parsedCert,
|
|
|
|
}
|
|
|
|
result.CAChain = append(result.CAChain, certBlock)
|
|
|
|
}
|
2016-09-16 15:05:43 +00:00
|
|
|
|
|
|
|
// Populate if it isn't there already
|
2015-06-18 14:44:02 +00:00
|
|
|
if len(c.SerialNumber) == 0 && len(c.Certificate) > 0 {
|
2016-09-16 15:05:43 +00:00
|
|
|
c.SerialNumber = GetHexFormatted(result.Certificate.SerialNumber.Bytes(), ":")
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToCertBundle converts a byte-based raw DER certificate bundle
|
|
|
|
// to a PEM-based string certificate bundle
|
|
|
|
func (p *ParsedCertBundle) ToCertBundle() (*CertBundle, error) {
|
2015-06-18 14:44:02 +00:00
|
|
|
result := &CertBundle{}
|
|
|
|
block := pem.Block{
|
|
|
|
Type: "CERTIFICATE",
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
if p.Certificate != nil {
|
2016-09-16 15:05:43 +00:00
|
|
|
result.SerialNumber = strings.TrimSpace(GetHexFormatted(p.Certificate.SerialNumber.Bytes(), ":"))
|
2015-06-18 14:44:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if p.CertificateBytes != nil && len(p.CertificateBytes) > 0 {
|
|
|
|
block.Bytes = p.CertificateBytes
|
2015-06-19 20:06:56 +00:00
|
|
|
result.Certificate = strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
for _, caCert := range p.CAChain {
|
|
|
|
block.Bytes = caCert.Bytes
|
|
|
|
certificate := strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
|
|
|
|
|
|
|
result.CAChain = append(result.CAChain, certificate)
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 14:44:02 +00:00
|
|
|
if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 {
|
2015-12-11 20:43:14 +00:00
|
|
|
block.Type = string(p.PrivateKeyFormat)
|
2015-06-18 14:44:02 +00:00
|
|
|
block.Bytes = p.PrivateKeyBytes
|
2015-12-14 18:16:47 +00:00
|
|
|
result.PrivateKeyType = p.PrivateKeyType
|
2015-12-11 20:43:14 +00:00
|
|
|
|
|
|
|
//Handle bundle not parsed by us
|
|
|
|
if block.Type == "" {
|
|
|
|
switch p.PrivateKeyType {
|
|
|
|
case ECPrivateKey:
|
|
|
|
block.Type = string(ECBlock)
|
|
|
|
case RSAPrivateKey:
|
|
|
|
block.Type = string(PKCS1Block)
|
|
|
|
}
|
2015-12-09 20:41:32 +00:00
|
|
|
}
|
2015-12-11 22:22:43 +00:00
|
|
|
|
2015-06-19 20:06:56 +00:00
|
|
|
result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
// Verify checks if the parsed bundle is valid. It validates the public
|
2017-05-10 14:28:35 +00:00
|
|
|
// key of the certificate to the private key and checks the certificate trust
|
2016-09-28 00:50:17 +00:00
|
|
|
// chain for path issues.
|
|
|
|
func (p *ParsedCertBundle) Verify() error {
|
|
|
|
// If private key exists, check if it matches the public key of cert
|
|
|
|
if p.PrivateKey != nil && p.Certificate != nil {
|
|
|
|
equal, err := ComparePublicKeys(p.Certificate.PublicKey, p.PrivateKey.Public())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not compare public and private keys: %s", err)
|
|
|
|
}
|
|
|
|
if !equal {
|
|
|
|
return fmt.Errorf("Public key of certificate does not match private key")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
certPath := p.GetCertificatePath()
|
|
|
|
if len(certPath) > 1 {
|
|
|
|
for i, caCert := range certPath[1:] {
|
|
|
|
if !caCert.Certificate.IsCA {
|
|
|
|
return fmt.Errorf("certificate %d of certificate chain is not a certificate authority", i+1)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(certPath[i].Certificate.AuthorityKeyId, caCert.Certificate.SubjectKeyId) {
|
|
|
|
return fmt.Errorf("certificate %d of certificate chain ca trust path is incorrect (%s/%s)",
|
|
|
|
i+1, certPath[i].Certificate.Subject.CommonName, caCert.Certificate.Subject.CommonName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ParsedCertBundle) GetCertificatePath() []*CertBlock {
|
|
|
|
var certPath []*CertBlock
|
|
|
|
|
|
|
|
certPath = append(certPath, &CertBlock{
|
|
|
|
Certificate: p.Certificate,
|
|
|
|
Bytes: p.CertificateBytes,
|
|
|
|
})
|
|
|
|
|
|
|
|
if len(p.CAChain) > 0 {
|
|
|
|
// Root CA puts itself in the chain
|
|
|
|
if p.CAChain[0].Certificate.SerialNumber != p.Certificate.SerialNumber {
|
|
|
|
certPath = append(certPath, p.CAChain...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return certPath
|
|
|
|
}
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
// GetSigner returns a crypto.Signer corresponding to the private key
|
|
|
|
// contained in this ParsedCertBundle. The Signer contains a Public() function
|
|
|
|
// for getting the corresponding public. The Signer can also be
|
|
|
|
// type-converted to private keys
|
|
|
|
func (p *ParsedCertBundle) getSigner() (crypto.Signer, error) {
|
|
|
|
var signer crypto.Signer
|
|
|
|
var err error
|
2015-11-16 21:32:49 +00:00
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
if p.PrivateKeyBytes == nil || len(p.PrivateKeyBytes) == 0 {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Given parsed cert bundle does not have private key information"}
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
switch p.PrivateKeyFormat {
|
|
|
|
case ECBlock:
|
2015-11-19 19:15:36 +00:00
|
|
|
signer, err = x509.ParseECPrivateKey(p.PrivateKeyBytes)
|
2015-06-17 16:43:36 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unable to parse CA's private EC key: %s", err)}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2015-06-18 14:44:02 +00:00
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
case PKCS1Block:
|
2015-11-19 19:15:36 +00:00
|
|
|
signer, err = x509.ParsePKCS1PrivateKey(p.PrivateKeyBytes)
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unable to parse CA's private RSA key: %s", err)}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2015-06-18 14:44:02 +00:00
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
case PKCS8Block:
|
|
|
|
if k, err := x509.ParsePKCS8PrivateKey(p.PrivateKeyBytes); err == nil {
|
|
|
|
switch k := k.(type) {
|
|
|
|
case *rsa.PrivateKey, *ecdsa.PrivateKey:
|
|
|
|
return k.(crypto.Signer), nil
|
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Found unknown private key type in pkcs#8 wrapping"}
|
2015-12-11 20:43:14 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)}
|
2015-11-19 19:15:36 +00:00
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Unable to determine type of private key; only RSA and EC are supported"}
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
2015-11-19 19:15:36 +00:00
|
|
|
return signer, nil
|
2015-11-16 21:32:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
// SetParsedPrivateKey sets the private key parameters on the bundle
|
2015-12-11 20:43:14 +00:00
|
|
|
func (p *ParsedCertBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) {
|
2015-11-19 19:15:36 +00:00
|
|
|
p.PrivateKey = privateKey
|
2015-12-14 13:17:20 +00:00
|
|
|
p.PrivateKeyType = privateKeyType
|
2015-11-19 19:15:36 +00:00
|
|
|
p.PrivateKeyBytes = privateKeyBytes
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
func getPKCS8Type(bs []byte) (PrivateKeyType, error) {
|
|
|
|
k, err := x509.ParsePKCS8PrivateKey(bs)
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return UnknownPrivateKey, errutil.UserError{fmt.Sprintf("Failed to parse pkcs#8 key: %v", err)}
|
2015-12-11 20:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch k.(type) {
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
return ECPrivateKey, nil
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
return RSAPrivateKey, nil
|
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return UnknownPrivateKey, errutil.UserError{"Found unknown private key type in pkcs#8 wrapping"}
|
2015-12-11 20:43:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-29 13:03:02 +00:00
|
|
|
// ToParsedCSRBundle converts a string-based CSR bundle
|
|
|
|
// to a byte-based raw CSR bundle
|
|
|
|
func (c *CSRBundle) ToParsedCSRBundle() (*ParsedCSRBundle, error) {
|
|
|
|
result := &ParsedCSRBundle{}
|
|
|
|
var err error
|
|
|
|
var pemBlock *pem.Block
|
|
|
|
|
|
|
|
if len(c.PrivateKey) > 0 {
|
|
|
|
pemBlock, _ = pem.Decode([]byte(c.PrivateKey))
|
|
|
|
if pemBlock == nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Error decoding private key from cert bundle"}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
result.PrivateKeyBytes = pemBlock.Bytes
|
|
|
|
|
2015-12-11 20:43:14 +00:00
|
|
|
switch BlockType(pemBlock.Type) {
|
|
|
|
case ECBlock:
|
2015-08-29 13:03:02 +00:00
|
|
|
result.PrivateKeyType = ECPrivateKey
|
2015-12-11 20:43:14 +00:00
|
|
|
case PKCS1Block:
|
2015-08-29 13:03:02 +00:00
|
|
|
result.PrivateKeyType = RSAPrivateKey
|
|
|
|
default:
|
|
|
|
// Try to figure it out and correct
|
|
|
|
if _, err := x509.ParseECPrivateKey(pemBlock.Bytes); err == nil {
|
|
|
|
result.PrivateKeyType = ECPrivateKey
|
|
|
|
c.PrivateKeyType = "ec"
|
|
|
|
} else if _, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil {
|
|
|
|
result.PrivateKeyType = RSAPrivateKey
|
|
|
|
c.PrivateKeyType = "rsa"
|
|
|
|
} else {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unknown private key type in bundle: %s", c.PrivateKeyType)}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.PrivateKey, err = result.getSigner()
|
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Error getting signer: %s", err)}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.CSR) > 0 {
|
|
|
|
pemBlock, _ = pem.Decode([]byte(c.CSR))
|
|
|
|
if pemBlock == nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Error decoding certificate from cert bundle"}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
result.CSRBytes = pemBlock.Bytes
|
|
|
|
result.CSR, err = x509.ParseCertificateRequest(result.CSRBytes)
|
|
|
|
if err != nil {
|
2017-04-28 12:30:24 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Error encountered parsing certificate bytes from raw bundle: %v", err)}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToCSRBundle converts a byte-based raw DER certificate bundle
|
|
|
|
// to a PEM-based string certificate bundle
|
|
|
|
func (p *ParsedCSRBundle) ToCSRBundle() (*CSRBundle, error) {
|
|
|
|
result := &CSRBundle{}
|
|
|
|
block := pem.Block{
|
|
|
|
Type: "CERTIFICATE REQUEST",
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.CSRBytes != nil && len(p.CSRBytes) > 0 {
|
|
|
|
block.Bytes = p.CSRBytes
|
|
|
|
result.CSR = strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 {
|
|
|
|
block.Bytes = p.PrivateKeyBytes
|
|
|
|
switch p.PrivateKeyType {
|
|
|
|
case RSAPrivateKey:
|
|
|
|
result.PrivateKeyType = "rsa"
|
|
|
|
block.Type = "RSA PRIVATE KEY"
|
|
|
|
case ECPrivateKey:
|
|
|
|
result.PrivateKeyType = "ec"
|
|
|
|
block.Type = "EC PRIVATE KEY"
|
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.InternalError{"Could not determine private key type when creating block"}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
result.PrivateKey = strings.TrimSpace(string(pem.EncodeToMemory(&block)))
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSigner returns a crypto.Signer corresponding to the private key
|
|
|
|
// contained in this ParsedCSRBundle. The Signer contains a Public() function
|
|
|
|
// for getting the corresponding public. The Signer can also be
|
|
|
|
// type-converted to private keys
|
2015-11-19 19:15:36 +00:00
|
|
|
func (p *ParsedCSRBundle) getSigner() (crypto.Signer, error) {
|
2015-08-29 13:03:02 +00:00
|
|
|
var signer crypto.Signer
|
|
|
|
var err error
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
if p.PrivateKeyBytes == nil || len(p.PrivateKeyBytes) == 0 {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Given parsed cert bundle does not have private key information"}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
switch p.PrivateKeyType {
|
2015-08-29 13:03:02 +00:00
|
|
|
case ECPrivateKey:
|
2015-11-19 19:15:36 +00:00
|
|
|
signer, err = x509.ParseECPrivateKey(p.PrivateKeyBytes)
|
2015-08-29 13:03:02 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unable to parse CA's private EC key: %s", err)}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case RSAPrivateKey:
|
2015-11-19 19:15:36 +00:00
|
|
|
signer, err = x509.ParsePKCS1PrivateKey(p.PrivateKeyBytes)
|
2015-08-29 13:03:02 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{fmt.Sprintf("Unable to parse CA's private RSA key: %s", err)}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2016-07-28 19:19:27 +00:00
|
|
|
return nil, errutil.UserError{"Unable to determine type of private key; only RSA and EC are supported"}
|
2015-08-29 13:03:02 +00:00
|
|
|
}
|
|
|
|
return signer, nil
|
|
|
|
}
|
|
|
|
|
2015-11-19 19:15:36 +00:00
|
|
|
// SetParsedPrivateKey sets the private key parameters on the bundle
|
2015-12-11 20:43:14 +00:00
|
|
|
func (p *ParsedCSRBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) {
|
2015-11-19 19:15:36 +00:00
|
|
|
p.PrivateKey = privateKey
|
|
|
|
p.PrivateKeyType = privateKeyType
|
|
|
|
p.PrivateKeyBytes = privateKeyBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTLSConfig returns a TLS config generally suitable for client
|
|
|
|
// authentiation. The returned TLS config can be modified slightly
|
|
|
|
// to be made suitable for a server requiring client authentication;
|
|
|
|
// specifically, you should set the value of ClientAuth in the returned
|
|
|
|
// config to match your needs.
|
|
|
|
func (p *ParsedCertBundle) GetTLSConfig(usage TLSUsage) (*tls.Config, error) {
|
|
|
|
tlsCert := tls.Certificate{
|
|
|
|
Certificate: [][]byte{},
|
|
|
|
}
|
|
|
|
|
|
|
|
tlsConfig := &tls.Config{
|
2016-07-12 23:32:47 +00:00
|
|
|
MinVersion: tls.VersionTLS12,
|
2015-11-19 19:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if p.Certificate != nil {
|
|
|
|
tlsCert.Leaf = p.Certificate
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.PrivateKey != nil {
|
|
|
|
tlsCert.PrivateKey = p.PrivateKey
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.CertificateBytes != nil && len(p.CertificateBytes) > 0 {
|
|
|
|
tlsCert.Certificate = append(tlsCert.Certificate, p.CertificateBytes)
|
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
if len(p.CAChain) > 0 {
|
|
|
|
for _, cert := range p.CAChain {
|
|
|
|
tlsCert.Certificate = append(tlsCert.Certificate, cert.Bytes)
|
|
|
|
}
|
2015-11-19 19:15:36 +00:00
|
|
|
|
|
|
|
// Technically we only need one cert, but this doesn't duplicate code
|
|
|
|
certBundle, err := p.ToCertBundle()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error converting parsed bundle to string bundle when getting TLS config: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
caPool := x509.NewCertPool()
|
2016-09-28 00:50:17 +00:00
|
|
|
ok := caPool.AppendCertsFromPEM([]byte(certBundle.CAChain[0]))
|
2015-11-19 19:15:36 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("Could not append CA certificate")
|
|
|
|
}
|
|
|
|
|
|
|
|
if usage&TLSServer > 0 {
|
|
|
|
tlsConfig.ClientCAs = caPool
|
|
|
|
tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven
|
|
|
|
}
|
|
|
|
if usage&TLSClient > 0 {
|
|
|
|
tlsConfig.RootCAs = caPool
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if tlsCert.Certificate != nil && len(tlsCert.Certificate) > 0 {
|
|
|
|
tlsConfig.Certificates = []tls.Certificate{tlsCert}
|
|
|
|
tlsConfig.BuildNameToCertificate()
|
|
|
|
}
|
|
|
|
|
|
|
|
return tlsConfig, nil
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:43:36 +00:00
|
|
|
// IssueData is a structure that is suitable for marshaling into a request;
|
|
|
|
// either via JSON, or into a map[string]interface{} via the structs package
|
|
|
|
type IssueData struct {
|
2015-08-29 13:03:02 +00:00
|
|
|
TTL string `json:"ttl" structs:"ttl" mapstructure:"ttl"`
|
2015-06-17 16:43:36 +00:00
|
|
|
CommonName string `json:"common_name" structs:"common_name" mapstructure:"common_name"`
|
2017-01-23 17:44:45 +00:00
|
|
|
OU string `json:"ou" structs:"ou" mapstructure:"ou"`
|
2015-06-17 16:43:36 +00:00
|
|
|
AltNames string `json:"alt_names" structs:"alt_names" mapstructure:"alt_names"`
|
|
|
|
IPSANs string `json:"ip_sans" structs:"ip_sans" mapstructure:"ip_sans"`
|
2015-10-02 19:47:45 +00:00
|
|
|
CSR string `json:"csr" structs:"csr" mapstructure:"csr"`
|
2015-06-17 16:43:36 +00:00
|
|
|
}
|