github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/src/crypto/x509/pem_decrypt.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x509
     6  
     7  // RFC 1423 describes the encryption of PEM blocks. The algorithm used to
     8  // generate a key from the password was derived by looking at the OpenSSL
     9  // implementation.
    10  
    11  import (
    12  	"crypto/aes"
    13  	"crypto/cipher"
    14  	"crypto/des"
    15  	"crypto/md5"
    16  	"encoding/hex"
    17  	"encoding/pem"
    18  	"errors"
    19  	"io"
    20  	"strings"
    21  )
    22  
    23  type PEMCipher int
    24  
    25  // Possible values for the EncryptPEMBlock encryption algorithm.
    26  const (
    27  	_ PEMCipher = iota
    28  	PEMCipherDES
    29  	PEMCipher3DES
    30  	PEMCipherAES128
    31  	PEMCipherAES192
    32  	PEMCipherAES256
    33  )
    34  
    35  // rfc1423Algo holds a method for enciphering a PEM block.
    36  type rfc1423Algo struct {
    37  	cipher     PEMCipher
    38  	name       string
    39  	cipherFunc func(key []byte) (cipher.Block, error)
    40  	keySize    int
    41  	blockSize  int
    42  }
    43  
    44  // rfc1423Algos holds a slice of the possible ways to encrypt a PEM
    45  // block.  The ivSize numbers were taken from the OpenSSL source.
    46  var rfc1423Algos = []rfc1423Algo{{
    47  	cipher:     PEMCipherDES,
    48  	name:       "DES-CBC",
    49  	cipherFunc: des.NewCipher,
    50  	keySize:    8,
    51  	blockSize:  des.BlockSize,
    52  }, {
    53  	cipher:     PEMCipher3DES,
    54  	name:       "DES-EDE3-CBC",
    55  	cipherFunc: des.NewTripleDESCipher,
    56  	keySize:    24,
    57  	blockSize:  des.BlockSize,
    58  }, {
    59  	cipher:     PEMCipherAES128,
    60  	name:       "AES-128-CBC",
    61  	cipherFunc: aes.NewCipher,
    62  	keySize:    16,
    63  	blockSize:  aes.BlockSize,
    64  }, {
    65  	cipher:     PEMCipherAES192,
    66  	name:       "AES-192-CBC",
    67  	cipherFunc: aes.NewCipher,
    68  	keySize:    24,
    69  	blockSize:  aes.BlockSize,
    70  }, {
    71  	cipher:     PEMCipherAES256,
    72  	name:       "AES-256-CBC",
    73  	cipherFunc: aes.NewCipher,
    74  	keySize:    32,
    75  	blockSize:  aes.BlockSize,
    76  },
    77  }
    78  
    79  // deriveKey uses a key derivation function to stretch the password into a key
    80  // with the number of bits our cipher requires. This algorithm was derived from
    81  // the OpenSSL source.
    82  func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
    83  	hash := md5.New()
    84  	out := make([]byte, c.keySize)
    85  	var digest []byte
    86  
    87  	for i := 0; i < len(out); i += len(digest) {
    88  		hash.Reset()
    89  		hash.Write(digest)
    90  		hash.Write(password)
    91  		hash.Write(salt)
    92  		digest = hash.Sum(digest[:0])
    93  		copy(out[i:], digest)
    94  	}
    95  	return out
    96  }
    97  
    98  // IsEncryptedPEMBlock returns if the PEM block is password encrypted.
    99  func IsEncryptedPEMBlock(b *pem.Block) bool {
   100  	_, ok := b.Headers["DEK-Info"]
   101  	return ok
   102  }
   103  
   104  // IncorrectPasswordError is returned when an incorrect password is detected.
   105  var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
   106  
   107  // DecryptPEMBlock takes a password encrypted PEM block and the password used to
   108  // encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
   109  // the DEK-Info header to determine the algorithm used for decryption. If no
   110  // DEK-Info header is present, an error is returned. If an incorrect password
   111  // is detected an IncorrectPasswordError is returned.
   112  func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
   113  	dek, ok := b.Headers["DEK-Info"]
   114  	if !ok {
   115  		return nil, errors.New("x509: no DEK-Info header in block")
   116  	}
   117  
   118  	idx := strings.Index(dek, ",")
   119  	if idx == -1 {
   120  		return nil, errors.New("x509: malformed DEK-Info header")
   121  	}
   122  
   123  	mode, hexIV := dek[:idx], dek[idx+1:]
   124  	ciph := cipherByName(mode)
   125  	if ciph == nil {
   126  		return nil, errors.New("x509: unknown encryption mode")
   127  	}
   128  	iv, err := hex.DecodeString(hexIV)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	if len(iv) != ciph.blockSize {
   133  		return nil, errors.New("x509: incorrect IV size")
   134  	}
   135  
   136  	// Based on the OpenSSL implementation. The salt is the first 8 bytes
   137  	// of the initialization vector.
   138  	key := ciph.deriveKey(password, iv[:8])
   139  	block, err := ciph.cipherFunc(key)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	data := make([]byte, len(b.Bytes))
   145  	dec := cipher.NewCBCDecrypter(block, iv)
   146  	dec.CryptBlocks(data, b.Bytes)
   147  
   148  	// Blocks are padded using a scheme where the last n bytes of padding are all
   149  	// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
   150  	// For example:
   151  	//	[x y z 2 2]
   152  	//	[x y 7 7 7 7 7 7 7]
   153  	// If we detect a bad padding, we assume it is an invalid password.
   154  	dlen := len(data)
   155  	if dlen == 0 || dlen%ciph.blockSize != 0 {
   156  		return nil, errors.New("x509: invalid padding")
   157  	}
   158  	last := int(data[dlen-1])
   159  	if dlen < last {
   160  		return nil, IncorrectPasswordError
   161  	}
   162  	if last == 0 || last > ciph.blockSize {
   163  		return nil, IncorrectPasswordError
   164  	}
   165  	for _, val := range data[dlen-last:] {
   166  		if int(val) != last {
   167  			return nil, IncorrectPasswordError
   168  		}
   169  	}
   170  	return data[:dlen-last], nil
   171  }
   172  
   173  // EncryptPEMBlock returns a PEM block of the specified type holding the
   174  // given DER-encoded data encrypted with the specified algorithm and
   175  // password.
   176  func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
   177  	ciph := cipherByKey(alg)
   178  	if ciph == nil {
   179  		return nil, errors.New("x509: unknown encryption mode")
   180  	}
   181  	iv := make([]byte, ciph.blockSize)
   182  	if _, err := io.ReadFull(rand, iv); err != nil {
   183  		return nil, errors.New("x509: cannot generate IV: " + err.Error())
   184  	}
   185  	// The salt is the first 8 bytes of the initialization vector,
   186  	// matching the key derivation in DecryptPEMBlock.
   187  	key := ciph.deriveKey(password, iv[:8])
   188  	block, err := ciph.cipherFunc(key)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	enc := cipher.NewCBCEncrypter(block, iv)
   193  	pad := ciph.blockSize - len(data)%ciph.blockSize
   194  	encrypted := make([]byte, len(data), len(data)+pad)
   195  	// We could save this copy by encrypting all the whole blocks in
   196  	// the data separately, but it doesn't seem worth the additional
   197  	// code.
   198  	copy(encrypted, data)
   199  	// See RFC 1423, section 1.1
   200  	for i := 0; i < pad; i++ {
   201  		encrypted = append(encrypted, byte(pad))
   202  	}
   203  	enc.CryptBlocks(encrypted, encrypted)
   204  
   205  	return &pem.Block{
   206  		Type: blockType,
   207  		Headers: map[string]string{
   208  			"Proc-Type": "4,ENCRYPTED",
   209  			"DEK-Info":  ciph.name + "," + hex.EncodeToString(iv),
   210  		},
   211  		Bytes: encrypted,
   212  	}, nil
   213  }
   214  
   215  func cipherByName(name string) *rfc1423Algo {
   216  	for i := range rfc1423Algos {
   217  		alg := &rfc1423Algos[i]
   218  		if alg.name == name {
   219  			return alg
   220  		}
   221  	}
   222  	return nil
   223  }
   224  
   225  func cipherByKey(key PEMCipher) *rfc1423Algo {
   226  	for i := range rfc1423Algos {
   227  		alg := &rfc1423Algos[i]
   228  		if alg.cipher == key {
   229  			return alg
   230  		}
   231  	}
   232  	return nil
   233  }