首页 > 解决方案 > 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;
    }

标签: javanode.jsencryptionaes-gcmnode-crypto

解决方案


所以,我在这里放了 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));
    }


推荐阅读