2019-09-20 14:26:05 +02:00
// Package pkcs8 implements functions to parse and convert private keys in PKCS#8 format, as defined in RFC5208 and RFC5958
package pkcs8
import (
2019-11-11 16:04:53 +01:00
"crypto"
2019-09-20 14:26:05 +02:00
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
2019-11-11 16:04:53 +01:00
"crypto/x509/pkix"
2019-09-20 14:26:05 +02:00
"encoding/asn1"
"errors"
"fmt"
)
2019-11-11 16:04:53 +01:00
// DefaultOpts are the default options for encrypting a key if none are given.
// The defaults can be changed by the library user.
var DefaultOpts = & Opts {
Cipher : AES256CBC ,
KDFOpts : PBKDF2Opts {
SaltSize : 8 ,
IterationCount : 10000 ,
HMACHash : crypto . SHA256 ,
} ,
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// KDFOpts contains options for a key derivation function.
// An implementation of this interface must be specified when encrypting a PKCS#8 key.
type KDFOpts interface {
// DeriveKey derives a key of size bytes from the given password and salt.
// It returns the key and the ASN.1-encodable parameters used.
DeriveKey ( password , salt [ ] byte , size int ) ( key [ ] byte , params KDFParameters , err error )
// GetSaltSize returns the salt size specified.
GetSaltSize ( ) int
// OID returns the OID of the KDF specified.
OID ( ) asn1 . ObjectIdentifier
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// KDFParameters contains parameters (salt, etc.) for a key deriviation function.
// It must be a ASN.1-decodable structure.
// An implementation of this interface is created when decoding an encrypted PKCS#8 key.
type KDFParameters interface {
// DeriveKey derives a key of size bytes from the given password.
// It uses the salt from the decoded parameters.
DeriveKey ( password [ ] byte , size int ) ( key [ ] byte , err error )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
var kdfs = make ( map [ string ] func ( ) KDFParameters )
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// RegisterKDF registers a function that returns a new instance of the given KDF
// parameters. This allows the library to support client-provided KDFs.
func RegisterKDF ( oid asn1 . ObjectIdentifier , params func ( ) KDFParameters ) {
kdfs [ oid . String ( ) ] = params
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
// Cipher represents a cipher for encrypting the key material.
type Cipher interface {
// IVSize returns the IV size of the cipher, in bytes.
IVSize ( ) int
// KeySize returns the key size of the cipher, in bytes.
KeySize ( ) int
// Encrypt encrypts the key material.
Encrypt ( key , iv , plaintext [ ] byte ) ( [ ] byte , error )
// Decrypt decrypts the key material.
Decrypt ( key , iv , ciphertext [ ] byte ) ( [ ] byte , error )
// OID returns the OID of the cipher specified.
OID ( ) asn1 . ObjectIdentifier
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
var ciphers = make ( map [ string ] func ( ) Cipher )
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// RegisterCipher registers a function that returns a new instance of the given
// cipher. This allows the library to support client-provided ciphers.
func RegisterCipher ( oid asn1 . ObjectIdentifier , cipher func ( ) Cipher ) {
ciphers [ oid . String ( ) ] = cipher
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
// Opts contains options for encrypting a PKCS#8 key.
type Opts struct {
Cipher Cipher
KDFOpts KDFOpts
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
// Unecrypted PKCS8
var (
oidPBES2 = asn1 . ObjectIdentifier { 1 , 2 , 840 , 113549 , 1 , 5 , 13 }
)
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
type encryptedPrivateKeyInfo struct {
EncryptionAlgorithm pkix . AlgorithmIdentifier
EncryptedData [ ] byte
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
type pbes2Params struct {
KeyDerivationFunc pkix . AlgorithmIdentifier
EncryptionScheme pkix . AlgorithmIdentifier
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
type privateKeyInfo struct {
Version int
PrivateKeyAlgorithm pkix . AlgorithmIdentifier
PrivateKey [ ] byte
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
func parseKeyDerivationFunc ( keyDerivationFunc pkix . AlgorithmIdentifier ) ( KDFParameters , error ) {
oid := keyDerivationFunc . Algorithm . String ( )
newParams , ok := kdfs [ oid ]
2019-09-20 14:26:05 +02:00
if ! ok {
2019-11-11 16:04:53 +01:00
return nil , fmt . Errorf ( "pkcs8: unsupported KDF (OID: %s)" , oid )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
params := newParams ( )
_ , err := asn1 . Unmarshal ( keyDerivationFunc . Parameters . FullBytes , params )
2019-09-20 14:26:05 +02:00
if err != nil {
2019-11-11 16:04:53 +01:00
return nil , errors . New ( "pkcs8: invalid KDF parameters" )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
return params , nil
}
func parseEncryptionScheme ( encryptionScheme pkix . AlgorithmIdentifier ) ( Cipher , [ ] byte , error ) {
oid := encryptionScheme . Algorithm . String ( )
newCipher , ok := ciphers [ oid ]
2019-09-20 14:26:05 +02:00
if ! ok {
2019-11-11 16:04:53 +01:00
return nil , nil , fmt . Errorf ( "pkcs8: unsupported cipher (OID: %s)" , oid )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
cipher := newCipher ( )
var iv [ ] byte
if _ , err := asn1 . Unmarshal ( encryptionScheme . Parameters . FullBytes , & iv ) ; err != nil {
return nil , nil , errors . New ( "pkcs8: invalid cipher parameters" )
}
return cipher , iv , nil
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
// ParsePrivateKey parses a DER-encoded PKCS#8 private key.
// Password can be nil.
// This is equivalent to ParsePKCS8PrivateKey.
func ParsePrivateKey ( der [ ] byte , password [ ] byte ) ( interface { } , KDFParameters , error ) {
2019-09-20 14:26:05 +02:00
// No password provided, assume the private key is unencrypted
2019-11-11 16:04:53 +01:00
if len ( password ) == 0 {
privateKey , err := x509 . ParsePKCS8PrivateKey ( der )
return privateKey , nil , err
2019-09-20 14:26:05 +02:00
}
// Use the password provided to decrypt the private key
var privKey encryptedPrivateKeyInfo
if _ , err := asn1 . Unmarshal ( der , & privKey ) ; err != nil {
2019-11-11 16:04:53 +01:00
return nil , nil , errors . New ( "pkcs8: only PKCS #5 v2.0 supported" )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
if ! privKey . EncryptionAlgorithm . Algorithm . Equal ( oidPBES2 ) {
return nil , nil , errors . New ( "pkcs8: only PBES2 supported" )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
var params pbes2Params
if _ , err := asn1 . Unmarshal ( privKey . EncryptionAlgorithm . Parameters . FullBytes , & params ) ; err != nil {
return nil , nil , errors . New ( "pkcs8: invalid PBES2 parameters" )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
cipher , iv , err := parseEncryptionScheme ( params . EncryptionScheme )
if err != nil {
return nil , nil , err
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
kdfParams , err := parseKeyDerivationFunc ( params . KeyDerivationFunc )
if err != nil {
return nil , nil , err
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
keySize := cipher . KeySize ( )
symkey , err := kdfParams . DeriveKey ( password , keySize )
if err != nil {
return nil , nil , err
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
encryptedKey := privKey . EncryptedData
decryptedKey , err := cipher . Decrypt ( symkey , iv , encryptedKey )
2019-09-20 14:26:05 +02:00
if err != nil {
2019-11-11 16:04:53 +01:00
return nil , nil , err
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
key , err := x509 . ParsePKCS8PrivateKey ( decryptedKey )
2019-09-20 14:26:05 +02:00
if err != nil {
2019-11-11 16:04:53 +01:00
return nil , nil , errors . New ( "pkcs8: incorrect password" )
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
return key , kdfParams , nil
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
// MarshalPrivateKey encodes a private key into DER-encoded PKCS#8 with the given options.
// Password can be nil.
func MarshalPrivateKey ( priv interface { } , password [ ] byte , opts * Opts ) ( [ ] byte , error ) {
if len ( password ) == 0 {
return x509 . MarshalPKCS8PrivateKey ( priv )
}
if opts == nil {
opts = DefaultOpts
}
2019-09-20 14:26:05 +02:00
// Convert private key into PKCS8 format
2019-11-11 16:04:53 +01:00
pkey , err := x509 . MarshalPKCS8PrivateKey ( priv )
2019-09-20 14:26:05 +02:00
if err != nil {
return nil , err
}
2019-11-11 16:04:53 +01:00
encAlg := opts . Cipher
salt := make ( [ ] byte , opts . KDFOpts . GetSaltSize ( ) )
2019-09-20 14:26:05 +02:00
_ , err = rand . Read ( salt )
if err != nil {
return nil , err
}
2019-11-11 16:04:53 +01:00
iv := make ( [ ] byte , encAlg . IVSize ( ) )
2019-09-20 14:26:05 +02:00
_ , err = rand . Read ( iv )
if err != nil {
return nil , err
}
2019-11-11 16:04:53 +01:00
key , kdfParams , err := opts . KDFOpts . DeriveKey ( password , salt , encAlg . KeySize ( ) )
if err != nil {
return nil , err
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
encryptedKey , err := encAlg . Encrypt ( key , iv , pkey )
if err != nil {
return nil , err
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
marshalledParams , err := asn1 . Marshal ( kdfParams )
if err != nil {
return nil , err
}
keyDerivationFunc := pkix . AlgorithmIdentifier {
Algorithm : opts . KDFOpts . OID ( ) ,
Parameters : asn1 . RawValue { FullBytes : marshalledParams } ,
}
marshalledIV , err := asn1 . Marshal ( iv )
if err != nil {
return nil , err
}
encryptionScheme := pkix . AlgorithmIdentifier {
Algorithm : encAlg . OID ( ) ,
Parameters : asn1 . RawValue { FullBytes : marshalledIV } ,
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
encryptionAlgorithmParams := pbes2Params {
EncryptionScheme : encryptionScheme ,
KeyDerivationFunc : keyDerivationFunc ,
}
marshalledEncryptionAlgorithmParams , err := asn1 . Marshal ( encryptionAlgorithmParams )
2019-09-20 14:26:05 +02:00
if err != nil {
return nil , err
}
2019-11-11 16:04:53 +01:00
encryptionAlgorithm := pkix . AlgorithmIdentifier {
Algorithm : oidPBES2 ,
Parameters : asn1 . RawValue { FullBytes : marshalledEncryptionAlgorithmParams } ,
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
encryptedPkey := encryptedPrivateKeyInfo {
EncryptionAlgorithm : encryptionAlgorithm ,
EncryptedData : encryptedKey ,
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
return asn1 . Marshal ( encryptedPkey )
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// ParsePKCS8PrivateKey parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKey ( der [ ] byte , v ... [ ] byte ) ( interface { } , error ) {
var password [ ] byte
if len ( v ) > 0 {
password = v [ 0 ]
}
privateKey , _ , err := ParsePrivateKey ( der , password )
return privateKey , err
}
2019-09-20 14:26:05 +02:00
2019-11-11 16:04:53 +01:00
// ParsePKCS8PrivateKeyRSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKeyRSA ( der [ ] byte , v ... [ ] byte ) ( * rsa . PrivateKey , error ) {
key , err := ParsePKCS8PrivateKey ( der , v ... )
if err != nil {
return nil , err
}
typedKey , ok := key . ( * rsa . PrivateKey )
if ! ok {
return nil , errors . New ( "key block is not of type RSA" )
}
return typedKey , nil
}
// ParsePKCS8PrivateKeyECDSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.
func ParsePKCS8PrivateKeyECDSA ( der [ ] byte , v ... [ ] byte ) ( * ecdsa . PrivateKey , error ) {
key , err := ParsePKCS8PrivateKey ( der , v ... )
if err != nil {
return nil , err
}
typedKey , ok := key . ( * ecdsa . PrivateKey )
if ! ok {
return nil , errors . New ( "key block is not of type ECDSA" )
}
return typedKey , nil
2019-09-20 14:26:05 +02:00
}
// ConvertPrivateKeyToPKCS8 converts the private key into PKCS#8 format.
// To encrypt the private key, the password of []byte type should be provided as the second parameter.
//
// The only supported key types are RSA and ECDSA (*rsa.PrivateKey or *ecdsa.PrivateKey for priv)
func ConvertPrivateKeyToPKCS8 ( priv interface { } , v ... [ ] byte ) ( [ ] byte , error ) {
2019-11-11 16:04:53 +01:00
var password [ ] byte
if len ( v ) > 0 {
password = v [ 0 ]
2019-09-20 14:26:05 +02:00
}
2019-11-11 16:04:53 +01:00
return MarshalPrivateKey ( priv , password , nil )
2019-09-20 14:26:05 +02:00
}