安全 - 解密java“BadPaddingException”

时间:2014-04-07 06:59:47

标签: java security encryption passwords cryptography

我正在加密字符串并将它们写入文本文件。为了解密内容,我正在读取该文件并打印解密的数据。当我用一个字符串值测试我的代码时,它加密和解密完全正常;但是,当我添加更多字符串进行加密时,加密工作正常,但解密给了我这个异常“javax.crypto.BadPaddingException:给定最终块没有正确填充”

这是我的代码。请帮忙!

// these are initialized in main
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
            ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            dcipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
// catches ..

// it will take a string and the file that will have the encrypted strings
private static void encrypt(String s, OutputStream os) throws IllegalBlockSizeException, BadPaddingException {
        try {
            byte[] buf = s.getBytes();
            byte[] b = ecipher.doFinal(buf);
            os.write(b);
// this is to write a new line after writing each encrypted value and avoid overwriting
            os.write(System.getProperty("line.separator").getBytes()); 
            os.flush();
            os.close();
        }
    catch (IOException e) {
        System.out.println("I/O Error:" + e.getMessage());
    }
}
// this will take the file that has all of the encryptions
private static void decrypt(InputStream is) throws IllegalBlockSizeException, BadPaddingException {
    try {
        byte[] buf = new byte[is.available()];
        is.read(buf);
        byte[] decrypted = dcipher.doFinal(buf);  // THE CAUSE OF THE PROBLEM!!!!
        System.out.println(new String (decrypted));
        is.close();
    }
    catch (IOException e) {
        System.out.println("I/O Error:" + e.getMessage());
    }

2 个答案:

答案 0 :(得分:1)

加密时,您将加密数据写入输出文件,然后添加换行符,但在解密时,您似乎正在读取文件的全部内容并对其进行解密,这将包括换行符,它将包含换行符。尝试解密,好像它是密文的一部分,导致你的填充异常。您还尝试使用单个解密调用解密所有单独编写的字符串,而它们需要单独解密。

我建议在将写入输出文件并附加换行符之前将加密数据转换为Base64 。解密时,读取一行,从Base64转换回byte[]并解密,然后对输入中的每一行重复。

答案 1 :(得分:0)

在加密字节之后添加一个新行是多余的,应该删除。

问题的实际原因是使用InputStream.available()来获取数据的大小。此方法可以返回从0到输入流中的实际字节数的任何值,因此不适合确定输入数据的大小。在您的示例中,它返回一些值,并且您读取等于它的字节数,但很可能它不是所有加密数据,并且密码将无法正确解码。

通常,无法确定输入流的大小,因此您必须使用循环来读取流。解密代码应基于以下范例:

private static byte[] decrypt( Cipher dcipher, InputStream in, int BUFFER_SIZE ) throws Exception {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    byte[] inputBuffer = new byte[ BUFFER_SIZE ]; // 4K or 8K are pretty good buffer size values 
    int r = in.read( inputBuffer );
    while ( r >= 0 ) {
        byte[] outputUpdate = dcipher.update( inputBuffer, 0, r );
        out.write( outputUpdate );
        r = in.read( inputBuffer );
    }
    byte[] outputFinalUpdate = dcipher.doFinal();
    out.write( outputFinalUpdate );
    return out.toByteArray();
}
  • 如果您不知道尺寸,则必须阅读周期中的InputStream
  • 您永远不应该忽略InputStream.read(byte[])
  • 的返回值
  • 如果你想一次性获取解密数据,ByteArrayOutputStream可以作为获取可调整大小的字节数组的最简单方法。

您还可以首先使用与上述周期相似的代码读取整个加密数据,但不使用中间Cipher.update(),然后使用单Cipher.doFinal()次调用一次性解码。

private static byte[] decrypt( Cipher dcipher, InputStream in, int BUFFER_SIZE ) throws Exception {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    byte[] inputBuffer = new byte[ BUFFER_SIZE ]; // 4K or 8K are pretty good buffer size values
    int r = in.read( inputBuffer );
    while ( r >= 0 ) {
        out.write( inputBuffer, 0, r );
        r = in.read( inputBuffer );
    }
    return dcipher.doFinal( out.toByteArray() );
}