Cipher class doesn't work with FileOutputStream

I am creating an application where I would like to use the CiphperOutputStream with RSA encryption. I created an RSAOutputStream class which will encrypt data as it writes out.

publicclass RSAOutputStream{

private CipherOutputStream cipherOutputStream;

private Cipher cipher;

public RSAOutputStream(RSAKey key, OutputStream output)throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException{

cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.ENCRYPT_MODE, key.getPublicKey());

cipherOutputStream =new CipherOutputStream(output,cipher);

}

publicvoid closeOutputStream()throws IOException{

cipherOutputStream.close();

}

public Cipher getCipher(){

return cipher;

}

publicvoid write(byte[] out)throws IOException{

cipherOutputStream.write(out);

}

}

However, the write doesn't seem to be working at all. The resulting output file is always zero at the end of the wrinting? Can someone tell me if I have this set up wrong?

[1898 byte] By [JNooreza] at [2007-11-27 3:30:21]
# 1
have you closed the output?
ejpa at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 2
Yes, I close the stream at the end of the output.
JNooreza at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 3

RSA is not normally used with CipherOutputStream because RSA should only be used to encrypt small amounts of data - typically of length less than the modulus but even less if PKCS1 padding is used.

It seem that if, using your code, you encrypt less than the modulus length of bytes then you will get an a file output of length of the modulus. If you try to encrypt more than the modulus length of bytes that the CipherOutputStream throws and swallows an exception so it writes nothing.

It is normal to use a hybrid approach with RSA. One normally just uses RSA to encrypt a random symmetric algorithm session key and then encrypts the real data with the symmetric algorithm. For details of the 'best' approach to doing this see section 13.6 of 'Practical Cryptography' by Ferguson and Schneier published by Wiley - ISBN 0-471-22357-3 .

sabre150a at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 4

Ok, I changed it so that it would use AES encryption instead of RSA for encrypting files. Although, when I decrypt a file, there appears to be data lost. When I read and write data I use a byte array of size 512 and use these methods for all streams:

read(byte[],int,int) andwrite(byte[],int,int). Is this the correct way to do this?

JNooreza at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 5

> Ok, I changed it so that it would use AES encryption

> instead of RSA for encrypting files. Although, when I

> decrypt a file, there appears to be data lost. When I

> read and write data I use a byte array of size 512

> and use these methods for all streams:

>

> read(byte[],int,int) andwrite(byte[],int,int). Is

> this the correct way to do this?

How can we tell in isolation? Why have you changed from using the Cipher streams?

sabre150a at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 6

Sorry for the lack of information: Here is the new version of my OutputStream and InputStream

public class AESOutputStream {

private OutputStream cipherOutputStream;

private Cipher cipher;

public AESOutputStream(AESKey key, OutputStream output) throws IOException,

NoSuchPaddingException, NoSuchAlgorithmException,

InvalidKeyException {

cipher = Cipher.getInstance("AES");

cipher.init(Cipher.ENCRYPT_MODE, key.getKey());

cipherOutputStream = new CipherOutputStream(output, cipher);

}

public void close() throws IOException {

cipherOutputStream.close();

}

public void finalWrite() throws IOException, BadPaddingException, IllegalBlockSizeException {

byte[] done = cipher.doFinal();

write(done);

}

public Cipher getCipher() {

return cipher;

}

public void write(byte[] out) throws IOException {

cipherOutputStream.write(out);

}

public void write(byte[] out, int off, int len) throws IOException {

cipherOutputStream.write(out,off,len);

}

public void write(int i) throws IOException {

cipherOutputStream.write(i);

}

}

public class AESInputStream extends InputStream {

private CipherInputStream cipherInputStream;

private Cipher cipher;

public AESInputStream(AESKey key, InputStream input) throws IOException,

NoSuchPaddingException, NoSuchAlgorithmException,

InvalidKeyException {

cipher = Cipher.getInstance("AES");

cipher.init(Cipher.DECRYPT_MODE, key.getKey());

cipherInputStream = new CipherInputStream(input, cipher);

}

public void close() throws IOException {

cipherInputStream.close();

}

public Cipher getCipher() {

return cipher;

}

public int read(byte[] b, int off, int len) throws IOException {

int total = cipherInputStream.read(b, off, len);

return total;

}

public int read(byte[] in) throws IOException {

int i = cipherInputStream.read(in);

return i;

}

public int read() throws IOException {

int input;

input = cipherInputStream.read();

return input;

}

public byte[] finalRead() throws BadPaddingException, IllegalBlockSizeException {

byte[] done = cipher.doFinal();

return done;

}

}

I first use the AESOutputStream to write out encrypted data and then use the AESInputStream to decrypt it. However, the decrypted data has some data loss when I check it. Have I set up the streams correctly?

JNooreza at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 7

I haven't checked all the code but you should not call doFinal() on a Cipher when using the CipherOutputStream. Just close() the stream.

You seem to be making hard work of this. Why not just use CipherOutputStream and CipherInputStrean as streams without wrapping them in your own class? Your two classes add no value. Also, why are you wrapping the key in your own AESKey class?

sabre150a at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 8

I ended up writing my own little class to accomplish what I think we're trying to accomplish here. It's been tested and works under Linux and 1.5. Hope this helps some:package javaforum;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

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.spec.SecretKeySpec;

public class AESStreams {

private SecretKeySpec spec;

public AESStreams(SecretKeySpec inSpec) {

spec = inSpec;

}

public BufferedInputStream getInput(InputStream inStrm)

throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {

Cipher c = Cipher.getInstance(spec.getAlgorithm());

c.init(Cipher.DECRYPT_MODE, spec);

BufferedInputStream strm = new BufferedInputStream(new CipherInputStream(inStrm, c));

return strm;

}

public BufferedOutputStream getOutput(OutputStream outStrm)

throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {

Cipher c = Cipher.getInstance(spec.getAlgorithm());

c.init(Cipher.ENCRYPT_MODE, spec);

BufferedOutputStream strm = new BufferedOutputStream(new CipherOutputStream(outStrm, c));

return strm;

}

public static String usage() {

return "java AESStreams <password> <in-plaintext-filename>";

}

// NOTE: This is a DISMAL way to generate a key - but it's quick...

public static byte[] keyFromPassword(String pwd) {

byte[] key = new byte[16];

for (int i=0; i<key.length; i++) key[i] = (byte)pwd.charAt(i%key.length);

return key;

}

public static void main( String[] args ) throws Exception {

if (args.length != 2) {

System.out.println(AESStreams.usage());

System.exit(0);

}

SecretKeySpec spec = new SecretKeySpec(keyFromPassword(args[0]), "AES");

File original = new File(args[1]);

File encFile = new File(original.getAbsolutePath()+".enc");

File decFile = new File(original.getAbsolutePath()+".dec");

AESStreams aes = new AESStreams(spec);

BufferedInputStream plaintextIn = new BufferedInputStream(new FileInputStream(original));

BufferedOutputStream ciphertextOut = aes.getOutput(new FileOutputStream(encFile));

byte[] buffer = new byte[4096];

int bytesRead = plaintextIn.read(buffer);

while (bytesRead > 0) {

ciphertextOut.write(buffer, 0, bytesRead);

bytesRead = plaintextIn.read(buffer);

}

ciphertextOut.flush();

ciphertextOut.close();

BufferedInputStream ciphertextIn = aes.getInput(new FileInputStream(encFile));

BufferedOutputStream plaintextOut = new BufferedOutputStream(new FileOutputStream(decFile));

buffer = new byte[4096];

bytesRead = ciphertextIn.read(buffer);

while (bytesRead > 0) {

plaintextOut.write(buffer, 0, bytesRead);

bytesRead = ciphertextIn.read(buffer);

}

plaintextOut.flush();

plaintextOut.close();

}

}

NOTE: the password-to-key process is...distinctly suboptimal. Search here (or Google) for "correcter" ways to get from user-input-strings to bytes.

G

ggaineya at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 9

Someone mention earlier that RSA encryption is used to encrypt symmetric keys. So for the example of AES, how would this work. What would be the order of operations for both encryption and decryption. Also, the AES key is in the form of the object SecretKey, so how would i get the corresponding key as an array of bytes to encrypt the key?

JNooreza at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...
# 10

> Someone mention earlier that RSA encryption is used

> to encrypt symmetric keys. So for the example of AES,

> how would this work. What would be the order of

> operations for both encryption and decryption. Also,

> the AES key is in the form of the object SecretKey,

> so how would i get the corresponding key as an array

> of bytes to encrypt the key?

See reply #3 .

sabre150a at 2007-7-12 8:33:23 > top of Java-index,Security,Cryptography...