将字节数组转换为字符串并返回字节数组的问题

时间:2012-02-01 14:58:59

标签: java string encryption bytearray

这个主题有很多问题,同样的解决方案,但这对我不起作用。我有一个加密的简单测试。加密/解密本身是有效的(只要我使用字节数组本身而不是字符串处理此测试)。问题是不希望将它作为字节数组处理,而是作为String处理,但是当我将字节数组编码为字符串并返回时,生成的字节数组与原始字节数组不同,因此解密不再起作用。我在相应的字符串方法中尝试了以下参数:UTF-8,UTF8,UTF-16,UTF8。他们都没有工作。生成的字节数组与原始数组不同。任何想法为什么会这样?

加密器:

public class NewEncrypter
{
    private String algorithm = "DESede";
    private Key key = null;
    private Cipher cipher = null;

    public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException
    {
         key = KeyGenerator.getInstance(algorithm).generateKey();
         cipher = Cipher.getInstance(algorithm);
    }

    public byte[] encrypt(String input) throws Exception
    {
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] inputBytes = input.getBytes("UTF-16");

        return cipher.doFinal(inputBytes);
    }

    public String decrypt(byte[] encryptionBytes) throws Exception
    {
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] recoveredBytes = cipher.doFinal(encryptionBytes);
        String recovered = new String(recoveredBytes, "UTF-16");

        return recovered;
    }
}

这是我尝试的测试:

public class NewEncrypterTest
{
    @Test
    public void canEncryptAndDecrypt() throws Exception
    {
        String toEncrypt = "FOOBAR";

        NewEncrypter encrypter = new NewEncrypter();

        byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
        System.out.println("encryptedByteArray:" + encryptedByteArray);

        String decoded = new String(encryptedByteArray, "UTF-16");
        System.out.println("decoded:" + decoded);

        byte[] encoded = decoded.getBytes("UTF-16");
        System.out.println("encoded:" + encoded);

        String decryptedText = encrypter.decrypt(encoded); //Exception here
        System.out.println("decryptedText:" + decryptedText);

        assertEquals(toEncrypt, decryptedText);
    }
}

4 个答案:

答案 0 :(得分:93)

将加密数据存储在字符串中并不是一个好主意,因为它们是用于人类可读的文本,而不是用于任意二进制数据。对于二进制数据,最好使用byte[]

但是,如果您必须这样做,您应该使用在字节和字符之间具有 1对1映射的编码,即每个字节序列的位置可以映射到一个唯一的字符序列,然后返回。一种这样的编码是 ISO-8859-1 ,即:

    String decoded = new String(encryptedByteArray, "ISO-8859-1");
    System.out.println("decoded:" + decoded);

    byte[] encoded = decoded.getBytes("ISO-8859-1"); 
    System.out.println("encoded:" + java.util.Arrays.toString(encoded));

    String decryptedText = encrypter.decrypt(encoded);

其他不会丢失数据的常见编码是十六进制 base64 ,但遗憾的是您需要一个帮助程序库。标准API没有为它们定义类。

使用UTF-16,程序会失败有两个原因:

  1. String.getBytes(“UTF-16”)将一个字节顺序标记字符添加到输出中以标识字节的顺序。您应该使用UTF-16LE或UTF-16BE来实现此目的。
  2. 并非所有字节序列都可以映射到UTF-16中的字符。首先,以UTF-16编码的文本必须具有偶数个字节。其次,UTF-16有一种机制,用于编码U + FFFF之外的unicode字符。这意味着,例如有4个字节的序列只映射到一个unicode字符。为此,4的前2个字节不能编码UTF-16中的任何字符。

答案 1 :(得分:18)

如果您的String有一些非典型的加盟器,例如š, ž, ć, Ō, ō, Ū等,则接受的解决方案将无效。

以下代码很适合我。

byte[] myBytes = Something.getMyBytes();
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP);
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP);

答案 2 :(得分:5)

现在,我找到了另一种解决方案......

    public class NewEncrypterTest
    {
        @Test
        public void canEncryptAndDecrypt() throws Exception
        {
            String toEncrypt = "FOOBAR";

            NewEncrypter encrypter = new NewEncrypter();

            byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
            String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray));

            byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray());
            String decryptedText = encrypter.decrypt(byteArrayToDecrypt); 

            System.out.println("decryptedText:" + decryptedText);

            assertEquals(toEncrypt, decryptedText);
        }
    }

答案 3 :(得分:0)

您的问题是您无法从任意字节数组构建UTF-16(或任何其他编码)字符串(请参阅UTF-16 on Wikipedia)。但是,您需要对加密的字节数组进行序列化和反序列化而不会有任何损失,以便保留它,并在以后使用它。这是经过修改的客户端代码,可以让您深入了解字节数组实际发生的情况:

public static void main(String[] args) throws Exception {
  String toEncrypt = "FOOBAR";

  NewEncrypter encrypter = new NewEncrypter();

  byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
  System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray));

  String decoded = new String(encryptedByteArray, "UTF-16");
  System.out.println("decoded:" + decoded);

  byte[] encoded = decoded.getBytes("UTF-16");
  System.out.println("encoded:" + Arrays.toString(encoded));

  String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value!
  System.out.println("decryptedText:" + decryptedText);
}

这是输出:

encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decoded:<some garbage>
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decryptedText:FOOBAR

从原始decryptedText恢复时,encryptedByteArray是正确的。请注意,由于encoded转换期间数据丢失,encryptedByteArray值与byte[] -> String("UTF-16")->byte[]不同。