简体   繁体   English

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

[英]Decrypting a byte[] back to String without loss of data

I have written a small application to encrypt and decrypt Strings using AES. 我编写了一个小应用程序,用于使用AES加密和解密字符串。 Here is the code: 这是代码:

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);
   }
}

Output: 输出:

Initial Plain Text = test text 123
Encrypted Text     = [B@407dcb32
Decrypted Text     = test text 123

My main areas of concern are around encrypting into a byte array and decrypting back to a String. 我主要关心的领域是加密成字节数组并解密回字符串。 I know that this can introduce unexpected behaviour and loss of data. 我知道这会带来意外的行为和数据丢失。 While this has not been observed in my testing, could anyone suggest any changes that would help combat this? 尽管在我的测试中没有观察到这一点,但是有人可以提出任何有助于解决此问题的更改吗? I think I have this covered by ensuring UTF-8 is used both ways. 通过确保同时使用UTF-8可以解决这一问题。

If anyone see's any other red flags with my code and how I have done this, I'm open to criticism/suggestions. 如果有人看到我的代码还有其他危险信号,以及如何做到这一点,那么我很乐意提出批评/建议。

Many thanks! 非常感谢!

You're calling toString() on a byte[] which is never a good idea. 您在byte[]上调用toString()从来都不是一个好主意。 Basically it's not giving you any useful information. 基本上,它没有给您任何有用的信息。

If you want to convert arbitrary binary data into a string, I'd suggest using hex or base64, both of which are covered elsewhere. 如果要将任意二进制数据转换为字符串,建议使用hex或base64,这两种方法在其他地方都有介绍。 There's no indication that you've actually lost any information here in the encryption/decryption - the problem is your display of the encrypted data. 没有迹象表明您在加密/解密过程中实际上已经丢失了任何信息-问题在于您显示的是加密数据。 So long as you don't try to treat that as simple encoded text data (because it isn't) you should be fine. 只要您尝试将其视为简单的编码文本数据(因为不是),就可以了。 In particular, your code is already specifying UTF-8 as the conversion from the original text to unencrypted binary data, and vice versa - so that's safe. 特别是,您的代码已经将UTF-8指定为从原始文本到未加密的二进制数据的转换,反之亦然-因此,这是安全的。

If you don't need to convert the byte array to a string, it's simplest to avoid doing so in the first place. 如果您不需要将字节数组转换为字符串,则最简单的方法是避免这样做。 (For example, you could write it to a file still in the binary form very simply, then load it back into a byte array later.) (例如,您可以非常简单地将其写入二进制格式的文件中,然后稍后将其加载回字节数组中。)

You asked for other red flags, so I'll give you a few pointers regarding the crypto: 您还要求其他危险信号,所以我将给您一些有关加密货币的提示:

  1. Generally you don't have to provide the provider name when you use an algorithm name. 通常,使用算法名称时不必提供提供者名称。 Specifying the provider makes your code less portable. 指定提供程序会使代码的可移植性降低。

  2. It is better to use a standardized padding mode such as "/PKCS5Padding" (identical to PKCS#7 padding in Java). 最好使用标准化的填充模式,例如"/PKCS5Padding" (与Java中的PKCS#7填充相同)。 If you want to use the current padding mode you can configure the Bouncy Castle provider and specify "/ZeroBytePadding" . 如果要使用当前的填充模式,则可以配置Bouncy Castle提供程序并指定"/ZeroBytePadding" This padding mode does not work correctly for plaintext that ends with zero valued bytes. 对于以零值字节结尾的纯文本,此填充模式无法正常工作。

  3. You store the IV in the same class variable as the key. 您将IV存储在与键相同的类变量中。 I know this is just test code, but normally the IV need to be send or established at both sides. 我知道这只是测试代码,但通常需要在双方发送或建立IV。 The most common way to use the same key at both sides is to prefix the IV to the ciphertext. 在两侧使用相同密钥的最常见方法是在IV前面加上密文。

  4. The size of the IV depends on the cipher. IV的大小取决于密码。 It is always 16 for AES, but you may want to make the IV size configurable or use the Cipher.getBlockSize() method. AES始终为16,但是您可能希望使IV大小可配置或使用Cipher.getBlockSize()方法。

  5. Use GCM mode (available since 1.8) encryption if you also want authenticity/integrity and protection against padding oracle attacks. 如果您还想要真实性/完整性和防止填充oracle攻击的保护,请使用GCM模式(从1.8开始可用)加密。

  6. You should use a fresh, random IV for each encrypt, instead of generating an IV just once. 您应该为每个加密使用全新的随机IV,而不是一次生成IV。

the way to make sure the conversion is without loss is to use the same Charset when converting back and forth as you do. 确保转换没有损失的方法是在进行来回转换时使用相同的字符集。

Creating a string of the encrypted data is however not safe for further use; 但是,创建一个字符串的加密数据并不安全,无法进一步使用。 it can contain any and all sequences of bytes and might not fit into whatever Charset you originally used (you're not making this error, just pointing it out). 它可以包含所有字节序列,并且可能不适合您最初使用的任何字符集(您不会犯此错误,只是指出来)。

You're also printing the hashcode of the byte[] mid way in the code, not the individual bytes. 您还将在代码的中间打印byte []的哈希码,而不是单个字节。

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

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