github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 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 }