mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 12:16:20 +08:00
pkcs8: merge from emmansun/pkcs8
This commit is contained in:
parent
823cf4a470
commit
21b8f82a6e
7
pkcs8/README.md
Normal file
7
pkcs8/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
OpenSSL can generate private keys in both "traditional format" and PKCS#8 format. Newer applications are advised to use more secure PKCS#8 format. Go standard crypto package provides a [function](http://golang.org/pkg/crypto/x509/#ParsePKCS8PrivateKey) to parse private key in PKCS#8 format. There is a limitation to this function. It can only handle unencrypted PKCS#8 private keys. To use this function, the user has to save the private key in file without encryption, which is a bad practice to leave private keys unprotected on file systems. In addition, Go standard package lacks the functions to convert RSA/ECDSA private keys into PKCS#8 format.
|
||||
|
||||
pkcs8 package fills the gap here. It implements functions to process private keys in PKCS#8 format, as defined in [RFC5208](https://tools.ietf.org/html/rfc5208) and [RFC5958](https://tools.ietf.org/html/rfc5958). It can handle both unencrypted PKCS#8 PrivateKeyInfo format and EncryptedPrivateKeyInfo format with PKCS#5 (v2.0) algorithms.
|
||||
|
||||
## Credits
|
||||
This is a fork of [youmark/pkcs8](https://github.com/youmark/pkcs8), and we added support for ShangMi.
|
||||
|
164
pkcs8/cipher.go
Normal file
164
pkcs8/cipher.go
Normal file
@ -0,0 +1,164 @@
|
||||
package pkcs8
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/padding"
|
||||
)
|
||||
|
||||
func genRandom(len int) ([]byte, error) {
|
||||
value := make([]byte, len)
|
||||
_, err := rand.Read(value)
|
||||
return value, err
|
||||
}
|
||||
|
||||
type cipherWithBlock struct {
|
||||
oid asn1.ObjectIdentifier
|
||||
ivSize int
|
||||
keySize int
|
||||
newBlock func(key []byte) (cipher.Block, error)
|
||||
}
|
||||
|
||||
func (c cipherWithBlock) KeySize() int {
|
||||
return c.keySize
|
||||
}
|
||||
|
||||
func (c cipherWithBlock) OID() asn1.ObjectIdentifier {
|
||||
return c.oid
|
||||
}
|
||||
|
||||
func (c cipherWithBlock) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
iv, err := genRandom(c.ivSize)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ciphertext, err := cbcEncrypt(block, key, iv, plaintext)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
marshalledIV, err := asn1.Marshal(iv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
encryptionScheme := pkix.AlgorithmIdentifier{
|
||||
Algorithm: c.oid,
|
||||
Parameters: asn1.RawValue{FullBytes: marshalledIV},
|
||||
}
|
||||
|
||||
return &encryptionScheme, ciphertext, nil
|
||||
}
|
||||
|
||||
func (c cipherWithBlock) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var iv []byte
|
||||
if _, err := asn1.Unmarshal(parameters.FullBytes, &iv); err != nil {
|
||||
return nil, errors.New("pkcs8: invalid cipher parameters")
|
||||
}
|
||||
|
||||
return cbcDecrypt(block, key, iv, encryptedKey)
|
||||
}
|
||||
|
||||
func cbcEncrypt(block cipher.Block, key, iv, plaintext []byte) ([]byte, error) {
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
|
||||
plainText := pkcs7.Pad(plaintext)
|
||||
ciphertext := make([]byte, len(plainText))
|
||||
mode.CryptBlocks(ciphertext, plainText)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func cbcDecrypt(block cipher.Block, key, iv, ciphertext []byte) ([]byte, error) {
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
mode.CryptBlocks(plaintext, ciphertext)
|
||||
return pkcs7.Unpad(plaintext)
|
||||
}
|
||||
|
||||
type cipherWithGCM struct {
|
||||
oid asn1.ObjectIdentifier
|
||||
nonceSize int
|
||||
keySize int
|
||||
newBlock func(key []byte) (cipher.Block, error)
|
||||
}
|
||||
|
||||
// http://javadoc.iaik.tugraz.at/iaik_jce/current/index.html?iaik/security/cipher/GCMParameters.html
|
||||
type gcmParameters struct {
|
||||
Nonce []byte `asn1:"tag:4"`
|
||||
ICVLen int
|
||||
}
|
||||
|
||||
func (c cipherWithGCM) KeySize() int {
|
||||
return c.keySize
|
||||
}
|
||||
|
||||
func (c cipherWithGCM) OID() asn1.ObjectIdentifier {
|
||||
return c.oid
|
||||
}
|
||||
|
||||
func (c cipherWithGCM) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
nonce, err := genRandom(c.nonceSize)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCMWithNonceSize(block, c.nonceSize)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
||||
paramSeq := gcmParameters{
|
||||
Nonce: nonce,
|
||||
ICVLen: aead.Overhead(),
|
||||
}
|
||||
paramBytes, err := asn1.Marshal(paramSeq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
encryptionAlgorithm := pkix.AlgorithmIdentifier{
|
||||
Algorithm: c.oid,
|
||||
Parameters: asn1.RawValue{
|
||||
Tag: asn1.TagSequence,
|
||||
Bytes: paramBytes,
|
||||
},
|
||||
}
|
||||
return &encryptionAlgorithm, ciphertext, nil
|
||||
}
|
||||
|
||||
func (c cipherWithGCM) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := gcmParameters{}
|
||||
_, err = asn1.Unmarshal(parameters.Bytes, ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aead, err := cipher.NewGCMWithNonceSize(block, len(params.Nonce))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if params.ICVLen != aead.Overhead() {
|
||||
return nil, errors.New("pkcs8: invalid tag size")
|
||||
}
|
||||
|
||||
return aead.Open(nil, params.Nonce, encryptedKey, nil)
|
||||
}
|
84
pkcs8/cipher_aes.go
Normal file
84
pkcs8/cipher_aes.go
Normal 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 = cipherWithGCM{
|
||||
nonceSize: 12,
|
||||
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 = cipherWithGCM{
|
||||
nonceSize: 12,
|
||||
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 = cipherWithGCM{
|
||||
nonceSize: 12,
|
||||
keySize: 32,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES256GCM,
|
||||
}
|
36
pkcs8/cipher_des.go
Normal file
36
pkcs8/cipher_des.go
Normal file
@ -0,0 +1,36 @@
|
||||
package pkcs8
|
||||
|
||||
import (
|
||||
"crypto/des"
|
||||
"encoding/asn1"
|
||||
)
|
||||
|
||||
var (
|
||||
oidDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
|
||||
oidDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidDESCBC, func() Cipher {
|
||||
return &DESCBC
|
||||
})
|
||||
|
||||
RegisterCipher(oidDESEDE3CBC, func() Cipher {
|
||||
return &TripleDESCBC
|
||||
})
|
||||
}
|
||||
|
||||
var DESCBC = cipherWithBlock{
|
||||
ivSize: des.BlockSize,
|
||||
keySize: 8,
|
||||
newBlock: des.NewCipher,
|
||||
oid: oidDESCBC,
|
||||
}
|
||||
|
||||
// TripleDESCBC is the 168-bit key 3DES cipher in CBC mode.
|
||||
var TripleDESCBC = cipherWithBlock{
|
||||
ivSize: des.BlockSize,
|
||||
keySize: 24,
|
||||
newBlock: des.NewTripleDESCipher,
|
||||
oid: oidDESEDE3CBC,
|
||||
}
|
37
pkcs8/cipher_sm4.go
Normal file
37
pkcs8/cipher_sm4.go
Normal file
@ -0,0 +1,37 @@
|
||||
package pkcs8
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
)
|
||||
|
||||
var (
|
||||
oidSM4CBC = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 2}
|
||||
oidSM4GCM = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 8}
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidSM4CBC, func() Cipher {
|
||||
return &SM4CBC
|
||||
})
|
||||
RegisterCipher(oidSM4GCM, func() Cipher {
|
||||
return &SM4GCM
|
||||
})
|
||||
}
|
||||
|
||||
// SM4CBC is the 128-bit key SM4 cipher in CBC mode.
|
||||
var SM4CBC = cipherWithBlock{
|
||||
ivSize: sm4.BlockSize,
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4CBC,
|
||||
}
|
||||
|
||||
// SM4GCM is the 128-bit key SM4 cipher in GCM mode.
|
||||
var SM4GCM = cipherWithGCM{
|
||||
nonceSize: 12,
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4GCM,
|
||||
}
|
140
pkcs8/kdf_pbkdf2.go
Normal file
140
pkcs8/kdf_pbkdf2.go
Normal file
@ -0,0 +1,140 @@
|
||||
package pkcs8
|
||||
|
||||
//
|
||||
// Reference https://datatracker.ietf.org/doc/html/rfc8018#section-5.2
|
||||
//
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"hash"
|
||||
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
// http://gmssl.org/docs/oid.html
|
||||
var (
|
||||
oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
|
||||
oidHMACWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 7}
|
||||
oidHMACWithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 8}
|
||||
oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9}
|
||||
oidHMACWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 10}
|
||||
oidHMACWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 11}
|
||||
oidHMACWithSHA512_224 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 12}
|
||||
oidHMACWithSHA512_256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 13}
|
||||
oidHMACWithSM3 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 401, 2}
|
||||
)
|
||||
|
||||
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(oidHMACWithSHA224):
|
||||
return sha256.New224, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSHA256):
|
||||
return sha256.New, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSHA384):
|
||||
return sha512.New384, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSHA512):
|
||||
return sha512.New, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSHA512_224):
|
||||
return sha512.New512_224, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSHA512_256):
|
||||
return sha512.New512_256, nil
|
||||
case ai.Algorithm.Equal(oidHMACWithSM3):
|
||||
return sm3.New, nil
|
||||
default:
|
||||
return nil, errors.New("pkcs8: unsupported hash function")
|
||||
}
|
||||
}
|
||||
|
||||
func newPRFParamFromHash(h Hash) (pkix.AlgorithmIdentifier, error) {
|
||||
switch h {
|
||||
case SHA1:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA1,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA224:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA224,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA256:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA256,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA384:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA384,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA512:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA512,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA512_224:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA512_224,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SHA512_256:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSHA512_256,
|
||||
Parameters: asn1.RawValue{Tag: asn1.TagNull}}, nil
|
||||
case SM3:
|
||||
return pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidHMACWithSM3,
|
||||
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 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
|
||||
}
|
66
pkcs8/kdf_scrypt.go
Normal file
66
pkcs8/kdf_scrypt.go
Normal file
@ -0,0 +1,66 @@
|
||||
package pkcs8
|
||||
|
||||
//
|
||||
// Reference https://datatracker.ietf.org/doc/html/rfc7914
|
||||
//
|
||||
|
||||
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
|
||||
}
|
359
pkcs8/pkcs8.go
Normal file
359
pkcs8/pkcs8.go
Normal file
@ -0,0 +1,359 @@
|
||||
// Package pkcs8 implements functions to parse and convert private keys in PKCS#8 format, as defined in RFC5208 and RFC5958
|
||||
package pkcs8
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strconv"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// Hash identifies a cryptographic hash function that is implemented in another
|
||||
// package.
|
||||
type Hash uint
|
||||
|
||||
const (
|
||||
SHA1 Hash = 1 + iota
|
||||
SHA224
|
||||
SHA256
|
||||
SHA384
|
||||
SHA512
|
||||
SHA512_224
|
||||
SHA512_256
|
||||
SM3
|
||||
)
|
||||
|
||||
// HashFunc simply returns the value of h so that Hash implements SignerOpts.
|
||||
func (h Hash) HashFunc() Hash {
|
||||
return h
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash calculating the given hash function. New panics
|
||||
// if the hash function is not linked into the binary.
|
||||
func (h Hash) New() hash.Hash {
|
||||
switch h {
|
||||
case SM3:
|
||||
return sm3.New()
|
||||
case SHA1:
|
||||
return sha1.New()
|
||||
case SHA224:
|
||||
return sha256.New224()
|
||||
case SHA256:
|
||||
return sha256.New()
|
||||
case SHA384:
|
||||
return sha512.New384()
|
||||
case SHA512:
|
||||
return sha512.New()
|
||||
case SHA512_224:
|
||||
return sha512.New512_224()
|
||||
case SHA512_256:
|
||||
return sha512.New512_256()
|
||||
|
||||
}
|
||||
panic("pkcs8: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
|
||||
}
|
||||
|
||||
// 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: SHA256,
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// for encrypted private-key information
|
||||
type encryptedPrivateKeyInfo struct {
|
||||
EncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedData []byte
|
||||
}
|
||||
|
||||
// Cipher represents a cipher for encrypting the key material.
|
||||
type Cipher interface {
|
||||
// KeySize returns the key size of the cipher, in bytes.
|
||||
KeySize() int
|
||||
// Encrypt encrypts the key material.
|
||||
Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error)
|
||||
// Decrypt decrypts the key material.
|
||||
Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []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 (
|
||||
oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
|
||||
)
|
||||
|
||||
type pbes2Params struct {
|
||||
KeyDerivationFunc pkix.AlgorithmIdentifier
|
||||
EncryptionScheme pkix.AlgorithmIdentifier
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func parseEncryptionScheme(encryptionScheme *pkix.AlgorithmIdentifier) (Cipher, error) {
|
||||
oid := encryptionScheme.Algorithm.String()
|
||||
newCipher, ok := ciphers[oid]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("pkcs8: unsupported cipher (OID: %s)", oid)
|
||||
}
|
||||
cipher := newCipher()
|
||||
return cipher, nil
|
||||
}
|
||||
|
||||
// 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 := smx509.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 {
|
||||
if block, _ := pem.Decode(der); block != nil {
|
||||
return nil, nil, errors.New("pkcs8: this method just supports DER-encoded key")
|
||||
}
|
||||
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, ¶ms); err != nil {
|
||||
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
|
||||
}
|
||||
|
||||
cipher, err := parseEncryptionScheme(¶ms.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, ¶ms.EncryptionScheme.Parameters, encryptedKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key, err := smx509.ParsePKCS8PrivateKey(decryptedKey)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("pkcs8: incorrect password")
|
||||
}
|
||||
return key, kdfParams, nil
|
||||
}
|
||||
|
||||
// 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 smx509.MarshalPKCS8PrivateKey(priv)
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = DefaultOpts
|
||||
}
|
||||
|
||||
// Convert private key into PKCS8 format
|
||||
pkey, err := smx509.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
|
||||
}
|
||||
|
||||
key, kdfParams, err := opts.KDFOpts.DeriveKey(password, salt, encAlg.KeySize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptionScheme, encryptedKey, err := encAlg.Encrypt(key, 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},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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("pkcs8: 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("pkcs8: key block is not of type ECDSA")
|
||||
}
|
||||
return typedKey, nil
|
||||
}
|
||||
|
||||
// ParsePKCS8PrivateKeySM2 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 ParsePKCS8PrivateKeySM2(der []byte, v ...[]byte) (*sm2.PrivateKey, error) {
|
||||
key, err := ParsePKCS8PrivateKey(der, v...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typedKey, ok := key.(*sm2.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs8: key block is not of type SM2")
|
||||
}
|
||||
return typedKey, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var password []byte
|
||||
if len(v) > 0 {
|
||||
password = v[0]
|
||||
}
|
||||
return MarshalPrivateKey(priv, password, nil)
|
||||
}
|
611
pkcs8/pkcs8_test.go
Normal file
611
pkcs8/pkcs8_test.go
Normal file
@ -0,0 +1,611 @@
|
||||
package pkcs8_test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/pkcs8"
|
||||
)
|
||||
|
||||
const rsa2048 = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBMF0LikTFOU/T
|
||||
8DUDSvORootvhUD67f6AXmEnntfXRvQ3O91+qt40tevS8JtFaq4gKxugRjjZRtni
|
||||
50aUGcEZ4leq3DboBL9XH089IEmxxLbJeJIXxgPeRHrXRINvUSspwRrJkX6fnXyi
|
||||
MdRhqdH2tG1yrXKkt9UvdSHfRYimDcJ+ry2zYlcbz9aoLDO1vEdS/IBu0jXAZ/Z/
|
||||
xaEVfkoWMzZM2SU+lfJeyzobii00VXGuSQKnI8E/e16kDpBXJ6PFSm6EyZmAad6O
|
||||
f+B9d/ZEXGQlbaooG54v5sGj54mg7m/75qMaxL2H8NER31gAeyvoyovfXI0vbswH
|
||||
8AozxGwDAgMBAAECggEAautIY62nt/urKaIExQjDWvO59gOq3fW/5+3UGWh5DqUv
|
||||
Xi5cvND2X/fbR4hwdu++5QDWrlKO/fmPd1wGnMrQK3IwkNiF7s1J1H74jN0EzEUR
|
||||
4NlBCbVGyMnfrqo1j/M9T0OXfr1udgpkQyQO5epl0QM0m8ZQ78bqTvSlxXsnULbQ
|
||||
py0Tx0uCWaP6FzDsZ+t2rj/SVH7hQNf8ITfQJhVol/n5Hza4+NRfp/DPXWZEvPlo
|
||||
GeMs9PDCa16tw8wI9EUnmFaeFlmtJPdTs5rVo9Ya/zmtoxN6AGTCG0IE6YRvh3Qn
|
||||
jttIp2QitOSBKmXpu1ZI6UTtimGgnfiJKK1BGVaMOQKBgQDfF6ZBMY/tLmDg1mgS
|
||||
QQKAOWMB0/3CvzcM96R0VACO2vr1BbePMXQQ/i27rD001Xl2wNTsETRk1Ji6btwQ
|
||||
64m4uxRSZCJmYyBAcJjfBtMWIDiihQTL55NFTd9YIPmqGmbj1ASQgtpQR5Cq/5YR
|
||||
9Vu0kTxMmADoiq1tR2VGZeScnwKBgQDdr4ITDFGSpqWKnyHQaQgTIW4uxQ5pQKIx
|
||||
aKbCNZOtSgJfqUCY+8gJMkFOtQzawrburD4qllFxdqhHLiXSx6/8zSTrsiexml2i
|
||||
7HxUZaSmn5Q4HFNngKKHXd4NGsWp237k8fJ2953KX89yEov8FpIiq6qvZH/LS8DN
|
||||
+GORAPSSHQKBgCHobUuRZefN2cmyrOTBXsjwb/zyJKq593sQFL3dmqwb2nLtaIXq
|
||||
JVMD3x2cQz1JiQmkq3gp8UW2DnSfrvEfa7JZNPCE6bmYLWm9825KkkDVquYAw8be
|
||||
LsMk3+J8OJZDJwpPylXQnbAAAJwM9tlJ6qNaQ8j8fX7avRtT86+sgv/PAoGABjJp
|
||||
yG6HuTm/Vuir4U+OUjqVAemwRXDxF8B9KOCmiCmRd2sbyyr+pIMrIDAfc94Njw5x
|
||||
jm81R56xhYvcss+yM7boWU5ZnbVa+LrznshYme/MDOV9z17hLDeLhYJCFEV2fp/k
|
||||
zz6MwqN7AQ1TrHBVFXMHCnAcwmoTsa5H2j3UmGECgYEAvvJ+o5+FPnBs+VU5FJxF
|
||||
fAGFpF3AwfbSCm2ARZOxMHAkpsz/FBXlo+rVZv6loTKTPQFMxIB15il7ls0CGI9q
|
||||
6UaZ5hkKjEOQUW8UYc8Cv0xpSkcuxcGrWzw4AMdc84XXi6F1+48ab9Gt0pN3tgUG
|
||||
qg+KU+JDsQLHHmykZ92cHPA=
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const encryptedRSA2048aes = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIrrW09+9XumECAggA
|
||||
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAxglavSPtrKNsM9cDXmrS1BIIE
|
||||
0Gy226c9+zxZ8jUsUIbDdsq1mPbqAWs1xImAj4nA7NMv6G/5QH9CrsmB+4r4GIiy
|
||||
CafN1W9YvFg3SISUbe+h156Pt2iKoZlVCbSa4XVo4diwmjloZIHM4Jk0Pu28CbJo
|
||||
QDVwPCuWMKppkfwr63RT+FBSfBEBaRCi4eXz6tOcMduBOlaiQvSREvDCCOeY9gja
|
||||
RgvyUa2Hf8oHNkSG9yXoMrvz0FayMWK/i7LU+2NqiPZVTvfGkqNkJJF/M7INKgLs
|
||||
d6A1hgyA7HVv4czQOPQJCArXeCycI1EJ4uSthJxqd/iYX0z52Tfa7q/0oAZ4HZt+
|
||||
wmcov8GwqfAg7Cu9soifYwfMYTghXOX2UKmQa/0UNK5ibj5cC9+oA09Ucx5twKDs
|
||||
nwSGEIb+7qNhZSRtEXtOL7bxQL8PUvAXWrTXluvZ+bv/9S53XYPL4E95rrLnTF/L
|
||||
csEYleNIpY/6HkPFtqPZiWCsVUZep9uPjZo29kh/246yKBFjsw5mXtm1S6ha4Xb9
|
||||
gUxqKQiWe9+tCkPHRVo2KJX1H4Al7UB9GqDR5oUhIayp6nYCeI/dLwPpikq1F8HO
|
||||
iJva/qV2iltxwyQHhEenyM9TPkPawqOOUKvDd1hZR0wzABcC3koLtwwKyEGzQPPW
|
||||
bxp5GBim9Pu/EGWY1d1H38eVu44jRP/3ONk8wvZcsIbn6U8bOeToUFmcjuuQ3cxf
|
||||
pDUruIA9PjWL9Se6TI3CytTUCbCb4bKRP+eE0B2LPwq6+dyvcY2yidYj9C2D25tb
|
||||
F+E1Wr7ro97OXQ8grMWwrTpZ9rUzmz5wzYWmOFaKJRiepkuUpx4HWl+fKn5r5LyV
|
||||
+cyYoSjApNgHe/9Pz7mNXNdeSmWcn4BVs1XgKi1MiJNWn5tNlKB3kz1kgraKOWbs
|
||||
9/dspegd5fQ6Lzvlt7CsJh/I76rE+90LAbXWVlQ/jm/4jrWownjW1oVIj0Xfxx7i
|
||||
UlmtTFoCIvNWRyoyK5pfL0JvxOtd5leHZniJoww0CPKYS0mibxYLc883Q5Hq5ZH/
|
||||
C7iBJN0aDJfVfkl0o4EQWaWQS0rAInhe7xTHmFFe5NP9lVTEwQt+C/fz7qalHe9P
|
||||
ulV8MsT/vg2/9twvxKbVCSzaDyta/TyhX76LTULprPr6ahDhP9rybmmK548m86kZ
|
||||
IxWdmed7Pt3YPeEImoLBoXh8eaWpYDlX2Be5/eqjw2wbg6srBKoA7swSkMsFXm5q
|
||||
+HgF4X6R9lfmLjs/UMOi9SM6ODh4xgq1DxX+bJZLfJwXj90i56Ij8OhjcBJ+DwUi
|
||||
ntosYkXp6lMZIyfI3jWG4IZwE9nt8oXJZfUtIU5mYF9DAV92fRwm1mCLMx0iznv1
|
||||
bvCu7yJ51nWB3xkIOqCYbzbREWmL+6/akGOqu1KDrFKBu0IyAqUWt0XrY3b4V5jm
|
||||
WjTXywDkCcGC6W0t4yhu1Yz8QhE5Giw2PHwwZ3940QZQsFcBM6RJOcnkbYTu8TFm
|
||||
7s9ZItSShwAN/i1nN1daF9lgdm4WKHWd/jqHIgl2NijiDgb5F5YaWgurKg9tOrEK
|
||||
oGJlPmBUiNynhqcz69ljjW6q4U2cfF4g6Onl2sucLdsFXejgVdsKBVXw+gjGr2TS
|
||||
lgmeHTcvZmTShvbN/TrHETjO7jEB4V2I4a4L7uybuWF/
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const encryptedRSA2048des3 = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIKXB+OWAc6pwCAggA
|
||||
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECCeQ2z+ohlaTBIIEyAbgxv69udAD
|
||||
N0JzM0+E/dYKTRxEVED1cyY+fcROfxkJvT3FOOWS65vaPo5i8n0e7pFHvm/ezkoL
|
||||
mNRYhoyd45pog4ALJ6O03bUBTLJPiowz90uHC7GWQelMl7LeNyX/7/5s2jOpgW82
|
||||
oB6JizF9SjZzCTzKTmZLOAz3GjIERWHmoIczy40nxP7zmHzVrxTp1V6gnzxgUIuA
|
||||
X/7FTMRlWvEbX9gzODx7stI/5/bLla1Y7PDWEs2aJCnsN7pXJSd0Ry2/iBnQKe6n
|
||||
p4RW7jRAiFTGXbR1E5ZoFsSUs0K9JLEJA+kq6x+smRGxioV3I/r6MLaeumNZ37Bx
|
||||
9OfiJAWk0Ei9EUjM4ZLWjnhgRyI2mThEXTbCevv2GonwG9G968QEMjfbXcLA6Opt
|
||||
0mmRutT6IgvflEZRi9BlmCGOecNHl+cojVCwmAPZKkk2e9lZe+x9+TXW66GJVFiK
|
||||
6BlgRwTcNPKePCYWPjsV5wUZACq0Y61nksBViyRUFsEkEEYMXIbh6bbUTTlJg/tk
|
||||
tCp/LF9oTf1XacJ8a/s6oLuz95R07u9E/liibzVavK0nVNSR5Xdo7QDivWxnaSLd
|
||||
wt8qUOnVbW0eSyq2BAKK7yvZfhz44D9WS8M8jp8gwj7Eti81LGqeh5IvqekDYmoz
|
||||
BFiY24PnRcZnpETA/e6v5dNrpE/OLHmdY1ag6aifIJCc1UG84Oi/nPBTZ7eHLGCd
|
||||
Kn4/9xdCVHd4077Qx9JLW9LutZXkqYaBckOEHtvaMfyWUaXiNty/N5RECGvn5wmM
|
||||
dwC6td6CqtojiHOB7GAUiwjHgbQLpNoIz1BiVTIo1eoD32+4RHYUxNmhsk0r22Zf
|
||||
ZnfnKBGgV7KKNKP3eFQnzSeNE0qFd5AtSpeJX0G0IsbuvXOE/7P0pj7DhD4HoYS7
|
||||
Mf2za6Wm/CVWNM4ekc3MsKb9D+ogzdQ4VYI2mzBdLulrYvfPCE6SHZlZ+ePE4LSr
|
||||
jexB6LYLZJU7Bxnslt9E/mjSzWHctF9LhHf7sl7NUhCHdDvij6Hd0l4+noQlDTtd
|
||||
rnXgL9fTjSfaVDv3Rt+AmNN9Cu9Y1FSBLYMe8LfGXXUPg86bTbGk3uFjkyIY3hE2
|
||||
/Kz1re4KXdDdjYe4ja5qZK8fWx0704NkzH1UO/HMd4Cnx3agyVeyJVG3iRSIesRG
|
||||
vI1ESJMMv1+MMGiWwRzHYvv7yrqp2steAVwjGu26/s1vwkZrvy8SjzYAXo1RLT9o
|
||||
cNnlPra6N7xReSohOibAp0kx1d81JqvEOvJIhR7KDXSRutgIPlqQgHXtmDW/VlCb
|
||||
w05Ptg3SXaCE0+pY0T+FYHusA3JEmyU0e629Ed/dl/j7Xpynl1V6/ndf3gdRGX0l
|
||||
d2IGneJsnj8yvP0dUsB2l71W/ZIM3HERDLxP9JByyINCBQ1BFsN81qUXpj6vGYjb
|
||||
hPyUmmsAGibXJOiGzmaP3nGgF9qbe1XiTRdbm2AZ3pEaJxkkFWsT+Yivz9yzZE0P
|
||||
3/w14HvS94X/Z2+yDLtQQgsLNkfw/Gpc4O0GMnLuOl4KSaTA37IdJR2jOFP7LtHR
|
||||
9Egbm93atZWSAyTO7OtZGmna6k6eGUsk8Dxp7cWOUkLf7C5sL6l3bBH7omlQHx9P
|
||||
RIiDkxAd7hbpm4/C/DoUZQ==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const encryptedRSA2048scrypt = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFJTBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIY6+u2Dcq3hwCAkAA
|
||||
AgEIAgEBMB0GCWCGSAFlAwQBKgQQ6Kut7Q560w1e+fqSiF6uUgSCBNBWRJP19DiT
|
||||
m/ZWEh4ukxTnrBpx59ATbuiBZjjty9vw/dkusUivNLsIoJDezuv4YxjxNx4zZsv+
|
||||
vI5/gWT78XdF0XHgrRKjB0AvQ4rdVSUhV2/sxMa8P5bwE7NikovkzP1rM0cPCLRE
|
||||
K5J81+pEOVKumJJg3jNtK18HtCiH0D4n276xK6fJ2BptA1BMNhlDkoz99kmwPfhg
|
||||
gMiJxbcGrYHMvCZAL8towTRomI82fjRwpEtT8eZ7aLUALDM53JXzhiz/bO2cKCRx
|
||||
4oLx6rChrqCTS4bZ++PPBS9klwW1kx5eMTGdv3IS+/Y7wvPtZ9jbwcjkSKpOsALv
|
||||
h/6CzUuTo5dIPDaOidLLHS4bfgKCC/da/uuow/ET7K6KBOZ6kCnXi300D+hZE8cJ
|
||||
GYjIQGVY3FtrtZx55hjeqyRsVrdKP0e83wNEnGgofsgeJ+H88zUxaMqIiz3e773M
|
||||
zshNXcCko9jAgr8PwRg7ARPql+TcS3fJ+HPBA1mDlT4xMXyFOgckMkz8xR08EA0M
|
||||
UcvtGAxLJYtsiMJigdrCI7lGmWZbj8tB2sS0JD95QsbR5CcsqzaELzoKMdOpG5MP
|
||||
4ZdgHpeGNtw15aAIdxfoFGGcNgLiZ+y7BC0fM9xYAPARrb64A3e3gmsJ3ZKEkZzR
|
||||
MbK9a08S+6VI68T9M0f3i53p/e09CYZ0TN3yMN/g/usxERzpji7zCjEYf6yuUeRF
|
||||
c3ceVVaxldexAOV0dEIUq8xehUhvhV129/hUHUyqsx1XiURWSx2TRSjuZ3SE63Sc
|
||||
LO81rijz4rFa69JXPGWNrzR0IS0CY8aMF79fwqpcLaRHIpfQLiIQ19qDHiipXCs/
|
||||
ZLli5MZQZ7AHoXqbHBQOqhiT2LLEgeVF4uEi0qM1ULfmmZMoJQg+ugRXPEJR0fa0
|
||||
ji6Hb/ZTDGwsdrNZGfTD4lJeiel3IPVcOzfeZqb6OsdkUSQzZZSvAET95qkKn/CN
|
||||
diPkX96iYuhjcace/f8xLnVY3TJ6WRpDW9oBzVFEm5jXtlHhVltau2Qmoi28pthE
|
||||
25QrNfoOs4qr2gaGA37VXSEW4yLU3jyqlP1esXxyEiqg9CKPnk/K/XxREjGJXElr
|
||||
FQtRif9b4QDBrZc38Y5ct7x+Ce7llJ3kKslVdF2rbVEn4nPIHIqw8oKDv/6+CNwo
|
||||
8O1B4u16WUqj2Th6hOQcmWb9Nb6Js5TSRtIJxrif6PTfTczSB9bZgU1fTxgr0tTI
|
||||
AERJLqFA9dvCxAehWrlegsSOwvJ/E8FwbGJLhiJs6aFk1fJ6NPkp62UkmvBMDq1w
|
||||
qYuwLSr920KrPsCYBa09Ldm9e88+nCQz5QWcJt2vvdIz0UQUqtsjUo8DWL0qNXgU
|
||||
JVSRrfE+64II2sxt3/9oywLCk9DG+dcWZH6SRjSt7y9KhWfhdGq1S6Og1Mjc+U4A
|
||||
L/TgycaVTBodGTmw5YlsbSBYzAwSBCaR7GLThhZqIlPrk6P3w8VZJ1B14nEcTP9Y
|
||||
GVdcEOqE0mwNtWZYcuy1cqPj6g/p9NOmLOnT8HGbjw9qtdl+iEGN/ZDWfu+En7ES
|
||||
Dv4v0MiWAMArKY8rAMWa9/phbWXVEtNz6RnJ460qxIax5GR0QPce3+lrswhmXSm4
|
||||
RNXdI4NIGtOdg8zwuKI5AefoLlWjt56Pzg==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const ec256 = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjLFzKb/8hsdSmPft
|
||||
s98RZ7AWzQnLDsMhy6v+/3BZlZ6hRANCAASKkodoH+hHmBfwoFfrvv1E+iMLt3g1
|
||||
s6hxOUMbkv6ZTVFXND/3z9zlJli6/YGrlSnsHOJc0GbwSYD1AMwZyr0T
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const encryptedEC256aes = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjVvKZtHlmIbAICCAAw
|
||||
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEL3jdkBvObn+QELgKVE2cnMEgZAl
|
||||
wgo3AjtXevJaGgep5GsW2krw9S7dC7xG9dR33Z/a9nBnO1rKm7Htf0+986w/1vmj
|
||||
4k3M2QiI/VY+tnDFE+46DLLKYtJGRT1aoAH+mwhzaQGwzJnKhbeA23aE0f7KWCAK
|
||||
+f999+SeHWro7FiRZjHEYVVLGQr/I7K5Wyh24YjN2nR4CU4X+GQU25My/pgSRog=
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const ec128 = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjLFzKb/8hsdSmPft
|
||||
s98RZ7AWzQnLDsMhy6v+/3BZlZ6hRANCAASKkodoH+hHmBfwoFfrvv1E+iMLt3g1
|
||||
s6hxOUMbkv6ZTVFXND/3z9zlJli6/YGrlSnsHOJc0GbwSYD1AMwZyr0T
|
||||
-----END PRIVATE KEY-----`
|
||||
|
||||
const encryptedEC128aes = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAg7qE4RYQEEugICCAAw
|
||||
HQYJYIZIAWUDBAECBBBa+6eKv6il/iEjOw8/AmEHBIGQ24YmBiMfzjJjFU+PAwXr
|
||||
zCfR3NPOHBwn3+BkpyivaezSrFWIF919cnDyI15Omd+Iz2oljrT/R4IDC9NOmoAy
|
||||
5uKixYGAOi74Qr9kdgrT2Bfvu9wq+dYqPwLjR4WFHl2ofrLn7RCaOa8mOh3bgfHP
|
||||
SnXPiACchx53PDh6bZTIZ0V9v0ymcMuXf758OXbUmSGN
|
||||
-----END ENCRYPTED PRIVATE KEY-----`
|
||||
|
||||
const encryptedEC256aes128sha1 = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAgEoFG3x07DbQICCAAw
|
||||
HQYJYIZIAWUDBAECBBCRN9PNX9rBqXhaHLUOsv7YBIGQFfXAPPV+COWABJdSarog
|
||||
eUHFNaQ+R6x55Tz/mquNIwiOrP9DNoEd1PGtKaHaO+ACSEQwMfrGeh8BuNV69EwP
|
||||
bhsob/MZeexRbrLe2YN7Y7/Y0wpujalGlliMvs35f1fpq/9RfVU+qRpFED2lT4dm
|
||||
zOuhMC9Oo3oMYlbEXAT9mq33MkGKMUth2ek/bQIvnCHG
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
// From https://tools.ietf.org/html/rfc7914
|
||||
const encryptedRFCscrypt = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIHiME0GCSqGSIb3DQEFDTBAMB8GCSsGAQQB2kcECzASBAVNb3VzZQIDEAAAAgEI
|
||||
AgEBMB0GCWCGSAFlAwQBKgQQyYmguHMsOwzGMPoyObk/JgSBkJb47EWd5iAqJlyy
|
||||
+ni5ftd6gZgOPaLQClL7mEZc2KQay0VhjZm/7MbBUNbqOAXNM6OGebXxVp6sHUAL
|
||||
iBGY/Dls7B1TsWeGObE0sS1MXEpuREuloZjcsNVcNXWPlLdZtkSH6uwWzR0PyG/Z
|
||||
+ZXfNodZtd/voKlvLOw5B3opGIFaLkbtLZQwMiGtl42AS89lZg==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
|
||||
func TestParsePKCS8PrivateKeyRSA(t *testing.T) {
|
||||
keyList := []struct {
|
||||
name string
|
||||
clear string
|
||||
encrypted string
|
||||
}{
|
||||
{
|
||||
name: "encryptedRSA2048aes",
|
||||
clear: rsa2048,
|
||||
encrypted: encryptedRSA2048aes,
|
||||
},
|
||||
{
|
||||
name: "encryptedRSA2048des3",
|
||||
clear: rsa2048,
|
||||
encrypted: encryptedRSA2048des3,
|
||||
},
|
||||
}
|
||||
for i, key := range keyList {
|
||||
t.Run(key.name, func(t *testing.T) {
|
||||
block, _ := pem.Decode([]byte(key.encrypted))
|
||||
_, err := pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes, []byte("password"))
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKeyRSA returned: %s", i, err)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes, []byte("wrong password"))
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes)
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
|
||||
block, _ = pem.Decode([]byte(key.clear))
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKeyRSA returned: %s", i, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePKCS8PrivateKeyECDSA(t *testing.T) {
|
||||
keyList := []struct {
|
||||
name string
|
||||
clear string
|
||||
encrypted string
|
||||
}{
|
||||
{
|
||||
name: "encryptedEC256aes",
|
||||
clear: ec256,
|
||||
encrypted: encryptedEC256aes,
|
||||
},
|
||||
}
|
||||
for i, key := range keyList {
|
||||
t.Run(key.name, func(t *testing.T) {
|
||||
block, _ := pem.Decode([]byte(key.encrypted))
|
||||
_, err := pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes, []byte("password"))
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKeyECDSA returned: %s", i, err)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes, []byte("wrong password"))
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes)
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
|
||||
block, _ = pem.Decode([]byte(key.clear))
|
||||
_, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKeyECDSA returned: %s", i, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePKCS8PrivateKey(t *testing.T) {
|
||||
keyList := []struct {
|
||||
name string
|
||||
clear string
|
||||
encrypted string
|
||||
password string
|
||||
}{
|
||||
{
|
||||
name: "encryptedRSA2048aes",
|
||||
clear: rsa2048,
|
||||
encrypted: encryptedRSA2048aes,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "encryptedRSA2048des3",
|
||||
clear: rsa2048,
|
||||
encrypted: encryptedRSA2048des3,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "encryptedRSA2048scrypt",
|
||||
clear: rsa2048,
|
||||
encrypted: encryptedRSA2048scrypt,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "encryptedEC256aes",
|
||||
clear: ec256,
|
||||
encrypted: encryptedEC256aes,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "encryptedEC256aes128sha1",
|
||||
clear: ec256,
|
||||
encrypted: encryptedEC256aes128sha1,
|
||||
password: "password",
|
||||
},
|
||||
{
|
||||
name: "encryptedRFCscrypt",
|
||||
clear: "",
|
||||
encrypted: encryptedRFCscrypt,
|
||||
password: "Rabbit",
|
||||
},
|
||||
{
|
||||
name: "encryptedEC128aes",
|
||||
clear: ec128,
|
||||
encrypted: encryptedEC128aes,
|
||||
password: "password",
|
||||
},
|
||||
}
|
||||
for i, key := range keyList {
|
||||
t.Run(key.name, func(t *testing.T) {
|
||||
block, _ := pem.Decode([]byte(key.encrypted))
|
||||
_, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte(key.password))
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKey returned: %s", i, err)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("wrong password"))
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err == nil {
|
||||
t.Errorf("%d: should have failed", i)
|
||||
}
|
||||
|
||||
if key.clear != "" {
|
||||
block, _ = pem.Decode([]byte(key.clear))
|
||||
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("%d: ParsePKCS8PrivateKey returned: %s", i, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertPrivateKeyToPKCS8(t *testing.T) {
|
||||
for i, password := range [][]byte{nil, []byte("password")} {
|
||||
var args [][]byte
|
||||
if password != nil {
|
||||
args = append(args, password)
|
||||
}
|
||||
rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: GenerateKey returned: %s", i, err)
|
||||
}
|
||||
der, err := pkcs8.ConvertPrivateKeyToPKCS8(rsaPrivateKey, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: ConvertPrivateKeyToPKCS8 returned: %s", i, err)
|
||||
}
|
||||
decodedRSAPrivateKey, err := pkcs8.ParsePKCS8PrivateKey(der, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: ParsePKCS8PrivateKey returned: %s", i, err)
|
||||
}
|
||||
if rsaPrivateKey.D.Cmp(decodedRSAPrivateKey.(*rsa.PrivateKey).D) != 0 {
|
||||
t.Fatalf("%d: Decoded key does not match original key", i)
|
||||
}
|
||||
|
||||
for _, curve := range []elliptic.Curve{
|
||||
elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(),
|
||||
} {
|
||||
ecPrivateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: GenerateKey returned: %s", i, curve, err)
|
||||
}
|
||||
der, err = pkcs8.ConvertPrivateKeyToPKCS8(ecPrivateKey, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: ConvertPrivateKeyToPKCS8 returned: %s", i, curve, err)
|
||||
}
|
||||
decodedECPrivateKey, err := pkcs8.ParsePKCS8PrivateKey(der, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: ParsePKCS8PrivateKey returned: %s", i, curve, err)
|
||||
}
|
||||
if ecPrivateKey.D.Cmp(decodedECPrivateKey.(*ecdsa.PrivateKey).D) != 0 {
|
||||
t.Fatalf("%d, %s: Decoded key does not match original key", i, curve)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalPrivateKey(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
password []byte
|
||||
opts *pkcs8.Opts
|
||||
}{
|
||||
{
|
||||
password: nil,
|
||||
opts: nil,
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.SM4CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SM3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.SM4GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SM3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA224,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA384,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512_224,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512_256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES192CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 1000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 2000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES192GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 10000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.DESCBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.TripleDESCBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256CBC,
|
||||
KDFOpts: pkcs8.ScryptOpts{
|
||||
CostParameter: 1 << 2,
|
||||
BlockSize: 8,
|
||||
ParallelizationParameter: 1,
|
||||
SaltSize: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: GenerateKey returned: %s", i, err)
|
||||
}
|
||||
der, err := pkcs8.MarshalPrivateKey(rsaPrivateKey, tt.password, tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err)
|
||||
}
|
||||
decodedRSAPrivateKey, _, err := pkcs8.ParsePrivateKey(der, tt.password)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: ParsePKCS8PrivateKey returned: %s", i, err)
|
||||
}
|
||||
if rsaPrivateKey.D.Cmp(decodedRSAPrivateKey.(*rsa.PrivateKey).D) != 0 {
|
||||
t.Fatalf("%d: Decoded key does not match original key", i)
|
||||
}
|
||||
|
||||
sm2PrivateKey, err := sm2.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: GenerateKey returned: %s", i, err)
|
||||
}
|
||||
|
||||
der, err = pkcs8.MarshalPrivateKey(sm2PrivateKey, tt.password, tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: MarshalPrivateKey returned: %s", i, err)
|
||||
}
|
||||
|
||||
decodedSM2PrivateKey, _, err := pkcs8.ParsePrivateKey(der, tt.password)
|
||||
if err != nil {
|
||||
t.Fatalf("%d: ParsePKCS8PrivateKey returned: %s", i, err)
|
||||
}
|
||||
if !sm2PrivateKey.Equal(decodedSM2PrivateKey) {
|
||||
t.Fatalf("%d: Decoded key does not match original key", i)
|
||||
}
|
||||
|
||||
for _, curve := range []elliptic.Curve{
|
||||
elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(),
|
||||
} {
|
||||
ecPrivateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: ConvertPrivateKeyToPKCS8 returned: %s", i, curve, err)
|
||||
}
|
||||
der, err = pkcs8.MarshalPrivateKey(ecPrivateKey, tt.password, tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: ConvertPrivateKeyToPKCS8 returned: %s", i, curve, err)
|
||||
}
|
||||
decodedECPrivateKey, _, err := pkcs8.ParsePrivateKey(der, tt.password)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, %s: ParsePKCS8PrivateKey returned: %s", i, curve, err)
|
||||
}
|
||||
if ecPrivateKey.D.Cmp(decodedECPrivateKey.(*ecdsa.PrivateKey).D) != 0 {
|
||||
t.Fatalf("%d, %s: Decoded key does not match original key", i, curve)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unknown int
|
||||
|
||||
func TestUnknownTypeFailure(t *testing.T) {
|
||||
badInput := unknown(0)
|
||||
_, err := pkcs8.ConvertPrivateKeyToPKCS8(badInput, []byte("password"))
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user