2024-01-26 01:16:37 +00:00
|
|
|
package pkcs15
|
|
|
|
|
|
|
|
import (
|
2024-06-24 22:23:05 +00:00
|
|
|
"crypto"
|
2024-09-17 22:44:33 +00:00
|
|
|
"crypto/ecdsa"
|
2024-01-26 01:16:37 +00:00
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2024-06-24 22:23:05 +00:00
|
|
|
"reflect"
|
2024-09-17 22:44:33 +00:00
|
|
|
"slices"
|
2024-01-26 01:16:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errPemKeyBadBlock = errors.New("pkcs15: pem key: failed to decode pem block")
|
|
|
|
errPemKeyFailedToParse = errors.New("pkcs15: pem key: failed to parse key")
|
2024-09-17 22:44:33 +00:00
|
|
|
errPemKeyWrongBlockType = errors.New("pkcs15: pem key: unsupported pem block type")
|
|
|
|
errKeyWrongType = errors.New("pkcs15: pem key: unsupported key type")
|
2024-01-26 01:16:37 +00:00
|
|
|
|
|
|
|
errPemCertBadBlock = errors.New("pkcs15: pem cert: failed to decode pem block")
|
|
|
|
errPemCertFailedToParse = errors.New("pkcs15: pem cert: failed to parse cert")
|
|
|
|
)
|
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
var (
|
|
|
|
supportedRSASizes = []int{1024, 2048, 3072, 4096}
|
|
|
|
supportedECDSACurves = []string{"P-256", "P-384", "P-521"}
|
|
|
|
)
|
|
|
|
|
2024-01-26 01:16:37 +00:00
|
|
|
// pemKeyDecode attempts to decode a pem encoded byte slice and then attempts
|
2024-09-17 22:44:33 +00:00
|
|
|
// to parse a private key from the decoded pem block. an error is returned
|
|
|
|
// if any of these steps fail OR if the key is not supported.
|
2024-06-24 22:23:05 +00:00
|
|
|
func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) {
|
2024-01-26 01:16:37 +00:00
|
|
|
// decode
|
|
|
|
pemBlock, _ := pem.Decode([]byte(keyPem))
|
|
|
|
if pemBlock == nil {
|
|
|
|
return nil, errPemKeyBadBlock
|
|
|
|
}
|
|
|
|
|
|
|
|
// parsing depends on block type
|
2024-06-24 22:23:05 +00:00
|
|
|
var privateKey crypto.PrivateKey
|
2024-01-26 01:16:37 +00:00
|
|
|
|
|
|
|
switch pemBlock.Type {
|
|
|
|
case "RSA PRIVATE KEY": // PKCS1
|
2024-06-24 22:23:05 +00:00
|
|
|
rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
|
2024-01-26 01:16:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errPemKeyFailedToParse
|
|
|
|
}
|
|
|
|
|
|
|
|
// basic sanity check
|
|
|
|
err = rsaKey.Validate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err)
|
|
|
|
}
|
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
// verify supported rsa bitlen
|
|
|
|
if !slices.Contains(supportedRSASizes, rsaKey.N.BitLen()) {
|
|
|
|
return nil, errKeyWrongType
|
2024-01-26 01:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// good to go
|
2024-06-24 22:23:05 +00:00
|
|
|
privateKey = rsaKey
|
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
case "EC PRIVATE KEY": // SEC1, ASN.1
|
|
|
|
ecdKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errPemKeyFailedToParse
|
|
|
|
}
|
2024-06-24 22:23:05 +00:00
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
// verify supported curve name
|
|
|
|
if !slices.Contains(supportedECDSACurves, ecdKey.Curve.Params().Name) {
|
|
|
|
return nil, errKeyWrongType
|
|
|
|
}
|
2024-06-24 22:23:05 +00:00
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
// good to go
|
|
|
|
privateKey = ecdKey
|
2024-01-26 01:16:37 +00:00
|
|
|
|
|
|
|
case "PRIVATE KEY": // PKCS8
|
|
|
|
pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errPemKeyFailedToParse
|
|
|
|
}
|
|
|
|
|
|
|
|
switch pkcs8Key := pkcs8Key.(type) {
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
// basic sanity check
|
2024-06-24 22:23:05 +00:00
|
|
|
err = pkcs8Key.Validate()
|
2024-01-26 01:16:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err)
|
|
|
|
}
|
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
// verify supported rsa bitlen
|
|
|
|
if !slices.Contains(supportedRSASizes, pkcs8Key.N.BitLen()) {
|
|
|
|
return nil, errKeyWrongType
|
2024-01-26 01:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// good to go
|
2024-06-24 22:23:05 +00:00
|
|
|
privateKey = pkcs8Key
|
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
// verify supported curve name
|
|
|
|
if !slices.Contains(supportedECDSACurves, pkcs8Key.Curve.Params().Name) {
|
|
|
|
return nil, errKeyWrongType
|
|
|
|
}
|
2024-06-24 22:23:05 +00:00
|
|
|
|
2024-09-17 22:44:33 +00:00
|
|
|
// good to go
|
|
|
|
privateKey = pkcs8Key
|
2024-01-26 01:16:37 +00:00
|
|
|
|
|
|
|
default:
|
2024-09-17 22:44:33 +00:00
|
|
|
return nil, errKeyWrongType
|
2024-01-26 01:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, errPemKeyWrongBlockType
|
|
|
|
}
|
|
|
|
|
|
|
|
// if rsaKey is nil somehow, error
|
2024-06-24 22:23:05 +00:00
|
|
|
if reflect.ValueOf(privateKey).IsNil() {
|
2024-01-26 01:16:37 +00:00
|
|
|
return nil, errors.New("pkcs15: pem key: rsa key unexpectedly nil (report bug to project repo)")
|
|
|
|
}
|
|
|
|
|
|
|
|
// success!
|
2024-06-24 22:23:05 +00:00
|
|
|
return privateKey, nil
|
2024-01-26 01:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// pemCertDecode attempts to decode a pem encoded byte slice and then attempts
|
|
|
|
// to parse a certificate from it. The certificate is also check against the
|
|
|
|
// key that is passed in to verify the key matches the certificate.
|
|
|
|
func pemCertDecode(certPem, keyPem []byte) (*x509.Certificate, error) {
|
|
|
|
// verify key and cert make a valid key pair
|
|
|
|
_, err := tls.X509KeyPair(certPem, keyPem)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// discard rest, apc tool only bundles end cert
|
|
|
|
block, _ := pem.Decode(certPem)
|
|
|
|
if block == nil || block.Type != "CERTIFICATE" {
|
|
|
|
return nil, errPemCertBadBlock
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the cert struct
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errPemCertFailedToParse
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
}
|