github.com/insolar/x-crypto@v0.0.0-20191031140942-75fab8a325f6/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 "encoding/hex" 16 "encoding/pem" 17 "errors" 18 "github.com/insolar/x-crypto/md5" 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 }