github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/pkcs8/pkcs8.go (about)

     1  // Package pkcs8 implements functions to encrypt, decrypt, parse and to convert
     2  // EC private keys to PKCS#8 format. However this package is hard forked from
     3  // https://github.com/youmark/pkcs8 and modified function signatures to match
     4  // signatures of crypto/x509 and cloudflare/cfssl/helpers to simplify package
     5  // swapping. License for original package is as follow:
     6  //
     7  // The MIT License (MIT)
     8  //
     9  // Copyright (c) 2014 youmark
    10  //
    11  // Permission is hereby granted, free of charge, to any person obtaining a copy
    12  // of this software and associated documentation files (the "Software"), to deal
    13  // in the Software without restriction, including without limitation the rights
    14  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    15  // copies of the Software, and to permit persons to whom the Software is
    16  // furnished to do so, subject to the following conditions:
    17  //
    18  // The above copyright notice and this permission notice shall be included in all
    19  // copies or substantial portions of the Software.
    20  //
    21  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    22  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    23  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    24  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    25  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    26  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    27  // SOFTWARE.
    28  package pkcs8
    29  
    30  import (
    31  	"bytes"
    32  	"crypto"
    33  	"crypto/aes"
    34  	"crypto/cipher"
    35  	"crypto/rand"
    36  	"crypto/sha1"
    37  	"encoding/asn1"
    38  	"encoding/pem"
    39  	"errors"
    40  
    41  	"github.com/cloudflare/cfssl/helpers/derhelpers"
    42  	"golang.org/x/crypto/pbkdf2"
    43  )
    44  
    45  // Copy from crypto/x509
    46  var (
    47  	oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
    48  )
    49  
    50  // Unencrypted PKCS#8
    51  var (
    52  	oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
    53  	oidPBES2       = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
    54  	oidAES256CBC   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
    55  )
    56  
    57  type ecPrivateKey struct {
    58  	Version       int
    59  	PrivateKey    []byte
    60  	NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
    61  	PublicKey     asn1.BitString        `asn1:"optional,explicit,tag:1"`
    62  }
    63  
    64  type privateKeyInfo struct {
    65  	Version             int
    66  	PrivateKeyAlgorithm []asn1.ObjectIdentifier
    67  	PrivateKey          []byte
    68  }
    69  
    70  // Encrypted PKCS8
    71  type pbkdf2Params struct {
    72  	Salt           []byte
    73  	IterationCount int
    74  }
    75  
    76  type pbkdf2Algorithms struct {
    77  	IDPBKDF2     asn1.ObjectIdentifier
    78  	PBKDF2Params pbkdf2Params
    79  }
    80  
    81  type pbkdf2Encs struct {
    82  	EncryAlgo asn1.ObjectIdentifier
    83  	IV        []byte
    84  }
    85  
    86  type pbes2Params struct {
    87  	KeyDerivationFunc pbkdf2Algorithms
    88  	EncryptionScheme  pbkdf2Encs
    89  }
    90  
    91  type pbes2Algorithms struct {
    92  	IDPBES2     asn1.ObjectIdentifier
    93  	PBES2Params pbes2Params
    94  }
    95  
    96  type encryptedPrivateKeyInfo struct {
    97  	EncryptionAlgorithm pbes2Algorithms
    98  	EncryptedData       []byte
    99  }
   100  
   101  // ParsePrivateKeyPEMWithPassword parses an encrypted or a decrypted PKCS#8 PEM to crypto.signer
   102  func ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) {
   103  	block, _ := pem.Decode(pemBytes)
   104  	if block == nil {
   105  		return nil, errors.New("invalid pem file")
   106  	}
   107  
   108  	var (
   109  		der []byte
   110  		err error
   111  	)
   112  	der = block.Bytes
   113  
   114  	if ok := IsEncryptedPEMBlock(block); ok {
   115  		der, err = DecryptPEMBlock(block, password)
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  	}
   120  
   121  	return derhelpers.ParsePrivateKeyDER(der)
   122  }
   123  
   124  // IsEncryptedPEMBlock checks if a PKCS#8 PEM-block is encrypted or not
   125  func IsEncryptedPEMBlock(block *pem.Block) bool {
   126  	der := block.Bytes
   127  
   128  	var privKey encryptedPrivateKeyInfo
   129  	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
   130  		return false
   131  	}
   132  
   133  	return true
   134  }
   135  
   136  // DecryptPEMBlock requires PKCS#8 PEM Block and password to decrypt and return unencrypted der []byte
   137  func DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) {
   138  	der := block.Bytes
   139  
   140  	var privKey encryptedPrivateKeyInfo
   141  	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
   142  		return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
   143  	}
   144  
   145  	if !privKey.EncryptionAlgorithm.IDPBES2.Equal(oidPBES2) {
   146  		return nil, errors.New("pkcs8: only PBES2 supported")
   147  	}
   148  
   149  	if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IDPBKDF2.Equal(oidPKCS5PBKDF2) {
   150  		return nil, errors.New("pkcs8: only PBKDF2 supported")
   151  	}
   152  
   153  	encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme
   154  	kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params
   155  
   156  	switch {
   157  	case encParam.EncryAlgo.Equal(oidAES256CBC):
   158  		iv := encParam.IV
   159  		salt := kdfParam.Salt
   160  		iter := kdfParam.IterationCount
   161  
   162  		encryptedKey := privKey.EncryptedData
   163  		symkey := pbkdf2.Key(password, salt, iter, 32, sha1.New)
   164  		block, err := aes.NewCipher(symkey)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  		mode := cipher.NewCBCDecrypter(block, iv)
   169  		mode.CryptBlocks(encryptedKey, encryptedKey)
   170  
   171  		if _, err := derhelpers.ParsePrivateKeyDER(encryptedKey); err != nil {
   172  			return nil, errors.New("pkcs8: incorrect password")
   173  		}
   174  
   175  		// Remove padding from key as it might be used to encode to memory as pem
   176  		keyLen := len(encryptedKey)
   177  		padLen := int(encryptedKey[keyLen-1])
   178  		if padLen > keyLen || padLen > aes.BlockSize {
   179  			return nil, errors.New("pkcs8: invalid padding size")
   180  		}
   181  		encryptedKey = encryptedKey[:keyLen-padLen]
   182  
   183  		return encryptedKey, nil
   184  	default:
   185  		return nil, errors.New("pkcs8: only AES-256-CBC supported")
   186  	}
   187  }
   188  
   189  func encryptPrivateKey(pkey, password []byte) ([]byte, error) {
   190  	// Calculate key from password based on PKCS5 algorithm
   191  	// Use 8 byte salt, 16 byte IV, and 2048 iteration
   192  	iter := 2048
   193  	salt := make([]byte, 8)
   194  	iv := make([]byte, 16)
   195  
   196  	if _, err := rand.Reader.Read(salt); err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	if _, err := rand.Reader.Read(iv); err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	key := pbkdf2.Key(password, salt, iter, 32, sha1.New)
   205  
   206  	// Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme
   207  	n := len(pkey)
   208  	padLen := aes.BlockSize - n%aes.BlockSize
   209  	if padLen > 0 {
   210  		padValue := []byte{byte(padLen)}
   211  		padding := bytes.Repeat(padValue, padLen)
   212  		pkey = append(pkey, padding...)
   213  	}
   214  
   215  	encryptedKey := make([]byte, len(pkey))
   216  	block, err := aes.NewCipher(key)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	mode := cipher.NewCBCEncrypter(block, iv)
   221  	mode.CryptBlocks(encryptedKey, pkey)
   222  
   223  	pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter}}
   224  	pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv}
   225  	pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}}
   226  
   227  	encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey}
   228  	return asn1.Marshal(encryptedPkey)
   229  }
   230  
   231  // EncryptPEMBlock takes DER-format bytes and password to return an encrypted PKCS#8 PEM-block
   232  func EncryptPEMBlock(data, password []byte) (*pem.Block, error) {
   233  	encryptedBytes, err := encryptPrivateKey(data, password)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	return &pem.Block{
   239  		Type:    "ENCRYPTED PRIVATE KEY",
   240  		Headers: map[string]string{},
   241  		Bytes:   encryptedBytes,
   242  	}, nil
   243  }
   244  
   245  // ConvertECPrivateKeyPEM takes an EC Private Key as input and returns PKCS#8 version of it
   246  func ConvertECPrivateKeyPEM(inPEM []byte) ([]byte, error) {
   247  	block, _ := pem.Decode(inPEM)
   248  	if block == nil {
   249  		return nil, errors.New("invalid pem bytes")
   250  	}
   251  
   252  	var ecPrivKey ecPrivateKey
   253  	if _, err := asn1.Unmarshal(block.Bytes, &ecPrivKey); err != nil {
   254  		return nil, errors.New("invalid ec private key")
   255  	}
   256  
   257  	var pkey privateKeyInfo
   258  	pkey.Version = 0
   259  	pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2)
   260  	pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA
   261  	pkey.PrivateKeyAlgorithm[1] = ecPrivKey.NamedCurveOID
   262  
   263  	// remove curve oid from private bytes as it is already mentioned in algorithm
   264  	ecPrivKey.NamedCurveOID = nil
   265  
   266  	privatekey, err := asn1.Marshal(ecPrivKey)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	pkey.PrivateKey = privatekey
   271  
   272  	der, err := asn1.Marshal(pkey)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  
   277  	return pem.EncodeToMemory(&pem.Block{
   278  		Type:  "PRIVATE KEY",
   279  		Bytes: der,
   280  	}), nil
   281  }
   282  
   283  // ConvertToECPrivateKeyPEM takes an unencrypted PKCS#8 PEM and converts it to
   284  // EC Private Key
   285  func ConvertToECPrivateKeyPEM(inPEM []byte) ([]byte, error) {
   286  	block, _ := pem.Decode(inPEM)
   287  	if block == nil {
   288  		return nil, errors.New("invalid pem bytes")
   289  	}
   290  
   291  	var pkey privateKeyInfo
   292  	if _, err := asn1.Unmarshal(block.Bytes, &pkey); err != nil {
   293  		return nil, errors.New("invalid pkcs8 key")
   294  	}
   295  
   296  	var ecPrivKey ecPrivateKey
   297  	if _, err := asn1.Unmarshal(pkey.PrivateKey, &ecPrivKey); err != nil {
   298  		return nil, errors.New("invalid private key")
   299  	}
   300  
   301  	ecPrivKey.NamedCurveOID = pkey.PrivateKeyAlgorithm[1]
   302  	key, err := asn1.Marshal(ecPrivKey)
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	return pem.EncodeToMemory(&pem.Block{
   308  		Type:  "EC PRIVATE KEY",
   309  		Bytes: key,
   310  	}), nil
   311  }