无法下载整个解密的txt文件

时间:2011-04-01 18:15:40

标签: java file-io iostream encryption

我正在写一个小测试应用程序。问题是...... Servlet将任何AES加密文件下载到我的桌​​面应用程序。然后我的桌面应用程序解密它并保存在本地硬盘上。它适用于二进制视频,图像等,但由于某种原因,我从txt文件中丢失了字符。我可以得到它,任何txt文件都缺少128个最终位(它是15或16个最终字符)。

我不知道为什么会这样,所以我需要你的建议

这是servlet代码:

final int BUFFER_SIZE = 4096;
FileInputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();
byte buffer[] = new byte[BUFFER_SIZE];

for (int nread = 0; (nread = in.read(buffer)) != -1;) {
    out.write(buffer, 0, nread);
}

out.flush();
out.close();
in.close();

桌面应用代码段:

response = httpclient.execute(httppost);//it is HttpClient 4...
resEntity = response.getEntity();
InputStream in = resEntity.getContent();
in = new CipherInputStream(in, decipher);//maybe the aes block missing here...
FileOutputStream out= new FileOutputStream(path);
byte[] buffer = new byte[4096];
int numRead = 0;

while ((count = in.read(buffer)) != -1) {
    out.write(buffer, 0, count);
}

out.flush();
out.close();

这就是我如何破译:

KeyGenerator kgen = KeyGenerator.getInstance("AES");
          kgen.init(128);
          key = kgen.generateKey();

    byte[] ivar = new byte[]
                      {
                          0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
                  };
AlgorithmParameterSpec params = new IvParameterSpec(ivar );
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key, params );

...这是桌面应用上传代码段

HttpPost httppost = null;
        HttpResponse response=null;
        HttpEntity resEntity=null;

        try {
          File file = filePath;
          fileLength=file.length();

          HttpClient httpclient = new DefaultHttpClient();
          httpclient.getParams().setParameter(CoreProtocolPNames.
                                              PROTOCOL_VERSION,
                                              HttpVersion.HTTP_1_1);


          String url="http://localhost:8080/testUrl";
          httppost = new HttpPost(url);

String name=file.getName();
InputStreamBody inputStreamBody=new InputStreamBody(new FileInputStream(file),name);

MultiPartEntity multiPartEntity = new MultiPartEntity();
multiPartEntity .addPart("file-name", new StringBody(name, Charset.forName("UTF-8")));
multiPartEntity .addPart("file-stream", inputStreamBody);

和InputStreamBody writeTo方法(你可以看到这里的文档http://hc.apache.org/httpcomponents-client-ga/httpmime/clover/org/apache/http/entity/mime/content/InputStreamBody.html)...

    byte[] ivar = new byte[]
                      {
                              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
                      };

    ...
    AlgorithmParameterSpec params = new IvParameterSpec(ivar);

    encipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    encipher .init(Cipher.ENCRYPT_MODE, key, params);

...

public void writeTo(final OutputStream out) throws IOException {
              if (out == null) {
                  throw new IllegalArgumentException("Output stream may not be null");
              }
              try {

              out = new CipherOutputStream(out, encipher );


              int numRead = 0;
              while ( (count = in.read(buf)) !=-1) {
                out.write(buf, 0, count);


              }
              out.flush();
              in.close();

            }
            catch (java.io.IOException e) {
            }
          }

请帮我解决问题。也许二进制文件也缺少128位,但除了文本文件内容之外它并不是那么引人注目:(我听说可能因为错误的序列流关闭等原因而发生,但我不确定。

我听说可能是InputStreamBody内容长度问题的方法:

public long getContentLength() {

             return -1;


         }

但是如果由于加密(参见writeTo方法)“in.length!= out.length”,如何正确设置修改后的输出长度?

请帮我解决这个问题

赞赏任何有用的评论:)

2 个答案:

答案 0 :(得分:1)

所以,我做了一个测试程序,看它是如何工作的(见下文)。

它会创建此文件:

O thou, my lovely boy, who in thy power
Dost hold Time's fickle glass, his sickle, hour;
Who hast by waning grown, and therein show'st
Thy lovers withering as thy sweet self grow'st;
If Nature, sovereign mistress over wrack,
As thou goest onwards, still will pluck thee back,
She keeps thee to this purpose, that her skill
May time disgrace and wretched minutes kill.
Yet fear her, O thou minion of her pleasure!
She may detain, but not still keep, her treasure:
Her audit, though delay'd, answer'd must be,
And her quietus is to render thee.

看起来很完整(diff表示它与输入相同。)

为什么你的程序似乎削减了一些东西,我不知道。

您在问题中输入的代码无法编译(需要进行一些修改,请参见下文),并且还使用随机密钥(来自密钥生成器)而不是用于加密示例文本文件的密钥。我在下面的示例中使用了一个固定密钥来获得可重复性,但是使用随机密钥它也会生成相同的解密文件(显然是另一个加密文件)。

在输入文件上运行该类,并将其加密输出与加密文件(服务器和客户端)进行比较。也许这有助于发现问题。

这里是示例代码。 main方法将三个文件名作为参数,第一个是原始文件(并且必须存在),第二个是加密文件,第三个是解密的(如果存在则都将被覆盖)。如果给出第四个参数,则使用随机密钥。

package de.fencing_game.paul.examples;

import java.io.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class EncryptDecrypt {


    AlgorithmParameterSpec params;

    public EncryptDecrypt()
        throws Exception
    {
        byte[] ivar = new byte[] {
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
        };
        params = new IvParameterSpec(ivar );
    }


    public void encrypt(SecretKey key, File from, File to)
        throws  Exception
    {
        Cipher ourCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ourCipher.init(Cipher.ENCRYPT_MODE, key, params );
        crypt(ourCipher, from, to);
    }

    public void decrypt(SecretKey key, File from, File to)
        throws Exception
    {
        Cipher ourCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ourCipher.init(Cipher.DECRYPT_MODE, key, params );
        crypt(ourCipher, from, to);
    }


    private void crypt(Cipher c, File from, File to) 
        throws IOException
    {
        InputStream in = new CipherInputStream(new FileInputStream(from), c);
        OutputStream out = new FileOutputStream(to);
        copyStream(in, out);
    }

    private void copyStream(InputStream in, OutputStream out)
        throws IOException
    {
        byte[] buffer = new byte[4096];
        int count = 0;

        while ((count = in.read(buffer)) != -1) {
            out.write(buffer, 0, count);
        }
        out.flush();
        out.close();
        in.close();
    }


    public static void main(String[] params)
        throws Exception
    {
        EncryptDecrypt ed = new EncryptDecrypt();

        if(params.length > 3) {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            SecretKey key = kgen.generateKey();
        }
        else {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("AES");
            // AES needs 128 bits = 16 bytes
            KeySpec spec =
                new SecretKeySpec("Test-KeyTest-Key".getBytes("US-ASCII"),
                                  "AES");
            System.out.println(spec);

            SecretKey key = factory.generateSecret(spec);
            System.out.println(key);
        }


        ed.encrypt(key, new File(params[0]), new File(params[1]));
        ed.decrypt(key, new File(params[1]), new File(params[2]));
    }

}

答案 1 :(得分:1)

您的servlet代码是否指定了内容长度?

缺少的尾随字节将是密码的最后一个块。最后的块总是需要特殊处理,因为它必须填充。填充使加密文件比原始文件大几个字节。如果你的servlet发送实际文件的内容长度,下载将被截断,最终被不正确地截断,输出缺少几个字节。

您正在尝试的二进制文件可能是密码块的精确倍数,因此不需要任何填充。