繁体   English   中英

AES/GCM/NoPadding加密JAVA&解密JavaScript

[英]AES/GCM/NoPadding encryption in JAVA & decryption in JavaScript

我正在使用 AES/GCM/NoPadding 128 位。 我想在 Java 中加密并在 Javascript 中解密。

当我在 JS 中加密并尝试在 Java 中解密时,我收到错误标签不匹配!null

当我在 Java 中加密并尝试在 JS 中解密时,出现如下错误

internal/crypto/cipher.js:164
  const ret = this._handle.final();
                           ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (internal/crypto/cipher.js:164:28)
    at decrypt (/tmp/HoErdq6TQ2.js:51:58)

我的 JS 中缺少什么请建议修复 JS 代码,Java 代码无法更改,因为它正在生产中使用

Java 代码:


import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.util.logging.Logger;
import java.util.Base64;

public class HelloWorld {

    private final static Logger LOGGER =  Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    public static void main(String []args) {
        String masterKey = "2f12cb0f1d2e3d12345f1af2b123dce4";
        String encrypted = aesEncryptStringV2("Hello, World!", masterKey);
        System.out.println(encrypted);
        

        String decrypted = aesDecryptStringV2(encrypted, masterKey);
        System.out.println(decrypted);
    }


    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 16;

    private static SecretKeySpec setKeyV2(final String myKey) {
        try {
            byte[] newKey = myKey.getBytes(StandardCharsets.UTF_8);
            MessageDigest sha = MessageDigest.getInstance("SHA-512");

            newKey = sha.digest(newKey);
            newKey = Arrays.copyOf(newKey, 16);

            return new SecretKeySpec(newKey, "AES");
        } catch (Exception e) {
            System.out.println("Error in setKeyV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesEncryptStringV2(
        final String strToEncrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.ENCRYPT_MODE, newSecretKey, gcmParameterSpec);
            return Base64.getEncoder()
                   .encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8
                                                                       )));
        } catch (Exception e) {
            System.out.println("Error in aesEncryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }

    public static synchronized String aesDecryptStringV2(
        final String strToDecrypt, final String secret) {
        try {
            SecretKeySpec newSecretKey = setKeyV2(secret);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new
            GCMParameterSpec(GCM_TAG_LENGTH * 8,
                             new byte[GCM_IV_LENGTH]);
            cipher.init(Cipher.DECRYPT_MODE, newSecretKey, gcmParameterSpec);

            return new
                   String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error in aesDecryptStringV2: ");
            System.out.println(e.getMessage());
        }
        return null;
    }
}

Javascript 代码:


const crypto = require('crypto');


const cryptoConfig = {
    cipherAlgorithm: 'aes-128-gcm',
    masterKey: '2f12cb0f1d2e3d12345f1af2b123dce4',
    ivLength: 12,
    tagLength: 16,
    digest: 'sha512'
}

const generateKey = () => {
    var h = crypto.createHash(cryptoConfig.digest);
    h.update(cryptoConfig.masterKey, 'utf8');
    
    var k = h.digest().slice(0, 16);
    return k;
  };

function encrypt(content) {
    const iv = crypto.randomBytes(cryptoConfig.ivLength);
    const key = generateKey();

    const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv, {authTagLength: cryptoConfig.tagLength});

    const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);

    const tag = cipher.getAuthTag();

    return Buffer.concat([iv, encrypted, tag]).toString('base64');
}


const decrypt = (encdata, masterkey) => {
    const bData = Buffer.from(encdata, 'base64');

    const iv = bData.slice(0, 12);
    const tag = bData.slice(bData.length - 16, bData.length);
    const text = bData.slice(12, bData.length - 16);

    const key = generateKey(masterkey);

    const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
    decipher.setAuthTag(tag);


    const decrypted =
      decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


    return decrypted;
  };

const encryptedData = encrypt('hello world'); 
console.log('encrypt data -> ', encryptedData);

const decryptedData = decrypt(encryptedData); 
console.log('decryptedData -> ', decryptedData);

因此 Java 代码不会将 IV/nonce 附加到 output。它使用指定长度的 0 缓冲区。

更新后的 JS 解密代码:

const decrypt = (encdata, masterkey) => {
const bData = Buffer.from(encdata, 'base64');

const iv = Buffer.alloc(12);
const tag = bData.slice(bData.length - 16, bData.length);
const text = bData.slice(0, bData.length - 16);

const key = generateKey(masterkey);

const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
decipher.setAuthTag(tag);


const decrypted =
  decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');


return decrypted;
};

严重的安全警告:不要在生产中使用上面的代码,因为它是不安全的。 您应该考虑更改生产代码,因为当使用同一密钥多次使用随机数时,AES GCM 模式下的加密变得完全不安全(Java 代码使用 static IV/随机数)

致谢 - Michael Fehr 的评论

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM