1 回答

TA貢獻1818條經驗 獲得超7個贊
在 CryptoJS 代碼中,第二個參數 incrypto.AES.encrypt()
作為字符串傳遞,因此它被解釋為密碼短語。
因此,在加密期間,首先創建一個 8 字節的 salt,然后使用 KDF 導出密碼短語、密鑰和 IV EVP_BytesToKey()
。
createRandomIv()
使用并顯式傳入的 IV將crypto.AES.encrypt()
被忽略!
hash.ToString(
) 以 OpenSSL 格式返回結果,該格式由前綴Salted__后跟 salt 和實際密文組成,均采用 Base64 編碼。eHex
包含相同的數據,但十六進制而不是 Base64 編碼。
CryptoJS 不會自動禁用 CTR 等流密碼模式的填充,因此使用 PKCS#7 填充數據,盡管這對于 CTR 不是必需的。
在 Go 代碼中,必須首先刪除不需要的 IV。從剩余的數據中,確定鹽和密文。
從 salt 和 passphrase 中,可以使用 檢索密鑰和 IV evp.BytesToKeyAES256CBCMD5()
。
使用密鑰和 IV 可以使用 AES-CTR 進行解密。
最后,必須刪除 PKCS#7 填充。
下面的 Go 代碼實現了這些步驟。輸入數據是使用 NodeJS 代碼生成的:
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"strings"
"github.com/walkert/go-evp"
)
func main() {
// Determine salt and actual ciphertext
encryptedPwd := "2db5c01b4825b6d4dd7a7b96f04f3bb5:53616c7465645f5f66cbd1d539b6e51d45efded11e2211fa5e02278855dc86145d4e4891b0e25df9df96fb97a10a9f444f4519f2da4c69c430c5cbf3e9803a1f"
split := strings.Split(encryptedPwd, ":")
saltCiphertext, _ := hex.DecodeString(split[1])
salt := saltCiphertext[8:16]
ciphertext := saltCiphertext[16:]
// Get key and IV
key, iv := evp.BytesToKeyAES256CBCMD5([]byte(salt), []byte("b676eac8cf70442385dfd4bcfaa61b52"))
// Decrypt
block, _ := aes.NewCipher(key)
plaintext := make([]byte, len(ciphertext))
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(plaintext, ciphertext)
// Unpad
unpaddedPlaintext := PKCS7Unpad(plaintext)
fmt.Println("Decrypted data: ", string(unpaddedPlaintext)) // Decrypted data: The quick brown fox jumps over the lazy dog
}
func PKCS7Unpad(src []byte) []byte {
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
}
關于安全性:
CryptoJS 執行的密鑰和 IV 的派生在EVP_BytesToKey()今天被認為是不安全的。
更安全的替代方法是將第二個參數作為 傳遞WordArray,以便將其解釋為密鑰并直接使用。
對于每個加密,必須生成一個隨機 IV。
可選地,可靠的密鑰派生(例如 PBKDF2)可以與為每個加密隨機生成的鹽結合使用。
IV 和 salt(都不是秘密)將與密文連接。
最好用GCM代替CTR作為密文,這樣可以驗證密文的真實性。
- 1 回答
- 0 關注
- 268 瀏覽
添加回答
舉報