Java – 为什么我的AES程序不能加密/解密双引号?

huangapple go评论75阅读模式
英文:

Java - Why is my AES program not encrypting/decrypting double quotes?

问题

这里是翻译好的内容:

我正在使用Java的加密库实现一个简单的AES-128加密程序。

不幸的是,这并不总是有效。有时候会发生这样的情况,明文字符串中的双引号(")在加密和解密时会显示为解密后字符串中的�。

根据我所了解,这个字符是&#xfffd(�的Unicode编码),是UTF-8解码器在遇到错误时使用的替代字符

因此,我的问题是为什么会出现这个错误,为什么只有有时会出现,以及如何解决它?

这是我的加密和解密代码块:

public static String encrypt(String value)
{
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

        byte[] encrypted = cipher.doFinal(value.getBytes());
        return Base64.getEncoder().encodeToString(encrypted);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

public static String decrypt(String encrypted)
{
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));

        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return null;
}

以下是一些示例情况:

情况1:引发错误

输入明文:had no pictures or conversations in it,“and what is the use of a book,” thought Alice “without pictures or conversations?”
加密字符串:UgapiW5aYIKkoKkyeHvvFlgf8mCIq1AopmCtYGiJ23eNILNn1OXtM4enEvB5Kt1imNmynyHCCjLbbjB7TV0sq2F3Iz+YUehOw50gje6IMj8fpaEracq1NvZQXSH5T8fyBtAUH3weU5FE5vr3dYmwTSGUxNR2gaRV6MV7vmcEuIz7A5MLnKjsb2+1Sya+l/k2
输入密文:UgapiW5aYIKkoKkyeHvvFlgf8mCIq1AopmCtYGiJ23eNILNn1OXtM4enEvB5Kt1imNmynyHCCjLbbjB7TV0sq2F3Iz+YUehOw50gje6IMj8fpaEracq1NvZQXSH5T8fyBtAUH3weU5FE5vr3dYmwTSGUxNR2gaRV6MV7vmcEuIz7A5MLnKjsb2+1Sya+l/k2
解密后:had no pictures or conversations in it,�and what is the use of a book,� thought Alice �without pictures or conversations?�

情况2:没有问题

输入明文:Hello there,“Camera-man”。He's sitting now.
加密字符串:jb2QJ5nLQCjGKw6l2q9GnX6jgTJVGWn6LiVRfE5oRT7WT7vYNejKPHIhgorbfaob
输入密文:jb2QJ5nLQCjGKw6l2q9GnX6jgTJVGWn6LiVRfE5oRT7WT7vYNejKPHIhgorbfaob
解密后:Hello there,“Camera-man”。He's sitting now。

英文:

I'm implementing a simple AES-128 encryption program using Java's Crypto library.

Unfortunately, this doesn't work very well all the time. Sometimes it happens that the double-quotes (") in the plaintext String are when encrypted and then decrypted, show up as � in the decrypted string.

From what I found out, this character is &#xfffd (&#65533), which is a replacement character used by the UTF-8 decoder when it encounters an error.

Thus, my question is why does this error occur, why only sometimes and how do I resolve it?

Here's my code block for encryption and decryption:

public static String encrypt(String value)
    {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(value.getBytes());
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String encrypted)
    {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

Here are some examples cases:

Case 1: Error caused

Enter Plaintext: had no pictures or conversations in it, “and what is the use of a book,” thought Alice “without pictures or conversations?”
Encrypted String: UgapiW5aYIKkoKkyeHvvFlgf8mCIq1AopmCtYGiJ23eNILNn1OXtM4enEvB5Kt1imNmynyHCCjLbbjB7TV0sq2F3Iz+YUehOw50gje6IMj8fpaEracq1NvZQXSH5T8fyBtAUH3weU5FE5vr3dYmwTSGUxNR2gaRV6MV7vmcEuIz7A5MLnKjsb2+1Sya+l/k2
Enter Ciphertext: UgapiW5aYIKkoKkyeHvvFlgf8mCIq1AopmCtYGiJ23eNILNn1OXtM4enEvB5Kt1imNmynyHCCjLbbjB7TV0sq2F3Iz+YUehOw50gje6IMj8fpaEracq1NvZQXSH5T8fyBtAUH3weU5FE5vr3dYmwTSGUxNR2gaRV6MV7vmcEuIz7A5MLnKjsb2+1Sya+l/k2
After decryption: had no pictures or conversations in it, �and what is the use of a book,� thought Alice �without pictures or conversations?�

Case 2: No issue

Enter Plaintext: Hello there, "Camera-man". He's sitting now. 
Encrypted String: jb2QJ5nLQCjGKw6l2q9GnX6jgTJVGWn6LiVRfE5oRT7WT7vYNejKPHIhgorbfaob
Enter Ciphertext: jb2QJ5nLQCjGKw6l2q9GnX6jgTJVGWn6LiVRfE5oRT7WT7vYNejKPHIhgorbfaob
After decryption: Hello there, "Camera-man". He's sitting now. 

答案1

得分: 2

我相信加密货币方面的内容在这里与问题无关;相反,问题必定出现在 String.getBytes() 和 new String(byte[]) 的往返过程中。您的“案例1:引发错误”的情况涉及非 ASCII 弯引号(而您的“案例2:无问题”使用普通 ASCII 引号),因此显然在您的系统上,String.getBytes() 和 new String(byte[]) 并不能很好地处理该字符。(这些方法的文档中使用“平台的默认字符集”,显然您的平台默认的字符集不支持该字符。)

要解决这个问题,我认为您只需要从 String.getBytes() 切换到 String.getBytes(Charset),并且从 new String(byte[]) 切换到 new String(byte[], Charset),在两种情况下都使用 StandardCharsets.UTF_8 作为字符集。(或者使用其他适当的字符集,但是 UTF-8 是现在最常见的选择。)

所以:

            byte[] encrypted = cipher.doFinal(
                value.getBytes(StandardCharsets.UTF_8));

            return new String(original, StandardCharsets.UTF_8);
英文:

I'm confident that the crypto stuff isn't relevant here; rather, the problem must be in the round trip of String.getBytes() and new String(byte[]). Your "Case 1: Error caused" involves non-ASCII curly quotes (whereas your "Case 2: No issue" uses regular ASCII quotes), so apparently String.getBytes() and new String(byte[]) on your system don't handle that character very well. (Those methods are documented as using "the platform's default charset", and apparently your platform's default charset is one that doesn't support that character.)

To fix this, I think all you need to do is switch from String.getBytes() to String.getBytes(Charset) and from new String(byte[]) to new String(byte[], Charset), using StandardCharsets.UTF_8 as the charset in both cases. (Or any other appropriate charset, but UTF-8 is the most common choice nowadays.)

So:

            byte[] encrypted = cipher.doFinal(
                value.getBytes(StandardCharsets.UTF_8));

and

            return new String(original, StandardCharsets.UTF_8);

答案2

得分: 2

你粘贴中的引号不是标准的ASCII ",而是其他字符。

你使用基于字符串的密钥和iv,并使用UTF-8将它们转换为实际的字节数组。这可能是一个错误,会稍微减少随机性,但不足以引起太多担忧。

然而,对于实际的有效载荷,你没有这样做 - 而这正是你应该做的。

不是 value.getBytes(),而是 value.getBytes(StandardCharsets.UTF-8),而不是 new String(original),而是 new String(original, StandardCharsets.UTF_8)

英文:

The quote in your paste is not the standard ASCII ", but something else.

You have string-based keys and ivs and use UTF-8 to turn those into actual byte arrays. This is probably a mistake, you're reducing randomness a bit there, but not enough to be too worried about.

However, for the actual payload, you don't do this - and that's where you should have.

It's not value.getBytes(), it's value.getBytes(StandardCharsets.UTF-8), and not new String(original), but new String(original, StandardCharsets.UTF_8).

huangapple
  • 本文由 发表于 2020年10月8日 12:51:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/64255982.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定