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.
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
25 // Possible values for the EncryptPEMBlock encryption algorithm.
35 // rfc1423Algo holds a method for enciphering a PEM block.
36 type rfc1423Algo
struct {
39 cipherFunc
func(key
[]byte) (cipher
.Block
, error
)
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
{{
49 cipherFunc
: des
.NewCipher
,
51 blockSize
: des
.BlockSize
,
53 cipher
: PEMCipher3DES
,
55 cipherFunc
: des
.NewTripleDESCipher
,
57 blockSize
: des
.BlockSize
,
59 cipher
: PEMCipherAES128
,
61 cipherFunc
: aes
.NewCipher
,
63 blockSize
: aes
.BlockSize
,
65 cipher
: PEMCipherAES192
,
67 cipherFunc
: aes
.NewCipher
,
69 blockSize
: aes
.BlockSize
,
71 cipher
: PEMCipherAES256
,
73 cipherFunc
: aes
.NewCipher
,
75 blockSize
: aes
.BlockSize
,
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 {
84 out
:= make([]byte, c
.keySize
)
87 for i
:= 0; i
< len(out
); i
+= len(digest
) {
92 digest
= hash
.Sum(digest
[:0])
98 // IsEncryptedPEMBlock returns if the PEM block is password encrypted.
99 func IsEncryptedPEMBlock(b
*pem
.Block
) bool {
100 _
, ok
:= b
.Headers
["DEK-Info"]
104 // IncorrectPasswordError is returned when an incorrect password is detected.
105 var IncorrectPasswordError
= errors
.New("x509: decryption password incorrect")
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"]
118 return nil, errors
.New("x509: no DEK-Info header in block")
121 idx
:= strings
.Index(dek
, ",")
123 return nil, errors
.New("x509: malformed DEK-Info header")
126 mode
, hexIV
:= dek
[:idx
], dek
[idx
+1:]
127 ciph
:= cipherByName(mode
)
129 return nil, errors
.New("x509: unknown encryption mode")
131 iv
, err
:= hex
.DecodeString(hexIV
)
135 if len(iv
) != ciph
.blockSize
{
136 return nil, errors
.New("x509: incorrect IV size")
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
)
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")
151 data
:= make([]byte, len(b
.Bytes
))
152 dec
:= cipher
.NewCBCDecrypter(block
, iv
)
153 dec
.CryptBlocks(data
, b
.Bytes
)
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.
159 // [x y 7 7 7 7 7 7 7]
160 // If we detect a bad padding, we assume it is an invalid password.
162 if dlen
== 0 || dlen%ciph
.blockSize
!= 0 {
163 return nil, errors
.New("x509: invalid padding")
165 last
:= int(data
[dlen
-1])
167 return nil, IncorrectPasswordError
169 if last
== 0 || last
> ciph
.blockSize
{
170 return nil, IncorrectPasswordError
172 for _
, val
:= range data
[dlen
-last
:] {
173 if int(val
) != last
{
174 return nil, IncorrectPasswordError
177 return data
[:dlen
-last
], nil
180 // EncryptPEMBlock returns a PEM block of the specified type holding the
181 // given DER-encoded data encrypted with the specified algorithm and
183 func EncryptPEMBlock(rand io
.Reader
, blockType
string, data
, password
[]byte, alg PEMCipher
) (*pem
.Block
, error
) {
184 ciph
:= cipherByKey(alg
)
186 return nil, errors
.New("x509: unknown encryption mode")
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())
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
)
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
205 copy(encrypted
, data
)
206 // See RFC 1423, section 1.1
207 for i
:= 0; i
< pad
; i
++ {
208 encrypted
= append(encrypted
, byte(pad
))
210 enc
.CryptBlocks(encrypted
, encrypted
)
214 Headers
: map[string]string{
215 "Proc-Type": "4,ENCRYPTED",
216 "DEK-Info": ciph
.name
+ "," + hex
.EncodeToString(iv
),
222 func cipherByName(name
string) *rfc1423Algo
{
223 for i
:= range rfc1423Algos
{
224 alg
:= &rfc1423Algos
[i
]
225 if alg
.name
== name
{
232 func cipherByKey(key PEMCipher
) *rfc1423Algo
{
233 for i
:= range rfc1423Algos
{
234 alg
:= &rfc1423Algos
[i
]
235 if alg
.cipher
== key
{