javax.crypto.aeadbadtagexception

aelbi1ox  于 2021-07-11  发布在  Java
关注(0)|答案(1)|浏览(591)

我已经围绕这里指定的加密和解密函数实现了一些 Package 器。

private static final Charset UTF = StandardCharsets.UTF_8;

private static String encrypt(String plainText, String aesKey) {
    byte[] cipher = encryptWithPrefixIV(plainText.getBytes(UTF),
            ConversionUtil.hexStringToByteArray(aesKey));
    return new String(cipher, UTF);
}

public static String decrypt(String cipher, String aesKey) {
    byte[] plainText = decryptWithPrefixIV(cipher.getBytes(UTF),
            ConversionUtil.hexStringToByteArray(aesKey));
    return new String(plainText, UTF);
}

函数hexstringtobytearray将字符串转换为十六进制格式,如 "ff"11111111 . 这个功能正在运行,并且经过了很好的测试!为了简洁起见,我不包括这个函数。问题是我越来越 javax.crypto.AEADBadTagException: Tag mismatch! 异常,错误是不可修复的。代码在一段时间内运行良好,并且时不时抛出一个异常。我用于测试的代码是:

public static void main(String[] args) {
    String plainText = "This is something";
    String cipher = encrypt(plainText);
    System.out.println(plainText.equals(decrypt(cipher)));
}

指定链接中提到的功能包括:

private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
private static final int TAG_LENGTH_BIT = 128;
private static final int IV_LENGTH_BYTE = 12;

public static byte[] encryptWithPrefixIV(byte[] pText, byte[] aesKey) {
    byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
    byte[] cipherText = encryptWithIV(pText, aesKey, iv);

    return ByteBuffer.allocate(iv.length + cipherText.length)
            .put(iv)
            .put(cipherText)
            .array();
}

public static byte[] encryptWithIV(byte[] pText, byte[] aesKey, byte[] iv) {
    try {
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        SecretKey secret = new SecretKeySpec(aesKey, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
        return cipher.doFinal(pText);
    } catch (Exception e) {
        throw new RuntimeException(e.toString());
    }
}

public static byte[] decryptWithPrefixIV(byte[] cText, byte[] aesKey) {
    ByteBuffer bb = ByteBuffer.wrap(cText);

    byte[] iv = new byte[IV_LENGTH_BYTE];
    bb.get(iv);
    // bb.get(iv, 0, iv.length);

    byte[] cipherText = new byte[bb.remaining()];
    bb.get(cipherText);

    return decryptWithIV(cipherText, aesKey, iv);
}

public static byte[] decryptWithIV(byte[] cText, byte[] aesKey, byte[] iv) {
    try {
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        SecretKey secret = new SecretKeySpec(aesKey, "AES");
        cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
        return cipher.doFinal(cText);
    } catch (Exception e) {
        throw new RuntimeException(e.toString());
    }
}

public static byte[] getRandomNonce(int numBytes) {
    byte[] nonce = new byte[numBytes];
    new SecureRandom().nextBytes(nonce);
    return nonce;
}
5uzkadbs

5uzkadbs1#

它将节省我们所有人的时间,提出了一个最小的运行代码,而不是碎片。。。
第一:main函数不完整,因为它没有为encrypt和decrypt函数提供aes密钥。
第二(也是主要原因):您将加密结果转换为字符串,但这必须失败,因为密码中的许多字节是不可打印的代码(这可能解释了为什么它有时——幸运地——工作)。将输出和相应的输入更改为base64编码字符串将有助于正确的工作程序:

Change
return new String(cipher, UTF);
to 
return Base64.getEncoder().encodeToString(cipher);

and 
byte[] plainText = decryptWithPrefixIV(cipher.getBytes(UTF), hexStringToByteArray(aesKey));
to
byte[] plainText = decryptWithPrefixIV(Base64.getDecoder().decode(cipher), hexStringToByteArray(aesKey));

输出为:

true

完整代码:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public class Org {

    private static final Charset UTF = StandardCharsets.UTF_8;
    private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
    private static final int TAG_LENGTH_BIT = 128;
    private static final int IV_LENGTH_BYTE = 12;

    public static void main(String[] args) {
        System.out.println("");

        String plainText = "This is something";
        String aesKey = "aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00aa00";
        String cipher = encrypt(plainText, aesKey);
        System.out.println(plainText.equals(decrypt(cipher, aesKey)));
    }

    private static String encrypt(String plainText, String aesKey) {
        byte[] cipher = encryptWithPrefixIV(plainText.getBytes(UTF),
                hexStringToByteArray(aesKey));
        return Base64.getEncoder().encodeToString(cipher);
        // ### do not convert a byte array to string !
        //return new String(cipher, UTF);
    }

    public static String decrypt(String cipher, String aesKey) {
        //byte[] plainText = decryptWithPrefixIV(cipher.getBytes(UTF), hexStringToByteArray(aesKey));
        byte[] plainText = decryptWithPrefixIV(Base64.getDecoder().decode(cipher), hexStringToByteArray(aesKey));
        return new String(plainText, UTF);
    }

    public static byte[] encryptWithPrefixIV(byte[] pText, byte[] aesKey) {
        byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
        byte[] cipherText = encryptWithIV(pText, aesKey, iv);

        return ByteBuffer.allocate(iv.length + cipherText.length)
                .put(iv)
                .put(cipherText)
                .array();
    }

    public static byte[] encryptWithIV(byte[] pText, byte[] aesKey, byte[] iv) {
        try {
            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
            SecretKey secret = new SecretKeySpec(aesKey, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
            return cipher.doFinal(pText);
        } catch (Exception e) {
            throw new RuntimeException(e.toString());
        }
    }

    public static byte[] decryptWithPrefixIV(byte[] cText, byte[] aesKey) {
        ByteBuffer bb = ByteBuffer.wrap(cText);

        byte[] iv = new byte[IV_LENGTH_BYTE];
        bb.get(iv);
        // bb.get(iv, 0, iv.length);

        byte[] cipherText = new byte[bb.remaining()];
        bb.get(cipherText);

        return decryptWithIV(cipherText, aesKey, iv);
    }

    public static byte[] decryptWithIV(byte[] cText, byte[] aesKey, byte[] iv) {
        try {
            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
            SecretKey secret = new SecretKeySpec(aesKey, "AES");
            cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
            return cipher.doFinal(cText);
        } catch (Exception e) {
            throw new RuntimeException(e.toString());
        }
    }

    public static byte[] getRandomNonce(int numBytes) {
        byte[] nonce = new byte[numBytes];
        new SecureRandom().nextBytes(nonce);
        return nonce;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}

相关问题