将byte []解密回String而不丢失数据

Mea*_*ell 1 java security encryption cryptography

我编写了一个小应用程序来使用AES加密和解密字符串.这是代码:

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class AesEncryptionTest {
    static IvParameterSpec initialisationVector = generateInitialisationVector();
    static SecretKey encryptionKey = generateKey();
    static String plainText = "test text 123\0\0\0";

    public static void main(String [] args) {
        try {
            System.out.println("Initial Plain Text = " + plainText);

            byte[] encryptedText = encrypt(plainText, encryptionKey);
            System.out.println("Encrypted Text     = " + encryptedText);

            String decryptedText = decrypt(encryptedText, encryptionKey);
            System.out.println("Decrypted Text     = " + decryptedText);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static byte[] encrypt(String plainText, SecretKey encryptionKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
        cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, initialisationVector);
        return cipher.doFinal(plainText.getBytes("UTF-8"));
    }

    public static String decrypt(byte[] encryptedText, SecretKey encryptionKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
        cipher.init(Cipher.DECRYPT_MODE, encryptionKey, initialisationVector);
        return new String(cipher.doFinal(encryptedText),"UTF-8");
    }

    public static SecretKey generateKey() {
        SecretKey secretKey = null;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(128);
            secretKey = keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException ex) {
           // Whine a little
        }
        return secretKey;
    }

    public static IvParameterSpec generateInitialisationVector() {
        byte[] initVector = new byte[16];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(initVector);

        return new IvParameterSpec(initVector);
   }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Initial Plain Text = test text 123
Encrypted Text     = [B@407dcb32
Decrypted Text     = test text 123
Run Code Online (Sandbox Code Playgroud)

我主要关注的是加密到字节数组并解密回String.我知道这会引入意外行为和数据丢失.虽然在我的测试中没有观察到这种情况,但有人可以建议任何有助于解决此问题的变化吗?我认为通过确保UTF-8双向使用来实现这一目标.

如果有人看到我的代码中的任何其他红旗以及我是如何做到这一点的,我会接受批评/建议.

非常感谢!

Jon*_*eet 5

你在呼唤toString()一个byte[]永远不是个好主意的东西.基本上它没有给你任何有用的信息.

如果你想将任意二进制数据转换成字符串,我建议使用hex或base64,这两者都在其他地方有所涉及.没有迹象表明您在加密/解密中实际上丢失了任何信息 - 问题在于您显示加密数据.只要您尝试将其视为简单的编码文本数据(因为它不是),您应该没问题.特别是,您的代码已经指定UTF-8作为从原始文本到未加密二进制数据的转换,反之亦然 - 这样安全.

如果您不需要将字节数组转换为字符串,那么最简单的方法是首先避免这样做.(例如,您可以非常简单地将其写入仍然以二进制形式存在的文件中,然后将其重新加载到字节数组中.)