我在 node.js 中有以下代碼,使用 crypto-js 使用帶有密鑰和 IV 的 AES 加密密碼。
const crypto = require('crypto-js');
const cryptKey = 'b676eac8cf70442385dfd4bcfaa61b52';
const createRandomIv = function () {
const keySize = 192 / 32;
const ivSize = 128 / 32;
const evp = crypto.algo.EvpKDF.create({ keySize: keySize ivSize, hasher: crypto.algo.SHA1 }).compute(cryptKey);
const iv = crypto.lib.WordArray.create(evp.words.slice(keySize), ivSize * 4);
return iv.toString();
};
const encryptPassword = function (password) {
const iv = createRandomIv();
const hash = crypto.AES.encrypt(
password,
cryptKey, {
iv,
mode: crypto.mode.CTR
}
);
const base64 = crypto.enc.Base64.parse(hash.toString());
const eHex = base64.toString(crypto.enc.Hex);
return `${iv}:${eHex}`;
};
const decryptPassword = function (encryptedPwd) {
const split = encryptedPwd.split(':');
if (split.length < 2) return '';
const reb64 = crypto.enc.Hex.parse(split[1]);
const bytes = reb64.toString(crypto.enc.Base64);
const hash = crypto.AES.decrypt(bytes, cryptKey, {
iv: split[0],
mode: crypto.mode.CTR
});
const plain = hash.toString(crypto.enc.Utf8);
return plain;
};
這是來自節點 js 的加密密碼。
const encryptedPassword = encryptPassword("Stack Overflow");
console.log(encryptedPassword);
// 2db5c01b4825b6d4dd7a7b96f04f3bb5:53616c7465645f5f691671363cda1b9d05ee6bdd637e1e99bc3b29ef2ad7ec53
并且已經嘗試使用 golang 對其進行解密,如下所示
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"strings"
)
func main() {
secretKey := "b676eac8cf70442385dfd4bcfaa61b52"
encryptedPwd := "2db5c01b4825b6d4dd7a7b96f04f3bb5:53616c7465645f5f691671363cda1b9d05ee6bdd637e1e99bc3b29ef2ad7ec53"
split := strings.Split(encryptedPwd, ":")
c, _ := aes.NewCipher([]byte(secretKey))
cfbdec := cipher.NewCBCDecrypter(c, []byte(split[0]))
plaintext := make([]byte, len(split[1]))
cfbdec.CryptBlocks(plaintext, []byte(split[1]))
fmt.Println(plaintext)
}
但它恐慌如下。
恐慌:cipher.NewCB??CDecrypter:IV 長度必須等于塊大小 goroutine 1 [運行]:crypto/cipher.NewCB??CDecrypter({0x10c4ee8, 0xc000066060}, {0xc00001e040, 0x1, 0x20})
更新 1
我在不使用 iv 的情況下將代碼更新為解密,但結果不是人類可讀的。
split := strings.Split(encryptedPwd, ":")
ciphertext, _ := hex.DecodeString(split[1])
c, _ := aes.NewCipher([]byte(secretKey))
plaintext := make([]byte, len(ciphertext))
c.Decrypt(plaintext, []byte(ciphertext))
fmt.Println(string(plaintext))
|A/ c *Z S/ x
我的golang代碼中的解密有什么問題,有人可以幫我嗎?我已經使用了相同的密鑰,并且通過將其從加密密碼中拆分出來。
uj5u.com熱心網友回復:
在 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作為密文,這樣可以驗證密文的真實性。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/405672.html
標籤:
下一篇:去多級優先佇列
