Copyright 2012 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package x509
RFC 1423 describes the encryption of PEM blocks. The algorithm used to generate a key from the password was derived by looking at the OpenSSL implementation.

import (
	
	
	
	
	
	
	
	
	
)

type PEMCipher int
Possible values for the EncryptPEMBlock encryption algorithm.
rfc1423Algo holds a method for enciphering a PEM block.
rfc1423Algos holds a slice of the possible ways to encrypt a PEM block. The ivSize numbers were taken from the OpenSSL source.
deriveKey uses a key derivation function to stretch the password into a key with the number of bits our cipher requires. This algorithm was derived from the OpenSSL source.
func ( rfc1423Algo) (,  []byte) []byte {
	 := md5.New()
	 := make([]byte, .keySize)
	var  []byte

	for  := 0;  < len();  += len() {
		.Reset()
		.Write()
		.Write()
		.Write()
		 = .Sum([:0])
		copy([:], )
	}
	return 
}
IsEncryptedPEMBlock returns whether the PEM block is password encrypted according to RFC 1423. Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by design. Since it does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.
func ( *pem.Block) bool {
	,  := .Headers["DEK-Info"]
	return 
}
IncorrectPasswordError is returned when an incorrect password is detected.
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
DecryptPEMBlock takes a PEM block encrypted according to RFC 1423 and the password used to encrypt it and returns a slice of decrypted DER encoded bytes. It inspects the DEK-Info header to determine the algorithm used for decryption. If no DEK-Info header is present, an error is returned. If an incorrect password is detected an IncorrectPasswordError is returned. Because of deficiencies in the format, it's not always possible to detect an incorrect password. In these cases no error will be returned but the decrypted DER bytes will be random noise. Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by design. Since it does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.
func ( *pem.Block,  []byte) ([]byte, error) {
	,  := .Headers["DEK-Info"]
	if ! {
		return nil, errors.New("x509: no DEK-Info header in block")
	}

	 := strings.Index(, ",")
	if  == -1 {
		return nil, errors.New("x509: malformed DEK-Info header")
	}

	,  := [:], [+1:]
	 := cipherByName()
	if  == nil {
		return nil, errors.New("x509: unknown encryption mode")
	}
	,  := hex.DecodeString()
	if  != nil {
		return nil, 
	}
	if len() != .blockSize {
		return nil, errors.New("x509: incorrect IV size")
	}
Based on the OpenSSL implementation. The salt is the first 8 bytes of the initialization vector.
	 := .deriveKey(, [:8])
	,  := .cipherFunc()
	if  != nil {
		return nil, 
	}

	if len(.Bytes)%.BlockSize() != 0 {
		return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
	}

	 := make([]byte, len(.Bytes))
	 := cipher.NewCBCDecrypter(, )
	.CryptBlocks(, .Bytes)
Blocks are padded using a scheme where the last n bytes of padding are all equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423. For example: [x y z 2 2] [x y 7 7 7 7 7 7 7] If we detect a bad padding, we assume it is an invalid password.
	 := len()
	if  == 0 || %.blockSize != 0 {
		return nil, errors.New("x509: invalid padding")
	}
	 := int([-1])
	if  <  {
		return nil, IncorrectPasswordError
	}
	if  == 0 ||  > .blockSize {
		return nil, IncorrectPasswordError
	}
	for ,  := range [-:] {
		if int() !=  {
			return nil, IncorrectPasswordError
		}
	}
	return [:-], nil
}
EncryptPEMBlock returns a PEM block of the specified type holding the given DER encoded data encrypted with the specified algorithm and password according to RFC 1423. Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by design. Since it does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.
func ( io.Reader,  string, ,  []byte,  PEMCipher) (*pem.Block, error) {
	 := cipherByKey()
	if  == nil {
		return nil, errors.New("x509: unknown encryption mode")
	}
	 := make([]byte, .blockSize)
	if ,  := io.ReadFull(, );  != nil {
		return nil, errors.New("x509: cannot generate IV: " + .Error())
The salt is the first 8 bytes of the initialization vector, matching the key derivation in DecryptPEMBlock.
	 := .deriveKey(, [:8])
	,  := .cipherFunc()
	if  != nil {
		return nil, 
	}
	 := cipher.NewCBCEncrypter(, )
	 := .blockSize - len()%.blockSize
We could save this copy by encrypting all the whole blocks in the data separately, but it doesn't seem worth the additional code.
See RFC 1423, Section 1.1.
	for  := 0;  < ; ++ {
		 = append(, byte())
	}
	.CryptBlocks(, )

	return &pem.Block{
		Type: ,
		Headers: map[string]string{
			"Proc-Type": "4,ENCRYPTED",
			"DEK-Info":  .name + "," + hex.EncodeToString(),
		},
		Bytes: ,
	}, nil
}

func ( string) *rfc1423Algo {
	for  := range rfc1423Algos {
		 := &rfc1423Algos[]
		if .name ==  {
			return 
		}
	}
	return nil
}

func ( PEMCipher) *rfc1423Algo {
	for  := range rfc1423Algos {
		 := &rfc1423Algos[]
		if .cipher ==  {
			return 
		}
	}
	return nil