Hi. I've got a problem with the RSA ciphered streams. Here is a piece of
code that generates an exception :
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
System.out.print("Generating the RSA key pair...");
KeyPair kp = gen.generateKeyPair();
System.out.println("done.");
Cipher ci = Cipher.getInstance("RSA");
ci.init(Cipher.ENCRYPT_MODE,kp.getPublic());
Cipher cid = Cipher.getInstance("RSA");
cid.init(Cipher.DECRYPT_MODE,kp.getPrivate());
CipherOutputStream cout = new CipherOutputStream(new
FileOutputStream("rsa.dat"),ci);
ObjectOutputStream oout = new ObjectOutputStream(cout);
oout.writeObject(kp.getPrivate().toString());
oout.flush();
oout.close();
CipherInputStream cin = new CipherInputStream(new
FileInputStream("rsa.dat"),cid);
ObjectInputStream oin = new ObjectInputStream(cin); // Exception here
System.out.println(oin.readObject());
oin.close();
The exception is EOFException and it's not very weird because the file
"rsa.dat" is empty after writing to it!! It happens only if object is "big"
( when I try to write some short String, for example, everything's ok ).
When I tried to write an unciphered output ( without using
CipheredOutputStream ) it went well of course.
My question is : can anybody tell me why it happens? What should I do to
write "big" objects to ciphered output stream using ObjectOutputStream
properly?
Thanks for any requests.
Kuba
[snip]
> The exception is EOFException and it's not very weird because the
> file "rsa.dat" is empty after writing to it!! It happens only if
[quoted text clipped - 7 lines]
>
> Kuba
Hi,
My experience shows that RSA Cipher implementations are usually only
capable of encrypting a single block at a time, after which the
entire Cipher object has to be reinitialized. This limits you to 127
bytes with a typical 1024-bit key size (less if you use a proper
padding scheme, like all real-world applications should). If you have
control over both ends of the system, I'd recommend:
1. Generate a random symmetric key (AES perhaps?)
2. Create a Cipher object using your RSA public key, in
Cipher.WRAP_MODE.
3. Use the Cipher.wrap() method to wrap your symmetric key using your
RSA key. Put the output in your stream, delimited somehow (length
bytes before hand?)
4. Create a Cipher object using the random symmetric key, in
Cipher.ENCRYPT_MODE.
5. Put a CipherOutputStream around the Cipher object.
6. Use the CipherOutputStream to encrypt your data.
For decryption:
1. Pull in the encrypted key from the beginning of the stream.
2. Create a Cipher object using your RSA private key, in
Cipher.UNWRAP_MODE.
3. Use the Cipher.unwrap() method to get the symmetric key.
4. Create a Cipher object using the recovered symmetric key, in
Cipher.DECRYPT_MODE.
5. Put a CipherInputStream around the Cipher object.
6. Use the CipherInputStream to decrypt your data.
Note that some symmetric ciphers in some modes may have extra
information which needs to be sent from one end to the other
(although not encrypted), such as an IV.
If you're dealing with very much data, you'll also find that this
method is a lot faster, since RSA is painfully slow and symmetric
ciphers are blazingly fast. Also, it's more space-efficient: at the
raw level, RSA encryption takes 127 bytes input and produces 128
bytes out. Any real-world system should utilize a padding system,
which uses up maybe 10 bytes, leaving you with just 117 bytes input
to 128 bytes output (read: every block of plaintext produces a
*bigger* block of ciphertext). Symmetric ciphers, on the other hand,
usually encrypt one blocksize worth of bytes to exactly the same
size, and also usually get padded only on the last block (read: the
ciphertext is bigger than the plaintext, but only by a tiny bit, and
by a constant amount, no matter how much plaintext there is).
Chris
Jakub Stuglik - 14 Dec 2004 18:42 GMT
Thanks very much. Now I will know that I shouldn't use it for my program.
I'll try the scheme you wrote.
Kuba