Reputation: 2086
I have created an InputStream that does File -> (MD5) -> Zip -> Encrypt -> (MD5) and a OutputStream that should reverse this action. But most files fails the test (see Main). Try a file with content "-abcdefghiz-abcdefghijklmnopqrstuvxyz". Others have described similar problems, but they had forgot to close the stream. If i comment out the Cipher or the Deflater/Inflater it works fine.
import java.io.*;
import java.security.*;
import java.math.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.spec.*;
import java.util.*;
import java.util.zip.*;
class InputStreamWithZipMd5Aes extends InputStream
{
InputStream is;
MessageDigest md = MessageDigest.getInstance("MD5");
MessageDigest mdEncrytped = MessageDigest.getInstance("MD5");
public InputStreamWithZipMd5Aes(InputStream innerInputStream, String password) throws Exception
{
if (password==null)
throw new IllegalArgumentException("password");
byte[] key = password.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[]
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
ecipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
//File -> MD5 -> Zip -> Encrypt -> MD5
is = new DigestInputStream(innerInputStream, md);
is = new DeflaterInputStream(is);
is = new CipherInputStream(is, ecipher);
is = new DigestInputStream(is, mdEncrytped);
}
public int read() throws IOException
{
return is.read();
}
public String getMd5()
{
BigInteger bi = new BigInteger(1, md.digest());
return String.format("%1$032X", bi);
}
public String getMd5encrypted()
{
BigInteger bi = new BigInteger(1, mdEncrytped.digest());
return String.format("%1$032X", bi);
}
}
class OutputStreamWithZipMd5Aes extends OutputStream
{
Cipher chiper;
OutputStream os;
MessageDigest md = MessageDigest.getInstance("MD5");
public OutputStreamWithZipMd5Aes(OutputStream innerOutpuStream, String password) throws Exception
{
if (password==null)
throw new IllegalArgumentException("password");
byte[] key = password.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
chiper = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[]
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
chiper.init(Cipher.DECRYPT_MODE, secretKeySpec, paramSpec);
// Decrypt -> Unzip -> MD5 -> File
os = new DigestOutputStream(innerOutpuStream, md);
os = new InflaterOutputStream(os);
os = new CipherOutputStream(os, chiper);
}
public void write(int n) throws IOException
{
os.write(n);
}
public String getMd5()
{
BigInteger bi = new BigInteger(1, md.digest());
return String.format("%1$032X", bi);
}
private static void verifyEncryptDecrypt(File file) throws Exception
{
InputStream inClean = new BufferedInputStream(new FileInputStream(file));
ByteArrayOutputStream bytesClean = new ByteArrayOutputStream();
InputStream in = new BufferedInputStream(new FileInputStream(file));
in = new InputStreamWithZipMd5Aes(in, "password");
ByteArrayOutputStream bytesProcessed = new ByteArrayOutputStream();
OutputStreamWithZipMd5Aes os = new OutputStreamWithZipMd5Aes(bytesProcessed, "password");
byte[] buffer = new byte[1024*1024];
while (true)
{
int read = in.read(buffer);
if (read == -1)
break;
os.write(buffer, 0, read);
}
while (true)
{
int read = inClean.read(buffer);
if (read == -1)
break;
bytesClean.write(buffer, 0, read);
}
os.close();
bytesClean.close();
System.out.println("#1 " + bytesClean.size() + " : " + bytesClean.toString());
System.out.println("#2 " + bytesProcessed.size() + " : " + bytesProcessed.toString());
System.out.println(((InputStreamWithZipMd5Aes)in).getMd5() + " == " + ((OutputStreamWithZipMd5Aes)os).getMd5());
}
public static void main(String[] a) throws Exception
{
File file1 = new File("c:\\test.html");
verifyEncryptDecrypt(file1);
}
}
Outputs with file "-abcdefghiz-abcdefghijklmnopqrstuvxyz"
#1 37 : -abcdefghiz-abcdefghijklmnopqrstuvxyz
#2 36 : -abcdefghiz-abcdefghijklmnopqrstuvxy
7F0F45E7EC19AE5F25B6E3F6874B0469 == 39A41A6C429A9010A70C8EA62650F1CD
Upvotes: 0
Views: 250
Reputation: 86774
You are closing YOUR extension of OutputStream
but not closing the actual output stream which is contained within OutputStreamWithZipMd5Aes
. You must close the contained output stream.
class OutputStreamWithZipMd5Aes extends OutputStream
{
...
OutputStream os;
...
@Override
public void close() throws IOException
{
os.close();
}
...
}
In fact, there's no reason your class OutputStreamWithZipMd5Aes
has to extend OutputStream
since you never use the stream you inherit (you never refer to this
).
This works just as well:
class OutputStreamWithZipMd5Aes
{
...
OutputStream os;
...
public void write(byte[] buffer, int pos, int n) throws IOException
{
os.write(buffer,pos,n);
}
public void close() throws IOException
{
os.close();
}
...
}
Upvotes: 2