update go to 1.20, bump deps

This commit is contained in:
Umputun
2023-08-07 13:03:16 -05:00
parent 7bc7703dc2
commit 2093f4ece2
582 changed files with 43315 additions and 12523 deletions
+6 -1
View File
@@ -1,8 +1,13 @@
arch:
- amd64
- ppc64le
language: go
go:
- "1.9.x"
- "1.10.x"
- "1.11.x"
- "1.12.x"
- "1.13.x"
- master
script:
+3 -2
View File
@@ -8,14 +8,15 @@ pkcs8 package fills the gap here. It implements functions to process private key
[**Godoc**](http://godoc.org/github.com/youmark/pkcs8)
## Installation
Supports Go 1.9+
Supports Go 1.10+. Release v1.1 is the last release supporting Go 1.9
```text
go get github.com/youmark/pkcs8
```
## dependency
This package depends on golang.org/x/crypto/pbkdf2 package. Use the following command to retrive pbkdf2 package
This package depends on golang.org/x/crypto/pbkdf2 and golang.org/x/crypto/scrypt packages. Use the following command to retrieve them
```text
go get golang.org/x/crypto/pbkdf2
go get golang.org/x/crypto/scrypt
```
+60
View File
@@ -0,0 +1,60 @@
package pkcs8
import (
"bytes"
"crypto/cipher"
"encoding/asn1"
)
type cipherWithBlock struct {
oid asn1.ObjectIdentifier
ivSize int
keySize int
newBlock func(key []byte) (cipher.Block, error)
}
func (c cipherWithBlock) IVSize() int {
return c.ivSize
}
func (c cipherWithBlock) KeySize() int {
return c.keySize
}
func (c cipherWithBlock) OID() asn1.ObjectIdentifier {
return c.oid
}
func (c cipherWithBlock) Encrypt(key, iv, plaintext []byte) ([]byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, err
}
return cbcEncrypt(block, key, iv, plaintext)
}
func (c cipherWithBlock) Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, err
}
return cbcDecrypt(block, key, iv, ciphertext)
}
func cbcEncrypt(block cipher.Block, key, iv, plaintext []byte) ([]byte, error) {
mode := cipher.NewCBCEncrypter(block, iv)
paddingLen := block.BlockSize() - (len(plaintext) % block.BlockSize())
ciphertext := make([]byte, len(plaintext)+paddingLen)
copy(ciphertext, plaintext)
copy(ciphertext[len(plaintext):], bytes.Repeat([]byte{byte(paddingLen)}, paddingLen))
mode.CryptBlocks(ciphertext, ciphertext)
return ciphertext, nil
}
func cbcDecrypt(block cipher.Block, key, iv, ciphertext []byte) ([]byte, error) {
mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
// TODO: remove padding
return plaintext, nil
}
+24
View File
@@ -0,0 +1,24 @@
package pkcs8
import (
"crypto/des"
"encoding/asn1"
)
var (
oidDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
)
func init() {
RegisterCipher(oidDESEDE3CBC, func() Cipher {
return TripleDESCBC
})
}
// TripleDESCBC is the 168-bit key 3DES cipher in CBC mode.
var TripleDESCBC = cipherWithBlock{
ivSize: des.BlockSize,
keySize: 24,
newBlock: des.NewTripleDESCipher,
oid: oidDESEDE3CBC,
}
+84
View File
@@ -0,0 +1,84 @@
package pkcs8
import (
"crypto/aes"
"encoding/asn1"
)
var (
oidAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
oidAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}
oidAES192CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 22}
oidAES192GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 26}
oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
oidAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46}
)
func init() {
RegisterCipher(oidAES128CBC, func() Cipher {
return AES128CBC
})
RegisterCipher(oidAES128GCM, func() Cipher {
return AES128GCM
})
RegisterCipher(oidAES192CBC, func() Cipher {
return AES192CBC
})
RegisterCipher(oidAES192GCM, func() Cipher {
return AES192GCM
})
RegisterCipher(oidAES256CBC, func() Cipher {
return AES256CBC
})
RegisterCipher(oidAES256GCM, func() Cipher {
return AES256GCM
})
}
// AES128CBC is the 128-bit key AES cipher in CBC mode.
var AES128CBC = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 16,
newBlock: aes.NewCipher,
oid: oidAES128CBC,
}
// AES128GCM is the 128-bit key AES cipher in GCM mode.
var AES128GCM = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 16,
newBlock: aes.NewCipher,
oid: oidAES128GCM,
}
// AES192CBC is the 192-bit key AES cipher in CBC mode.
var AES192CBC = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 24,
newBlock: aes.NewCipher,
oid: oidAES192CBC,
}
// AES192GCM is the 912-bit key AES cipher in GCM mode.
var AES192GCM = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 24,
newBlock: aes.NewCipher,
oid: oidAES192GCM,
}
// AES256CBC is the 256-bit key AES cipher in CBC mode.
var AES256CBC = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 32,
newBlock: aes.NewCipher,
oid: oidAES256CBC,
}
// AES256GCM is the 256-bit key AES cipher in GCM mode.
var AES256GCM = cipherWithBlock{
ivSize: aes.BlockSize,
keySize: 32,
newBlock: aes.NewCipher,
oid: oidAES256GCM,
}
+91
View File
@@ -0,0 +1,91 @@
package pkcs8
import (
"crypto"
"crypto/sha1"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"hash"
"golang.org/x/crypto/pbkdf2"
)
var (
oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
oidHMACWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 7}
oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9}
)
func init() {
RegisterKDF(oidPKCS5PBKDF2, func() KDFParameters {
return new(pbkdf2Params)
})
}
func newHashFromPRF(ai pkix.AlgorithmIdentifier) (func() hash.Hash, error) {
switch {
case len(ai.Algorithm) == 0 || ai.Algorithm.Equal(oidHMACWithSHA1):
return sha1.New, nil
case ai.Algorithm.Equal(oidHMACWithSHA256):
return sha256.New, nil
default:
return nil, errors.New("pkcs8: unsupported hash function")
}
}
func newPRFParamFromHash(h crypto.Hash) (pkix.AlgorithmIdentifier, error) {
switch h {
case crypto.SHA1:
return pkix.AlgorithmIdentifier{
Algorithm: oidHMACWithSHA1,
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
case crypto.SHA256:
return pkix.AlgorithmIdentifier{
Algorithm: oidHMACWithSHA256,
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
}
return pkix.AlgorithmIdentifier{}, errors.New("pkcs8: unsupported hash function")
}
type pbkdf2Params struct {
Salt []byte
IterationCount int
PRF pkix.AlgorithmIdentifier `asn1:"optional"`
}
func (p pbkdf2Params) DeriveKey(password []byte, size int) (key []byte, err error) {
h, err := newHashFromPRF(p.PRF)
if err != nil {
return nil, err
}
return pbkdf2.Key(password, p.Salt, p.IterationCount, size, h), nil
}
// PBKDF2Opts contains options for the PBKDF2 key derivation function.
type PBKDF2Opts struct {
SaltSize int
IterationCount int
HMACHash crypto.Hash
}
func (p PBKDF2Opts) DeriveKey(password, salt []byte, size int) (
key []byte, params KDFParameters, err error) {
key = pbkdf2.Key(password, salt, p.IterationCount, size, p.HMACHash.New)
prfParam, err := newPRFParamFromHash(p.HMACHash)
if err != nil {
return nil, nil, err
}
params = pbkdf2Params{salt, p.IterationCount, prfParam}
return key, params, nil
}
func (p PBKDF2Opts) GetSaltSize() int {
return p.SaltSize
}
func (p PBKDF2Opts) OID() asn1.ObjectIdentifier {
return oidPKCS5PBKDF2
}
+62
View File
@@ -0,0 +1,62 @@
package pkcs8
import (
"encoding/asn1"
"golang.org/x/crypto/scrypt"
)
var (
oidScrypt = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11591, 4, 11}
)
func init() {
RegisterKDF(oidScrypt, func() KDFParameters {
return new(scryptParams)
})
}
type scryptParams struct {
Salt []byte
CostParameter int
BlockSize int
ParallelizationParameter int
}
func (p scryptParams) DeriveKey(password []byte, size int) (key []byte, err error) {
return scrypt.Key(password, p.Salt, p.CostParameter, p.BlockSize,
p.ParallelizationParameter, size)
}
// ScryptOpts contains options for the scrypt key derivation function.
type ScryptOpts struct {
SaltSize int
CostParameter int
BlockSize int
ParallelizationParameter int
}
func (p ScryptOpts) DeriveKey(password, salt []byte, size int) (
key []byte, params KDFParameters, err error) {
key, err = scrypt.Key(password, salt, p.CostParameter, p.BlockSize,
p.ParallelizationParameter, size)
if err != nil {
return nil, nil, err
}
params = scryptParams{
BlockSize: p.BlockSize,
CostParameter: p.CostParameter,
ParallelizationParameter: p.ParallelizationParameter,
Salt: salt,
}
return key, params, nil
}
func (p ScryptOpts) GetSaltSize() int {
return p.SaltSize
}
func (p ScryptOpts) OID() asn1.ObjectIdentifier {
return oidScrypt
}
+239 -235
View File
@@ -2,116 +2,275 @@
package pkcs8
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"golang.org/x/crypto/pbkdf2"
"fmt"
)
// Copy from crypto/x509
var (
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
)
// 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,
},
}
// Copy from crypto/x509
var (
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
// 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
}
// Copy from crypto/x509
func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
switch curve {
case elliptic.P224():
return oidNamedCurveP224, true
case elliptic.P256():
return oidNamedCurveP256, true
case elliptic.P384():
return oidNamedCurveP384, true
case elliptic.P521():
return oidNamedCurveP521, true
}
// 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)
}
return nil, false
var kdfs = make(map[string]func() KDFParameters)
// 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
}
// 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
}
var ciphers = make(map[string]func() Cipher)
// 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
}
// Opts contains options for encrypting a PKCS#8 key.
type Opts struct {
Cipher Cipher
KDFOpts KDFOpts
}
// Unecrypted PKCS8
var (
oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
oidAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9}
oidDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
)
type ecPrivateKey struct {
Version int
PrivateKey []byte
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
type encryptedPrivateKeyInfo struct {
EncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedData []byte
}
type pbes2Params struct {
KeyDerivationFunc pkix.AlgorithmIdentifier
EncryptionScheme pkix.AlgorithmIdentifier
}
type privateKeyInfo struct {
Version int
PrivateKeyAlgorithm []asn1.ObjectIdentifier
PrivateKeyAlgorithm pkix.AlgorithmIdentifier
PrivateKey []byte
}
// Encrypted PKCS8
type prfParam struct {
IdPRF asn1.ObjectIdentifier
NullParam asn1.RawValue
func parseKeyDerivationFunc(keyDerivationFunc pkix.AlgorithmIdentifier) (KDFParameters, error) {
oid := keyDerivationFunc.Algorithm.String()
newParams, ok := kdfs[oid]
if !ok {
return nil, fmt.Errorf("pkcs8: unsupported KDF (OID: %s)", oid)
}
params := newParams()
_, err := asn1.Unmarshal(keyDerivationFunc.Parameters.FullBytes, params)
if err != nil {
return nil, errors.New("pkcs8: invalid KDF parameters")
}
return params, nil
}
type pbkdf2Params struct {
Salt []byte
IterationCount int
PrfParam prfParam `asn1:"optional"`
func parseEncryptionScheme(encryptionScheme pkix.AlgorithmIdentifier) (Cipher, []byte, error) {
oid := encryptionScheme.Algorithm.String()
newCipher, ok := ciphers[oid]
if !ok {
return nil, nil, fmt.Errorf("pkcs8: unsupported cipher (OID: %s)", oid)
}
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
}
type pbkdf2Algorithms struct {
IdPBKDF2 asn1.ObjectIdentifier
PBKDF2Params pbkdf2Params
// 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) {
// No password provided, assume the private key is unencrypted
if len(password) == 0 {
privateKey, err := x509.ParsePKCS8PrivateKey(der)
return privateKey, nil, err
}
// Use the password provided to decrypt the private key
var privKey encryptedPrivateKeyInfo
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
return nil, nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
}
if !privKey.EncryptionAlgorithm.Algorithm.Equal(oidPBES2) {
return nil, nil, errors.New("pkcs8: only PBES2 supported")
}
var params pbes2Params
if _, err := asn1.Unmarshal(privKey.EncryptionAlgorithm.Parameters.FullBytes, &params); err != nil {
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
}
cipher, iv, err := parseEncryptionScheme(params.EncryptionScheme)
if err != nil {
return nil, nil, err
}
kdfParams, err := parseKeyDerivationFunc(params.KeyDerivationFunc)
if err != nil {
return nil, nil, err
}
keySize := cipher.KeySize()
symkey, err := kdfParams.DeriveKey(password, keySize)
if err != nil {
return nil, nil, err
}
encryptedKey := privKey.EncryptedData
decryptedKey, err := cipher.Decrypt(symkey, iv, encryptedKey)
if err != nil {
return nil, nil, err
}
key, err := x509.ParsePKCS8PrivateKey(decryptedKey)
if err != nil {
return nil, nil, errors.New("pkcs8: incorrect password")
}
return key, kdfParams, nil
}
type pbkdf2Encs struct {
EncryAlgo asn1.ObjectIdentifier
IV []byte
// 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
}
// Convert private key into PKCS8 format
pkey, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, err
}
encAlg := opts.Cipher
salt := make([]byte, opts.KDFOpts.GetSaltSize())
_, err = rand.Read(salt)
if err != nil {
return nil, err
}
iv := make([]byte, encAlg.IVSize())
_, err = rand.Read(iv)
if err != nil {
return nil, err
}
key, kdfParams, err := opts.KDFOpts.DeriveKey(password, salt, encAlg.KeySize())
if err != nil {
return nil, err
}
encryptedKey, err := encAlg.Encrypt(key, iv, pkey)
if err != nil {
return nil, err
}
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},
}
encryptionAlgorithmParams := pbes2Params{
EncryptionScheme: encryptionScheme,
KeyDerivationFunc: keyDerivationFunc,
}
marshalledEncryptionAlgorithmParams, err := asn1.Marshal(encryptionAlgorithmParams)
if err != nil {
return nil, err
}
encryptionAlgorithm := pkix.AlgorithmIdentifier{
Algorithm: oidPBES2,
Parameters: asn1.RawValue{FullBytes: marshalledEncryptionAlgorithmParams},
}
encryptedPkey := encryptedPrivateKeyInfo{
EncryptionAlgorithm: encryptionAlgorithm,
EncryptedData: encryptedKey,
}
return asn1.Marshal(encryptedPkey)
}
type pbes2Params struct {
KeyDerivationFunc pbkdf2Algorithms
EncryptionScheme pbkdf2Encs
}
type pbes2Algorithms struct {
IdPBES2 asn1.ObjectIdentifier
PBES2Params pbes2Params
}
type encryptedPrivateKeyInfo struct {
EncryptionAlgorithm pbes2Algorithms
EncryptedData []byte
// 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
}
// 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.
//
// The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format.
func ParsePKCS8PrivateKeyRSA(der []byte, v ...[]byte) (*rsa.PrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
@@ -125,8 +284,6 @@ func ParsePKCS8PrivateKeyRSA(der []byte, v ...[]byte) (*rsa.PrivateKey, error) {
}
// 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.
//
// The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format.
func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, error) {
key, err := ParsePKCS8PrivateKey(der, v...)
if err != nil {
@@ -139,167 +296,14 @@ func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, erro
return typedKey, nil
}
// 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.
//
// The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format.
func ParsePKCS8PrivateKey(der []byte, v ...[]byte) (interface{}, error) {
// No password provided, assume the private key is unencrypted
if v == nil {
return x509.ParsePKCS8PrivateKey(der)
}
// Use the password provided to decrypt the private key
password := v[0]
var privKey encryptedPrivateKeyInfo
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
}
if !privKey.EncryptionAlgorithm.IdPBES2.Equal(oidPBES2) {
return nil, errors.New("pkcs8: only PBES2 supported")
}
if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IdPBKDF2.Equal(oidPKCS5PBKDF2) {
return nil, errors.New("pkcs8: only PBKDF2 supported")
}
encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme
kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params
iv := encParam.IV
salt := kdfParam.Salt
iter := kdfParam.IterationCount
keyHash := sha1.New
if kdfParam.PrfParam.IdPRF.Equal(oidHMACWithSHA256) {
keyHash = sha256.New
}
encryptedKey := privKey.EncryptedData
var symkey []byte
var block cipher.Block
var err error
switch {
case encParam.EncryAlgo.Equal(oidAES128CBC):
symkey = pbkdf2.Key(password, salt, iter, 16, keyHash)
block, err = aes.NewCipher(symkey)
case encParam.EncryAlgo.Equal(oidAES256CBC):
symkey = pbkdf2.Key(password, salt, iter, 32, keyHash)
block, err = aes.NewCipher(symkey)
case encParam.EncryAlgo.Equal(oidDESEDE3CBC):
symkey = pbkdf2.Key(password, salt, iter, 24, keyHash)
block, err = des.NewTripleDESCipher(symkey)
default:
return nil, errors.New("pkcs8: only AES-256-CBC, AES-128-CBC and DES-EDE3-CBC are supported")
}
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encryptedKey, encryptedKey)
key, err := x509.ParsePKCS8PrivateKey(encryptedKey)
if err != nil {
return nil, errors.New("pkcs8: incorrect password")
}
return key, nil
}
func convertPrivateKeyToPKCS8(priv interface{}) ([]byte, error) {
var pkey privateKeyInfo
switch priv := priv.(type) {
case *ecdsa.PrivateKey:
eckey, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return nil, err
}
oidNamedCurve, ok := oidFromNamedCurve(priv.Curve)
if !ok {
return nil, errors.New("pkcs8: unknown elliptic curve")
}
// Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0).
// But openssl set to v1 even publicKey is present
pkey.Version = 1
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2)
pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA
pkey.PrivateKeyAlgorithm[1] = oidNamedCurve
pkey.PrivateKey = eckey
case *rsa.PrivateKey:
// Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0).
// But openssl set to v1 even publicKey is present
pkey.Version = 0
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
pkey.PrivateKeyAlgorithm[0] = oidPublicKeyRSA
pkey.PrivateKey = x509.MarshalPKCS1PrivateKey(priv)
}
return asn1.Marshal(pkey)
}
func convertPrivateKeyToPKCS8Encrypted(priv interface{}, password []byte) ([]byte, error) {
// Convert private key into PKCS8 format
pkey, err := convertPrivateKeyToPKCS8(priv)
if err != nil {
return nil, err
}
// Calculate key from password based on PKCS5 algorithm
// Use 8 byte salt, 16 byte IV, and 2048 iteration
iter := 2048
salt := make([]byte, 8)
iv := make([]byte, 16)
_, err = rand.Read(salt)
if err != nil {
return nil, err
}
_, err = rand.Read(iv)
if err != nil {
return nil, err
}
key := pbkdf2.Key(password, salt, iter, 32, sha256.New)
// Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme
padding := aes.BlockSize - len(pkey)%aes.BlockSize
if padding > 0 {
n := len(pkey)
pkey = append(pkey, make([]byte, padding)...)
for i := 0; i < padding; i++ {
pkey[n+i] = byte(padding)
}
}
encryptedKey := make([]byte, len(pkey))
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encryptedKey, pkey)
// pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter, prfParam{oidHMACWithSHA256}}}
pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter, prfParam{oidHMACWithSHA256, asn1.RawValue{Tag: asn1.TagNull}}}}
pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv}
pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}}
encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey}
return asn1.Marshal(encryptedPkey)
}
// 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.PublicKey or *ecdsa.PublicKey for priv)
// The only supported key types are RSA and ECDSA (*rsa.PrivateKey or *ecdsa.PrivateKey for priv)
func ConvertPrivateKeyToPKCS8(priv interface{}, v ...[]byte) ([]byte, error) {
if v == nil {
return convertPrivateKeyToPKCS8(priv)
var password []byte
if len(v) > 0 {
password = v[0]
}
password := string(v[0])
return convertPrivateKeyToPKCS8Encrypted(priv, []byte(password))
return MarshalPrivateKey(priv, password, nil)
}