github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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 /* 8 x509/pem_decrypt.go 对pem块做加密的实现。 9 参考标准: RFC 1423 10 其中派生密钥的算法参考的是OpenSSL的实现。 11 */ 12 13 // RFC 1423 describes the encryption of PEM blocks. The algorithm used to 14 // generate a key from the password was derived by looking at the OpenSSL 15 // implementation. 16 17 import ( 18 "crypto/aes" 19 "crypto/cipher" 20 "crypto/des" 21 "crypto/md5" 22 "encoding/hex" 23 "encoding/pem" 24 "errors" 25 "io" 26 "strings" 27 28 "github.com/hxx258456/ccgo/sm4" 29 ) 30 31 type PEMCipher int 32 33 // Possible values for the EncryptPEMBlock encryption algorithm. 34 const ( 35 _ PEMCipher = iota 36 PEMCipherDES 37 PEMCipher3DES 38 PEMCipherAES128 39 PEMCipherAES192 40 PEMCipherAES256 41 PEMCipherSM4 42 ) 43 44 // rfc1423Algo holds a method for enciphering a PEM block. 45 type rfc1423Algo struct { 46 cipher PEMCipher 47 name string 48 cipherFunc func(key []byte) (cipher.Block, error) 49 keySize int 50 blockSize int 51 } 52 53 // rfc1423Algos holds a slice of the possible ways to encrypt a PEM 54 // block. The ivSize numbers were taken from the OpenSSL source. 55 var rfc1423Algos = []rfc1423Algo{{ 56 cipher: PEMCipherDES, 57 name: "DES-CBC", 58 cipherFunc: des.NewCipher, 59 keySize: 8, 60 blockSize: des.BlockSize, 61 }, { 62 cipher: PEMCipher3DES, 63 name: "DES-EDE3-CBC", 64 cipherFunc: des.NewTripleDESCipher, 65 keySize: 24, 66 blockSize: des.BlockSize, 67 }, { 68 cipher: PEMCipherAES128, 69 name: "AES-128-CBC", 70 cipherFunc: aes.NewCipher, 71 keySize: 16, 72 blockSize: aes.BlockSize, 73 }, { 74 cipher: PEMCipherAES192, 75 name: "AES-192-CBC", 76 cipherFunc: aes.NewCipher, 77 keySize: 24, 78 blockSize: aes.BlockSize, 79 }, { 80 cipher: PEMCipherAES256, 81 name: "AES-256-CBC", 82 cipherFunc: aes.NewCipher, 83 keySize: 32, 84 blockSize: aes.BlockSize, 85 }, { 86 // 添加国密SM4的对应实现 87 cipher: PEMCipherSM4, 88 name: "SM4-128-CBC", 89 cipherFunc: sm4.NewCipher, 90 keySize: 16, 91 blockSize: sm4.BlockSize, 92 }, 93 } 94 95 // deriveKey uses a key derivation function to stretch the password into a key 96 // with the number of bits our cipher requires. This algorithm was derived from 97 // the OpenSSL source. 98 func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { 99 hash := md5.New() 100 out := make([]byte, c.keySize) 101 var digest []byte 102 103 for i := 0; i < len(out); i += len(digest) { 104 hash.Reset() 105 hash.Write(digest) 106 hash.Write(password) 107 hash.Write(salt) 108 digest = hash.Sum(digest[:0]) 109 copy(out[i:], digest) 110 } 111 return out 112 } 113 114 // IsEncryptedPEMBlock returns whether the PEM block is password encrypted 115 // according to RFC 1423. 116 func IsEncryptedPEMBlock(b *pem.Block) bool { 117 _, ok := b.Headers["DEK-Info"] 118 return ok 119 } 120 121 // ErrorIncorrectPassword is returned when an incorrect password is detected. 122 var ErrorIncorrectPassword = errors.New("x509: decryption password incorrect") 123 124 // DecryptPEMBlock takes a PEM block encrypted according to RFC 1423 and the 125 // password used to encrypt it and returns a slice of decrypted DER encoded 126 // bytes. It inspects the DEK-Info header to determine the algorithm used for 127 // decryption. If no DEK-Info header is present, an error is returned. If an 128 // incorrect password is detected an ErrorIncorrectPassword is returned. Because 129 // of deficiencies in the format, it's not always possible to detect an 130 // incorrect password. In these cases no error will be returned but the 131 // decrypted DER bytes will be random noise. 132 func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { 133 dek, ok := b.Headers["DEK-Info"] 134 if !ok { 135 return nil, errors.New("x509: no DEK-Info header in block") 136 } 137 138 idx := strings.Index(dek, ",") 139 if idx == -1 { 140 return nil, errors.New("x509: malformed DEK-Info header") 141 } 142 143 mode, hexIV := dek[:idx], dek[idx+1:] 144 ciph := cipherByName(mode) 145 if ciph == nil { 146 return nil, errors.New("x509: unknown encryption mode") 147 } 148 iv, err := hex.DecodeString(hexIV) 149 if err != nil { 150 return nil, err 151 } 152 if len(iv) != ciph.blockSize { 153 return nil, errors.New("x509: incorrect IV size") 154 } 155 156 // Based on the OpenSSL implementation. The salt is the first 8 bytes 157 // of the initialization vector. 158 key := ciph.deriveKey(password, iv[:8]) 159 block, err := ciph.cipherFunc(key) 160 if err != nil { 161 return nil, err 162 } 163 164 if len(b.Bytes)%block.BlockSize() != 0 { 165 return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size") 166 } 167 168 data := make([]byte, len(b.Bytes)) 169 dec := cipher.NewCBCDecrypter(block, iv) 170 dec.CryptBlocks(data, b.Bytes) 171 172 // Blocks are padded using a scheme where the last n bytes of padding are all 173 // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423. 174 // For example: 175 // [x y z 2 2] 176 // [x y 7 7 7 7 7 7 7] 177 // If we detect a bad padding, we assume it is an invalid password. 178 dlen := len(data) 179 if dlen == 0 || dlen%ciph.blockSize != 0 { 180 return nil, errors.New("x509: invalid padding") 181 } 182 last := int(data[dlen-1]) 183 if dlen < last { 184 return nil, ErrorIncorrectPassword 185 } 186 if last == 0 || last > ciph.blockSize { 187 return nil, ErrorIncorrectPassword 188 } 189 for _, val := range data[dlen-last:] { 190 if int(val) != last { 191 return nil, ErrorIncorrectPassword 192 } 193 } 194 return data[:dlen-last], nil 195 } 196 197 // EncryptPEMBlock returns a PEM block of the specified type holding the 198 // given DER encoded data encrypted with the specified algorithm and 199 // password according to RFC 1423. 200 func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) { 201 ciph := cipherByKey(alg) 202 if ciph == nil { 203 return nil, errors.New("x509: unknown encryption mode") 204 } 205 iv := make([]byte, ciph.blockSize) 206 if _, err := io.ReadFull(rand, iv); err != nil { 207 return nil, errors.New("x509: cannot generate IV: " + err.Error()) 208 } 209 // The salt is the first 8 bytes of the initialization vector, 210 // matching the key derivation in DecryptPEMBlock. 211 key := ciph.deriveKey(password, iv[:8]) 212 block, err := ciph.cipherFunc(key) 213 if err != nil { 214 return nil, err 215 } 216 enc := cipher.NewCBCEncrypter(block, iv) 217 pad := ciph.blockSize - len(data)%ciph.blockSize 218 encrypted := make([]byte, len(data), len(data)+pad) 219 // We could save this copy by encrypting all the whole blocks in 220 // the data separately, but it doesn't seem worth the additional 221 // code. 222 copy(encrypted, data) 223 // See RFC 1423, Section 1.1. 224 for i := 0; i < pad; i++ { 225 encrypted = append(encrypted, byte(pad)) 226 } 227 enc.CryptBlocks(encrypted, encrypted) 228 229 return &pem.Block{ 230 Type: blockType, 231 Headers: map[string]string{ 232 "Proc-Type": "4,ENCRYPTED", 233 "DEK-Info": ciph.name + "," + hex.EncodeToString(iv), 234 }, 235 Bytes: encrypted, 236 }, nil 237 } 238 239 func cipherByName(name string) *rfc1423Algo { 240 for i := range rfc1423Algos { 241 alg := &rfc1423Algos[i] 242 if alg.name == name { 243 return alg 244 } 245 } 246 return nil 247 } 248 249 func cipherByKey(key PEMCipher) *rfc1423Algo { 250 for i := range rfc1423Algos { 251 alg := &rfc1423Algos[i] 252 if alg.cipher == key { 253 return alg 254 } 255 } 256 return nil 257 }