You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js与Java AES-GCM 256跨语言加解密互兼容问题求助

AES-GCM跨Java/Node.js加解密兼容问题解决

核心问题分析

  1. Java代码错误:GCMParameterSpec构造参数使用错误,第一个参数应为认证标签(Tag)的长度(单位:比特),而非AAD的长度。你代码里误用了aad.length*8,虽数值巧合等于128比特(16字节),但未调用updateAAD()导致AAD为空;另外Java中doFinal()返回的字节数组是密文 + Tag的组合,密文长度等于明文长度,并非固定16字节。
  2. Node.js代码错误:错误固定截取前16字节作为密文,应从末尾取16字节作为Tag,剩余部分为密文;同时decipher.update()的编码参数使用错误,密文已为Buffer,无需再按base64解析。

修正后的Java加密代码

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encoder3 {
    public static void main(String[] args) throws Exception {
        String base64Key = "XHgxl8qs5D6sejZncSsYg7OqPIeV0uQe5I9Zh+uHLcc=";
        String originalString = "Hello, NODE JS!";

        // GCM标准推荐IV长度为12字节(96比特)
        byte[] iv = new byte[12];
        new SecureRandom().nextBytes(iv);
        
        // 若不需要AAD可注释以下两行,需传递AAD给Node.js时保留
        byte[] aad = new byte[16];
        new SecureRandom().nextBytes(aad);
        
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        SecretKey key = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        // 第一个参数为Tag长度(128比特=16字节),第二个为IV
        GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
        
        // 使用AAD时必须调用此方法,否则跳过
        cipher.updateAAD(aad);

        // doFinal返回结果:密文 + Tag
        byte[] cipherTextWithTag = cipher.doFinal(originalString.getBytes(StandardCharsets.UTF_8));

        String encodedString = Base64.getEncoder().encodeToString(cipherTextWithTag);
        String ivString = Base64.getEncoder().encodeToString(iv);
        String aadString = Base64.getEncoder().encodeToString(aad); // 使用AAD时传递此值
        
        System.out.println("Encoded String (密文+Tag): " + encodedString);
        System.out.println("IV String: " + ivString);
        System.out.println("AAD String: " + aadString); // 使用AAD时输出
    }
}

修正后的Node.js解密代码

const crypto = require('crypto');

function decrypt(encodedString, iv, base64Key, base64Aad = '') {
    const key = Buffer.from(base64Key, 'base64');
    const ivBuffer = Buffer.from(iv, 'base64');
    const cipherTextWithTagBuffer = Buffer.from(encodedString, 'base64');
    
    // 从末尾截取16字节作为Tag,剩余部分为密文
    const tag = cipherTextWithTagBuffer.slice(-16);
    const cipherText = cipherTextWithTagBuffer.slice(0, -16);

    const decipher = crypto.createDecipheriv('aes-256-gcm', key, ivBuffer);
    
    // Java使用AAD时必须设置,否则跳过
    if (base64Aad) {
        decipher.setAAD(Buffer.from(base64Aad, 'base64'));
    }
    decipher.setAuthTag(tag);
    
    // 直接传递Buffer,无需base64编码参数
    let decrypted = decipher.update(cipherText, undefined, 'utf-8');
    decrypted += decipher.final('utf-8');

    return decrypted;
}

// Example usage
const base64Key = 'XHgxl8qs5D6sejZncSsYg7OqPIeV0uQe5I9Zh+uHLcc=';
// 替换为Java输出的Encoded String
const encodedStringFromJava = '替换为Java生成的密文+Tag的Base64字符串';
// 替换为Java输出的IV String
const iv = '替换为Java生成的IV的Base64字符串';
// Java使用AAD时替换为对应值,否则留空
const aad = '';

const decodedString = decrypt(encodedStringFromJava, iv, base64Key, aad);
console.log('Decoded String:', decodedString);

关键注意事项

  • IV规范:GCM模式优先使用12字节(96比特)IV,这是行业标准,兼顾安全性与性能。
  • Tag一致性:两端Tag长度必须统一,常用128比特(16字节)。
  • AAD同步:若任意一方使用AAD,另一方必须传入完全相同的AAD值,否则解密会触发认证失败。
  • 密文格式:Java输出的密文是「明文长度的密文 + 16字节Tag」,Node.js需按此动态拆分,不能固定截取长度。

内容的提问来源于stack exchange,提问作者Priyabrata Nayak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 12:45:27