2012-10-09 55 views
13

其實,我搜索很多從互聯網和計算器也爲這個,最後一塊殘缺與CipherInputStream/CipherOutputStream,即使填充AES/CBC/PKCS5Padding

起初我並不在我的加密和解密使用的填充,

但最後,我從這裏

https://stackoverflow.com/a/10775577/1115788

瞭解決方案,我更新了我的代碼填充的AES/CBC/PKCS5Padding 和相同的錯誤是COMI NG,和最後一個塊不解密...

我正在爲這最後的兩天,但沒有解決發現

我Crypter代碼:

package mani.droid.browsedropbox; 

import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.math.BigInteger; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import javax.crypto.Cipher; 
import javax.crypto.CipherInputStream; 
import javax.crypto.CipherOutputStream; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

public class Crypter { 

    Cipher encipher; 
    Cipher decipher; 
CipherInputStream cis; 
CipherOutputStream cos; 
FileInputStream fis; 
byte[] ivbytes = new byte[]{(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e',                       (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',  (byte)'n', (byte)'o', (byte)'p'}; 
    IvParameterSpec iv = new IvParameterSpec(ivbytes); 

public boolean enCrypt(String key, InputStream is, OutputStream os) 
{ 
    try { 
     byte[] encoded = new BigInteger(key, 16).toByteArray(); 
     SecretKey seckey = new SecretKeySpec(encoded, "AES"); 
     encipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
     encipher.init(Cipher.ENCRYPT_MODE, seckey, iv); 
     cis = new CipherInputStream(is, encipher); 
     copyByte(cis, os); 
     return true; 
    } 
    catch (InvalidKeyException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    return false; 
} 

public boolean deCrypt(String key, InputStream is, OutputStream os) 
{ 
    try { 
     byte[] encoded = new BigInteger(key, 16).toByteArray(); 
     SecretKey seckey = new SecretKeySpec(encoded, "AES"); 
     encipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
     encipher.init(Cipher.DECRYPT_MODE, seckey, iv); 
     cos = new CipherOutputStream(os, encipher); 
     copyByte(is, cos); 
     //cos.close(); 
     return true; 
    } 
    catch (InvalidKeyException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    return false; 
} 

public void copyByte(InputStream is, OutputStream os) throws IOException 
{ 
    byte[] buf = new byte[8192]; 
    int numbytes; 
    while((numbytes = is.read(buf)) != -1) 
    { 
     os.write(buf, 0, numbytes); 
     os.flush(); 
    } 
    os.close(); 
    is.close(); 
} 
} 
+0

是密文的休息好嗎?密碼輸入流具有在地毯下方洗牌異常的令人厭惡的習慣,包括'BadPaddingException'的實例。 –

+0

檢查您正在寫入的文件的大小(?),並確保您正確地寫入所有內容。 –

+0

@owlstead密碼的其餘部分完全解密,但最後一個塊丟失,檢查我在'copyByte'中打印'numbytes'的代碼,結果是加密時,所有塊都被讀取,即使是最後一個不完整的16位塊,但是對於解密,最後一個塊的值不是16,小於16 ...,所以我的猜測是,它用null或0填充,所以它在加密時讀取完整塊,所以加密文件大小與我的原始文件大小不是16bytes的倍數... – Mani

回答

7

最後我得到了回答我自己的問題,與試錯 其實這裏衝突是我設置填充在encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

和集合Ⅳ一些價值觀.....,

最後我得到了答案纔剛剛更換了算法

來源:

AES/CBC/PKCS7Paddinng

要:

AES/CFB8/NoPadding

和它的工作就像魅力....,所以我建議這個答案爲其他人誰努力解決這個問題,如果你解決了你的問題,在這裏提到其他人...

+0

你明白爲什麼嗎?或者有一個解釋的鏈接? –

+0

我也面臨這個問題,我需要鏈接瞭解,這個問題是如何解決的。請給我鏈接? – Karthick

+0

某些算法(如DESeade或AES)需要輸入數據才能具有良好的大小,這通常是Cipher.getBlockSize()的倍數。您可以選擇在algortihm(「PKCS7Paddinng」)中使用填充還是不填充填充,並且由您來確定輸入數據的尺寸是否合適。如果不是,你會得到這個例外。 –

11

我有完全相同的問題。 接受的解決方案的工作原理是因爲您使用了不需要填充的密碼模式,但這不是解決密碼相關問題的方法。

根據CipherOutputStream documentation,您必須調用close()方法才能正確完成加密(即添加了填充塊)。

此方法調用封裝的密碼 對象,這會導致由封裝的密碼緩衝至 任何字節進行處理的方法doFinal。通過調用此輸出流的沖洗方法 來寫出結果。

此方法將封裝的密碼對象重置爲其初始狀態 並調用基礎輸出流的close方法。

如果要在調用CipherOutputStream.close()方法後保留OutputStream打開狀態,則可以將OutputStream封裝到未關閉OutputStream的流中。例如:

public class NotClosingOutputStream extends OutputStream { 
    private final OutputStream os; 

    public NotClosingOutputStream(OutputStream os) { 
    this.os = os; 
    } 

    @Override 
    public void write(int b) throws IOException { 
    os.write(b); 
    } 

    @Override 
    public void close() throws IOException { 
    // not closing the stream. 
    } 

    @Override 
    public void flush() throws IOException { 
    os.flush(); 
    } 

    @Override 
    public void write(byte[] buffer, int offset, int count) throws IOException { 
    os.write(buffer, offset, count); 
    } 

    @Override 
    public void write(byte[] buffer) throws IOException { 
    os.write(buffer); 
    } 
} 

然後你可以使用:

... 
cos = new CipherOutputStream(new NotClosingOutputStream(os), encipher); 
copyByte(is, cos); 
cos.close(); 
... 

注意os流沒有得到封閉的,你需要做的是你自己在適當的時候。

+1

這應該是未來讀者接受的問題的答案。 – Robert

0

我見過CipherInputStream失敗,並帶有填充問題。這種行爲在不同版本的JVM之間有所不同。例如7u55 32位我的代碼工作正常,7u55 64位相同的代碼失敗...並且我還在後面的32位JVM上看到故障。解決方法是使用字節數組方法並避免CipherInputStream。

0

不確定這是否與OP的問題有關,但這可能有助於某人。

當你反覆拿到java.io.IOException: last block incomplete in decryption無論你改變什麼,檢查,如果你還在使用從以前的一些運行該文件。如果您的讀/寫測試代碼附加到該文件,您將永遠得到該例外 - 除非您刪除了您寫入的損壞文件。

0

使用帶填充的CipherInputStream是可能的,切換到NoPadding是一種解決方法,但不是解決方案。

當CipherInputStream到達流尾時應用填充。重要的一點是,您必須至少調用CipherInputStream的read()方法兩次才能獲取所有數據。

下面的例子演示如何讀取與填充一個CipherInputStream:

public static void test() throws Exception { 
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

    SecureRandom rnd = new SecureRandom(); 
    byte[] keyData = new byte[16]; 
    byte[] iv = new byte[16]; 
    rnd.nextBytes(keyData); 
    rnd.nextBytes(iv); 
    SecretKeySpec key = new SecretKeySpec(keyData, "AES"); 

    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); 

    ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
    CipherOutputStream out = new CipherOutputStream(buffer, cipher); 

    byte[] plain = "Test1234567890_ABCDEFG".getBytes(); 
    out.write(plain); 
    out.flush(); 
    out.close(); 
    byte[] encrypted = buffer.toByteArray(); 
    System.out.println("Plaintext length: " + plain.length); 
    System.out.println("Padding length : " + (cipher.getBlockSize() - (plain.length % cipher.getBlockSize()))); 
    System.out.println("Cipher length : " + encrypted.length); 

    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); 
    CipherInputStream in = new CipherInputStream(new ByteArrayInputStream(encrypted), cipher); 
    buffer = new ByteArrayOutputStream(); 
    byte[] b = new byte[100]; 
    int read; 
    while ((read = in.read(b)) >= 0) { 
     buffer.write(b, 0, read); 
    } 
    in.close(); 

    // prints Test1234567890_ABCDEFG 
    System.out.println(new String(buffer.toByteArray())); 
}