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