...

Source file src/crypto/x509/pem_decrypt.go

Documentation: crypto/x509

		 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 whether the PEM block is password encrypted
		99  // according to RFC 1423.
	 100  //
	 101  // Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by
	 102  // design. Since it does not authenticate the ciphertext, it is vulnerable to
	 103  // padding oracle attacks that can let an attacker recover the plaintext.
	 104  func IsEncryptedPEMBlock(b *pem.Block) bool {
	 105  	_, ok := b.Headers["DEK-Info"]
	 106  	return ok
	 107  }
	 108  
	 109  // IncorrectPasswordError is returned when an incorrect password is detected.
	 110  var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
	 111  
	 112  // DecryptPEMBlock takes a PEM block encrypted according to RFC 1423 and the
	 113  // password used to encrypt it and returns a slice of decrypted DER encoded
	 114  // bytes. It inspects the DEK-Info header to determine the algorithm used for
	 115  // decryption. If no DEK-Info header is present, an error is returned. If an
	 116  // incorrect password is detected an IncorrectPasswordError is returned. Because
	 117  // of deficiencies in the format, it's not always possible to detect an
	 118  // incorrect password. In these cases no error will be returned but the
	 119  // decrypted DER bytes will be random noise.
	 120  //
	 121  // Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by
	 122  // design. Since it does not authenticate the ciphertext, it is vulnerable to
	 123  // padding oracle attacks that can let an attacker recover the plaintext.
	 124  func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
	 125  	dek, ok := b.Headers["DEK-Info"]
	 126  	if !ok {
	 127  		return nil, errors.New("x509: no DEK-Info header in block")
	 128  	}
	 129  
	 130  	idx := strings.Index(dek, ",")
	 131  	if idx == -1 {
	 132  		return nil, errors.New("x509: malformed DEK-Info header")
	 133  	}
	 134  
	 135  	mode, hexIV := dek[:idx], dek[idx+1:]
	 136  	ciph := cipherByName(mode)
	 137  	if ciph == nil {
	 138  		return nil, errors.New("x509: unknown encryption mode")
	 139  	}
	 140  	iv, err := hex.DecodeString(hexIV)
	 141  	if err != nil {
	 142  		return nil, err
	 143  	}
	 144  	if len(iv) != ciph.blockSize {
	 145  		return nil, errors.New("x509: incorrect IV size")
	 146  	}
	 147  
	 148  	// Based on the OpenSSL implementation. The salt is the first 8 bytes
	 149  	// of the initialization vector.
	 150  	key := ciph.deriveKey(password, iv[:8])
	 151  	block, err := ciph.cipherFunc(key)
	 152  	if err != nil {
	 153  		return nil, err
	 154  	}
	 155  
	 156  	if len(b.Bytes)%block.BlockSize() != 0 {
	 157  		return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
	 158  	}
	 159  
	 160  	data := make([]byte, len(b.Bytes))
	 161  	dec := cipher.NewCBCDecrypter(block, iv)
	 162  	dec.CryptBlocks(data, b.Bytes)
	 163  
	 164  	// Blocks are padded using a scheme where the last n bytes of padding are all
	 165  	// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
	 166  	// For example:
	 167  	//	[x y z 2 2]
	 168  	//	[x y 7 7 7 7 7 7 7]
	 169  	// If we detect a bad padding, we assume it is an invalid password.
	 170  	dlen := len(data)
	 171  	if dlen == 0 || dlen%ciph.blockSize != 0 {
	 172  		return nil, errors.New("x509: invalid padding")
	 173  	}
	 174  	last := int(data[dlen-1])
	 175  	if dlen < last {
	 176  		return nil, IncorrectPasswordError
	 177  	}
	 178  	if last == 0 || last > ciph.blockSize {
	 179  		return nil, IncorrectPasswordError
	 180  	}
	 181  	for _, val := range data[dlen-last:] {
	 182  		if int(val) != last {
	 183  			return nil, IncorrectPasswordError
	 184  		}
	 185  	}
	 186  	return data[:dlen-last], nil
	 187  }
	 188  
	 189  // EncryptPEMBlock returns a PEM block of the specified type holding the
	 190  // given DER encoded data encrypted with the specified algorithm and
	 191  // password according to RFC 1423.
	 192  //
	 193  // Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by
	 194  // design. Since it does not authenticate the ciphertext, it is vulnerable to
	 195  // padding oracle attacks that can let an attacker recover the plaintext.
	 196  func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
	 197  	ciph := cipherByKey(alg)
	 198  	if ciph == nil {
	 199  		return nil, errors.New("x509: unknown encryption mode")
	 200  	}
	 201  	iv := make([]byte, ciph.blockSize)
	 202  	if _, err := io.ReadFull(rand, iv); err != nil {
	 203  		return nil, errors.New("x509: cannot generate IV: " + err.Error())
	 204  	}
	 205  	// The salt is the first 8 bytes of the initialization vector,
	 206  	// matching the key derivation in DecryptPEMBlock.
	 207  	key := ciph.deriveKey(password, iv[:8])
	 208  	block, err := ciph.cipherFunc(key)
	 209  	if err != nil {
	 210  		return nil, err
	 211  	}
	 212  	enc := cipher.NewCBCEncrypter(block, iv)
	 213  	pad := ciph.blockSize - len(data)%ciph.blockSize
	 214  	encrypted := make([]byte, len(data), len(data)+pad)
	 215  	// We could save this copy by encrypting all the whole blocks in
	 216  	// the data separately, but it doesn't seem worth the additional
	 217  	// code.
	 218  	copy(encrypted, data)
	 219  	// See RFC 1423, Section 1.1.
	 220  	for i := 0; i < pad; i++ {
	 221  		encrypted = append(encrypted, byte(pad))
	 222  	}
	 223  	enc.CryptBlocks(encrypted, encrypted)
	 224  
	 225  	return &pem.Block{
	 226  		Type: blockType,
	 227  		Headers: map[string]string{
	 228  			"Proc-Type": "4,ENCRYPTED",
	 229  			"DEK-Info":	ciph.name + "," + hex.EncodeToString(iv),
	 230  		},
	 231  		Bytes: encrypted,
	 232  	}, nil
	 233  }
	 234  
	 235  func cipherByName(name string) *rfc1423Algo {
	 236  	for i := range rfc1423Algos {
	 237  		alg := &rfc1423Algos[i]
	 238  		if alg.name == name {
	 239  			return alg
	 240  		}
	 241  	}
	 242  	return nil
	 243  }
	 244  
	 245  func cipherByKey(key PEMCipher) *rfc1423Algo {
	 246  	for i := range rfc1423Algos {
	 247  		alg := &rfc1423Algos[i]
	 248  		if alg.cipher == key {
	 249  			return alg
	 250  		}
	 251  	}
	 252  	return nil
	 253  }
	 254  

View as plain text