java - nodejs和Java中的AES GCM加密解密
问题描述
因此,我们在nodejs中使用AES GCM加密和解密如下,我们需要在Java中使用它。因此可以在 Java 中加密并在 nodeJs 中解密,反之亦然。
这是节点中的加密解密功能
const encrypt = (text, masterkey) => {
// random initialization vector
const iv = crypto.randomBytes(16);
// random salt
const salt = crypto.randomBytes(64);
// derive encryption key: 32 byte key length
// in assumption the masterkey is a cryptographic and NOT a password there is no need for
// a large number of iterations. It may can replaced by HKDF
// the value of 2145 is randomly chosen!
const key = crypto.pbkdf2Sync(masterkey, salt, 2145, 32, 'sha512');
// AES 256 GCM Mode
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
// encrypt the given text
const encrypted = Buffer.concat([
cipher.update(text, 'utf8'),
cipher.final()
]);
// extract the auth tag
const tag = cipher.getAuthTag();
// generate output
return Buffer.concat([salt, iv, tag, encrypted]).toString('base64');
};
const decrypt = (encdata, masterkey) => {
// base64 decoding
const bData = Buffer.from(encdata, 'base64');
// convert data to buffers
const salt = bData.slice(0, 64);
const iv = bData.slice(64, 80);
const tag = bData.slice(80, 96);
const text = bData.slice(96);
// derive key using; 32 byte key length
const key = crypto.pbkdf2Sync(masterkey, salt, 2145, 32, 'sha512');
// AES 256 GCM Mode
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
// encrypt the given text
return decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
};
这是我在 Java 中所做的,但遇到了input too short
异常。我也尝试过模拟以上 nodeJs 加密功能,但它似乎不起作用。我有初始化向量(IV),盐,与nodejs相同的密钥(在java中它是128位签名的)
public static String decrypt(String encData, String masterKey)
{
var cipherText = Base64.getDecoder().decode(encData.getBytes(StandardCharsets.UTF_8));
var salt = Arrays.copyOfRange(cipherText, 0, 64);
var iv = Arrays.copyOfRange(cipherText, 64, 80);
var tag = Arrays.copyOfRange(cipherText, 80, 96);
var ciphertext = Arrays.copyOfRange(cipherText, 96, cipherText.length);
var key = getKeyFromPassword(masterKey, salt);
//GCM_TAG_LENGTH = 16
return helper("AES/GCM/NoPadding", ciphertext, tag, key, new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv));
}
public static SecretKey getKeyFromPassword(String masterKey, byte[] salt) {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(masterKey.toCharArray(), salt, 2145, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec)
.getEncoded(), "AES");
return secret;
}
public static String helper(String algorithm, byte[] cipherText, SecretKey key,
GCMParameterSpec gcmParameterSpec){
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
cipher.update(cipherText);
byte[] plainText = cipher.doFinal(tag);
return new String(plainText, StandardCharsets.UTF_8);
}
更新:上面的代码适用于java中的解密
但是现在需要在java中加密
这就是我正在做的事情,但是Tag mismatch!
在解密时出现异常。我还更改了标签和密文的顺序,但仍然发生了同样的错误。
public static String encrypt(String text, String masterKey)
{
var iv = generateIv(16);
var salt = generateIv(64);
var key = getKeyFromPassword(masterKey, salt);
var cipher = helper1("AES/GCM/NoPadding", text, key, new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv));
var outputStream = new ByteArrayOutputStream();
var tag = Arrays.copyOfRange(cipher, 0, 16);
var ciphertext = Arrays.copyOfRange(cipher, 16, cipher.length);
outputStream.write(salt);
outputStream.write(iv);
outputStream.write(tag);
outputStream.write(ciphertext);
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
}
public static byte[] generateIv(int N) {
byte[] iv = new byte[N];
new SecureRandom().nextBytes(iv);
return iv;
}
public static byte[] helper1(String algorithm, String input, SecretKey key,
GCMParameterSpec gcmParameterSpec){
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);
return cipher.doFinal(input.getBytes());
}
public static SecretKey getKeyFromPassword(String masterKey, byte[] salt) {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(masterKey.toCharArray(), salt, 2145, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec)
.getEncoded(), "AES");
return secret;
}
解决方案
所以,我在这里放了 Java 版本的加密和解密函数
public static byte[] generateIv(int N) {
byte[] iv = new byte[N];
new SecureRandom().nextBytes(iv);
return iv;
}
public static SecretKey getKeyFromPassword(String masterKey, byte[] salt) {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(masterKey.toCharArray(), salt, 2145, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec)
.getEncoded(), "AES");
return secret;
}
public static byte[] encryptHelper(String algorithm, String input, SecretKey key,
GCMParameterSpec gcmParameterSpec){
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);
return cipher.doFinal(input.getBytes());
}
@SneakyThrows
public static String decryptHelper(String algorithm, byte[] cipherText, byte[] tag,SecretKey key,
GCMParameterSpec gcmParameterSpec){
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
cipher.update(cipherText);
byte[] plainText = cipher.doFinal(tag);
return new String(plainText, StandardCharsets.UTF_8);
}
public static String encrypt(String text, String masterKey)
{
var iv = generateIv(16);
var salt = generateIv(64);
var key = getKeyFromPassword(masterKey, salt);
var cipher = encryptHelper("AES/GCM/NoPadding", text, key, new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv));
var outputStream = new ByteArrayOutputStream();
var ciphertext = Arrays.copyOfRange(cipher, 0, text.length());
var tag = Arrays.copyOfRange(cipher, text.length(), cipher.length);
outputStream.write(salt);
outputStream.write(iv);
outputStream.write(tag);
outputStream.write(ciphertext);
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
}
public static String decrypt(String encData, String masterKey)
{
var cipherText = Base64.getDecoder().decode(encData.getBytes(StandardCharsets.UTF_8));
var salt = Arrays.copyOfRange(cipherText, 0, 64);
var iv = Arrays.copyOfRange(cipherText, 64, 80);
var tag = Arrays.copyOfRange(cipherText, 80, 96);
var ciphertext = Arrays.copyOfRange(cipherText, 96, cipherText.length);
var key = getKeyFromPassword(masterKey, salt);
return decryptHelper("AES/GCM/NoPadding", ciphertext, tag, key, new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv));
}
推荐阅读
- java - 如何使用 @ApiParam 指定日期格式
- html - d3 :使用数据集中的列在散点图上着色点
- python - 如何从 Amazon S3 存储桶加载 xls 文件并转换为 xlsx 并保存到 Amazon S3
- typescript - 在自定义插件中重用 Tensorboard 的前端
- python - Tesseract OCR 没有检测到文本笔画宽度变换框?
- flutter - 释放缓冲区失败:Flutter 中出现未知错误 -1 (1)
- r - 在 R 中的散点图上绘制新点
- jasper-reports - 如何在 CategoryPlot 图表上格式化为两位小数
- powerbi - 从 Power BI Desktop 和 Web 客户端获取实时数据?
- python - Spyder 更新后更新内核模块