有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java AES使用相同的IV进行加密和解密

我的代码有一些问题,我没有使用相同的IV进行加密和解密。我知道,为了正确地执行此操作,我必须在输入数据之前将IV写入我的输出文件,但是我正在努力实现这一点。有人能帮我解决这个问题吗

再次编辑代码以显示全部范围

public class TestFileEncryption {
    private static void mainCrypto(int cipherMode, File inputFile, File outputFile) throws Exception{
        //Let the user enter the key they wish to use
        Key secretKey = new SecretKeySpec(UITest.getStoreKey().getBytes(), UITest.getSendAlg()); //Generates a key based on the default keysize for the specified algorithm

        //Generate an Initialization Vector (IV)
        final int ALG_KEYLENGTH = UITest.getStoreKey().length(); //Change this as desired for the security level you want
        byte[] iv = new byte[ALG_KEYLENGTH]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
        SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
        prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method

        //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
        Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode

        //Initialize the Cipher for Encryption
        cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));

        //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
        FileInputStream inputStream = new FileInputStream(inputFile);
        byte[] inputBytes = new byte[(int) inputFile.length() - ALG_KEYLENGTH];
        inputStream.read(iv);
        inputStream.read(inputBytes);
        byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        outputStream.write(iv);
        outputStream.write(outputBytes);
        inputStream.close();
        outputStream.close();
    }

    public static void encrypt(File inputFile, File outputFile) throws Exception {
        mainCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
    }

    public static void decrypt(File inputFile, File outputFile) throws Exception {
        mainCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
    }

    public static void main(String[] args) {}
}

共 (2) 个答案

  1. # 1 楼答案

    只是扩展了@Javier的答案

    看起来您希望使用相同的方法进行加密和解密(取决于模式),但是在处理IV时存在差异

    您生成了一个随机IV,然后使用(普通)输入的输入对其进行重写,最后将其写入输出(不管它是如何解密的)

    因此,您必须区分模式是否为

    • 加密-IV在密文之前生成并写入输出
    • 解密-IV从输入读取并用于解密,但不写入输出

    诸如此类:

    private void encrypt(File inputFile, File outputFile)  {
        //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
        FileInputStream inputStream = new FileInputStream(inputFile);
        byte[] inputBytes = new byte[(int) inputFile.length()];
        byte[] iv = new byte[16]; // 16 for AES-CBC
        SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
        prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
    
        //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
        Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
    
        //Initialize the Cipher for Encryption
        cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));      
            inputStream.read(inputBytes);
            byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(iv);
            outputStream.write(outputBytes);
            outputStream.flush();
            inputStream.close();
            outputStream.close();
        }
    }
    
    private void decrypt(File inputFile, File outputFile) {
        //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
        FileInputStream inputStream = new FileInputStream(inputFile);
        byte[] inputBytes = new byte[(int) inputFile.length()-16];
        byte[] iv = new byte[16]; // 16 for AES-CBC
    
        //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
        Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
    
        //Initialize the Cipher for Encryption
        cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));      
        inputStream.read(iv);
        inputStream.read(inputBytes);
        byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        outputStream.write(outputBytes);
        outputStream.flush();
        inputStream.close();
        outputStream.close();
    }
    

    为了省去一些细节,也许您可以直接使用Java CipherOutputStream和CipherInputStream,实现将为您处理这些细节(如果您不关心确切的格式)

    接下来您缺少的是一个身份验证标记,至少是确保密文完整性的明文散列。(称为身份验证加密)

  2. # 2 楼答案

    你只需要在密文之前写IV:

    outputStream.write(iv);
    outputStream.write(outputBytes);
    

    然后,解密时,读取IV和密文:

    byte[] iv = new byte[ALG_BLOCKSIZE];
    byte[] inputBytes = new byte[(int) inputFile.length() - ALG_BLOCKSIZE];
    inputStream.read(iv);
    inputStream.read(inputBytes);
    

    这里ALG_BLOCKSIZE对于AES-CBC需要为16