...

Source file src/pkg/crypto/x509/pem_decrypt.go

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

View as plain text