我试图将我的java代码转换为NodeJs代码。这有点复杂,因为自定义格式包括密码和盐。
在main方法中有一个例子。
下面是我的java代码:public class App {
private static final int DYN_SALT_LENGTH = 10;
private static final int ITERATION_COUNT = 65556;
private static final int KEY_LENGTH = 256;
private static final String SECRET_KEY_ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMER = "AES/CBC/PKCS5Padding";
private static Base64 base64Instance = new Base64();
public static String decrypt(String data, String password, String salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
ITERATION_COUNT,
KEY_LENGTH);
SecretKey secretKey = factory.generateSecret(spec);
ByteBuffer buffer = ByteBuffer.wrap(base64Instance.decode(data));
buffer.position(DYN_SALT_LENGTH);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
// Read the IV
byte[] ivBytes = new byte[cipher.getBlockSize()];
buffer.get(ivBytes, 0, ivBytes.length);
// Read encrypted text.
byte[] encryptedTextBytes = new byte[buffer.capacity() - DYN_SALT_LENGTH - ivBytes.length];
buffer.get(encryptedTextBytes);
// Initialize Cipher.
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
String result = new String(cipher.doFinal(encryptedTextBytes), StandardCharsets.UTF_8);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to decrypt data", e);
}
}
public static String encrypt(String data, String password, String salt) {
// Create new salt for every new encryption request.
byte[] saltBytes = new byte[DYN_SALT_LENGTH];
new SecureRandom().nextBytes(saltBytes);
try {
// Create secret key spec.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
ITERATION_COUNT,
KEY_LENGTH);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
byte[] ivBytes;
byte[] encryptedTextBytes;
// Initialize cipher
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// Create initialization vector IV
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
// Encrypt the text.
encryptedTextBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
// Response will be in the form of <salt><IV><encryptedText>
ByteBuffer byteBuffer = ByteBuffer.allocate(saltBytes.length + ivBytes.length + encryptedTextBytes.length);
byteBuffer.put(saltBytes);
byteBuffer.put(ivBytes);
byteBuffer.put(encryptedTextBytes);
return base64Instance.encodeToString(byteBuffer.array());
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt data", e);
}
}
public static void main(String[] args) {
String password = "password";
String salt = "salt";
String data = "hello world";
String resultEncrypted = encrypt(data, password, salt);
System.out.println(resultEncrypted);
String resultDecrypted = decrypt(resultEncrypted, password, salt);
System.out.println(resultDecrypted);
}
}
我正在尝试使用JS代码,但没有关于我做错了什么的线索:
function getAlgorithm(keyBase64) {
var key = Buffer.from(keyBase64, "base64");
switch (key.length) {
case 16:
return "aes-128-cbc";
case 32:
return "aes-256-cbc";
}
throw new Error("Invalid key length: " + key.length);
}
function decrypt(messagebase64, keyBase64, ivBase64) {
const key = Buffer.from(keyBase64, "base64");
const iv = Buffer.from(ivBase64, "base64");
const decipher = crypto.createDecipheriv(
getAlgorithm(keyBase64),
key,
iv.slice(0, 16)
);
let decrypted = decipher.update(messagebase64, "base64", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
const base64Encrypted =
"2vSIh0J64zhrQuayUV+UIyPTpmSaN4gAv7B3CVC/a68eBfeU0bMwRm2I";
const key = crypto.scryptSync("password", "salt", 16);
const encrypted = Buffer.from(base64Encrypted, "base64");
const encryptedWOSalt = Buffer.from(base64Encrypted, "base64").slice(10);
const iv = encrypted.slice(10, 10 + 17);
const result = decrypt(
encryptedWOSalt.toString("base64"),
key,
iv.toString("base64")
);
console.log(result);
抛出错误:Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
因为你只发布了一个NodeJS解密代码,我专注于解密。加密将以类似的方式实现。如果您对此有疑问,请用相应的加密代码发布一个新的问题。
NodeJS代码中有几个bug:
- 密钥派生错误(在NodeJS代码中使用脚本,而在Java代码中使用PBKDF2/HMAC-SHA1)
- 盐、IV和密文分离错误/缺失
- 编码错误和不必要的编码/解码周期
下面的NodeJS代码可以工作:
var crypto = require('crypto')
function getAlgorithm(key) {
switch (key.length) {
case 16:
return "aes-128-cbc";
case 32:
return "aes-256-cbc";
}
throw new Error("Invalid key length: " + key.length);
}
function decrypt(message, key, iv) {
const decipher = crypto.createDecipheriv(
getAlgorithm(key),
key,
iv
);
let decrypted = Buffer.concat([decipher.update(message), decipher.final()]);
return decrypted.toString("utf8");
}
const DYN_SALT_LENGTH = 10;
const IV_LENGTH = 16;
const ITERATION_COUNT = 65556;
const KEY_LENGTH = 256;
const base64Encrypted = "ossqoyCaaQINWUkTsHNGRe5Isd5s7c7U8KcLua78Ehm9jAxQNOd2tyjj";
// Separate salt, IV and ciphertext
const encrypted = Buffer.from(base64Encrypted, "base64");
const salt = encrypted.slice(0, DYN_SALT_LENGTH);
const iv = encrypted.slice(DYN_SALT_LENGTH, DYN_SALT_LENGTH + IV_LENGTH);
const ciphertext = encrypted.slice(DYN_SALT_LENGTH + IV_LENGTH);
// Derive key voa PBKDF2/HMAC-SHA1
const key = crypto.pbkdf2Sync("password", "salt", ITERATION_COUNT, KEY_LENGTH/8, "sha1");
// Decrypt
const result = decrypt(
ciphertext,
key,
iv
);
console.log(result); // hello world
请注意,当前的Java代码(因此也包括NodeJS代码)不使用随机盐进行密钥派生,而是使用静态盐,可能用于测试目的。在最终溶液中,出于安全考虑,将使用随机盐。