 |
AppletTalk.com Java discussions newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
JMecc Guest
|
Posted: Sun Dec 24, 2006 6:27 am Post subject: Java Stream Encryption |
|
|
I am trying to encrypt a stream that I am sending from a port on one
machine to another machine (both sides using Java classes). The
standard writing across ports works:
Server:
serverSocket = new ServerSocket(port);
Socket sock = serverSocket.accept();
PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
out.println("message");
Client:
Socket sock = new Socket(server, port);
BufferedReader in = new BufferedReader(new
InputStreamReader(sock.getInputStream()));
String success = in.readLine();
I am trying to RSA encrypt this stream by using the CipherInputStream &
CipherOutputStream classes:
Server:
serverSocket = new ServerSocket(port);
Socket sock = serverSocket.accept();
CipherOutputStream cos = new
CipherOutputStream(socket.getOutputStream(),cyO);
PrintWriter out = new PrintWriter(cos, true);
out.println("message");
Client:
Socket sock = new Socket(server, port);
CipherInputStream cis = new
CipherInputStream(sock.getInputStream(),cyI);
BufferedReader in = new BufferedReader(new InputStreamReader(cis));
String success = in.readLine();
I retrieve the public & private keys from files (keys are serializable
objects) and then create ciphers:
cyI = Cipher.getInstance("RSA"); //Input stream cipher to decrypt
cyI.init(Cipher.DECRYPT_MODE, privateKey);
cyO = Cipher.getInstance("RSA"); //Output stream cipher to encrypt
cyO.init(Cipher.ENCRYPT_MODE, publicKey);
the keys were originally created via KeyPairGenerator:
KeyPairGenerator KPG = KeyPairGenerator.getInstance("RSA");
KPG.initialize(1024); //Key length
KP = KPG.generateKeyPair();
priKey = (RSAPrivateKey) KP.getPrivate();
pubKey = (RSAPublicKey) KP.getPublic();
The program just hangs, so running eclipse debugging on both gets me to
the "String success = in.readLine();" line on the client, where it
blocks waiting for the server. The server has no problem running its
lines to send the data but for some reason the client does not
recognize that anything is coming in and just keeps blocking. Anyone
see what my pitfall is?
Thanks,
Jo |
|
| Back to top |
|
 |
sgoo Guest
|
Posted: Tue Dec 26, 2006 6:06 am Post subject: Re: Java Stream Encryption |
|
|
Not sure, maybe the server side needs a flush after the println? |
|
| Back to top |
|
 |
Ralf Ullrich Guest
|
Posted: Tue Dec 26, 2006 4:10 pm Post subject: Re: Java Stream Encryption |
|
|
JMecc wrote:
| Quote: | The server has no problem running its
lines to send the data but for some reason the client does not
recognize that anything is coming in and just keeps blocking. Anyone
see what my pitfall is?
|
See JavaDoc for CipherOutputStream.flush() where it reads: "Any bytes
buffered by the encapsulated cipher and waiting to be processed by it will
not be written out. For example, if the encapsulated cipher is a block
cipher, and the total number of bytes written using one of the write
methods is less than the cipher's block size, no bytes will be written out."
And regarding Block-Ciphers see also JavaDoc for Cipher: "Using modes such
as CFB and OFB, block ciphers can encrypt data in units smaller than the
cipher's actual block size. When requesting such a mode, you may
optionally specify the number of bits to be processed at a time by
appending this number to the mode name as shown in the
"DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no
such number is specified, a provider-specific default is used. (For
example, the SunJCE provider uses a default of 64 bits for DES.) Thus,
block ciphers can be turned into byte-oriented stream ciphers by using an
8 bit mode such as CFB8 or OFB8."
So, unless you created your Cipher as "CFB8" or "OFB8", what you observe
is expected behavior. Works as designed.
As for RSA, well RSA unfortunately supports only ECB as mode, so you
cannot create it with "CFB8" or "OFB8" as mode, and hence you wont be able
to force a 'flush' on the Cipher through an CipherOutputStream other than
through a stream close operation, which will (in your case) also
effectively close the network connection.
My advice: Use DES/CFB8/NoPadding (or something similar with larger keys)
for your encrypted network connection. You might want to encrypt the DES
symmetric key via RSA and send it upfront on your network socket, before
wrapping the socket in a Cipher*Stream, so you can establish a 'session
key' for DES encryption, using your possibly already deployed asymetric
RSA public/private keys.
HTH
cu |
|
| Back to top |
|
 |
JMecc Guest
|
Posted: Tue Dec 26, 2006 9:15 pm Post subject: Re: Java Stream Encryption |
|
|
sgoo wrote:
| Quote: | Not sure, maybe the server side needs a flush after the println?
|
Thanks for the reply. Alas no, out.flush() didn't change anything.
What I find odd is that the reading and writing semantics are good
without encryption and then are not with encryption. SInce I am using
a natural pair to do this (CipherInputStream & CipherOutputStream)
there shouldn't be an issue so I am thinking that either:
a) I am using the Cipher[In/Out]putStream functions incorrectly.
b) The way I generate the RSA keys
(KeyPairGenerator.getInstance("RSA")) is incompatible with
CipherInputStream.
Any thoughts?
Thanks,
Jo |
|
| Back to top |
|
 |
Ralf Ullrich Guest
|
Posted: Fri Dec 29, 2006 8:18 pm Post subject: Re: Java Stream Encryption |
|
|
JMecc wrote:
| Quote: | I guess I am not restricted to RSA, it is just the only one I've heard
of but I need some way of encrypting client-server information passage
after an initial handshake.
|
Jo,
there are asymmetric encryption schemes, like RSA, those use
public/private key pairs, i.e. one of both keys on each end, and there are
symmetric encryption schemes, like DES, those use the same key on both
ends. This is basic encryption know how, if you do not know this, you
should try to read a book about encryption before you try to code a Java
program using encryption, because chances are, that out of ignorance you
make it wrong, and hence your program wont be more secure than it would be
without using encryption.
However, if you use DES, then both sides have to use the same key, and in
Java you do not create a KeyPair from KeyPairGenerator and the feed the
private (or public) key of the pair to the DES Cipher, as you tried, but
instead you use a KeyGenerator, to create a Key, and you have to share
this key on both ends of your communication. However, anybody who
intercepts this shared key, can read your communication, so it is only as
secure, as your shared key is. But: it is easy to create a Cipher, that
enables a per byte encryption, and hence allows a Cipher*Stream to
transmit single bytes, just as you need it:
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(56); //Key length
Key k = kg.generateKey(); // that is the key to be shared!
Cipher c = Cipher.getInstance("DES/CFB8/NoPadding");
c.init(Cipher.ENCRYPT_MODE, k);
System.out.println(c.getBlockSize());
CipherOutputStream cos = new CipherOutputStream(System.out, c);
cos.write('A');
cos.flush();
To avoid this, asymmetric encryption schemes like RSA use public/private
key pairs, where you can freely distribute the public key, used for
encryption, while keeping the private key, the one used for decryption, to
yourself. Because you never transport your private key, it can never be
intercepted, and hence this scheme can be much more easily protected
against eavesdropper, than symmetric schemes. However, as you know by now,
you cannot create a asymmetric Cipher, that allows per byte encryption as
shown above. Hence such a Cipher used for a Cipher*Stream will only
transmit encrypted bytes, if either enough data has been written or the
stream is closed an the encryption scheme gets finalized. Also such
asymmetric Cipher are much much slower than symmetric Ciphers and are not
suitable to encrypt large amounts of data.
So in order to get both:
a) better secrecy of asymmetric encryption,
b) better speed and bytewise operation of symmetric encryption,
you have to combine them:
A) Create a KeyPair (e.g. for RSA). Keep the private key at the receiving
end, and transport the public key to the sender. (Well, if nothing else
works, put it in the newspaper, so the sender can read it there!)
B) Open a unencrypted Socket between Sender and Receiver.
C0) Create a Random symmetric Key (e.g. for DES) at the sending end.
C1) Convert this key to a sequence of bytes.
C2) encrypt this sequence of bytes using the public key and a (RSA)Cipher.
The result is again a sequence of now encrypted bytes.
C3) Transmit this encrypted bytes openly through the socket to the
receiving end.
C4) At the receiving end decrypt those bytes using the private key and
(RSA) Cipher. You will get the same sequence of unencrypted bytes as in
C1, but now you have this sequence on both ends, that is you have
established a shared secret.
C5) At the receiving end create a symmetric Key but not random as in C0)
instead use the sequence of bytes as initialization data. You will now
have a Key-Object on both ends and both objects wil represent the same
symmetric key.
D) Now use the Key object to create a symmetric Cipher. (Dont forget to
tell it to use CFB8 or OFB8 mode!) And use this Cipher for your
Cipher*Streams.
E) You have now established a secure data transport from the sending
CipherOutputStream to the receiving CipherInputStream, which will actually
transmit data, if you flush it.
If you cannot build it from this explanation, then please refrain from
coding encryption part yourself and pay some expert to do it for you,
because using encryption in a wrong way, is often not better than using no
encryption at all. And if cou cannot do it with this explanation, then you
will probably do it wrong anyway.
cu |
|
| Back to top |
|
 |
Mike Amling Guest
|
Posted: Fri Dec 29, 2006 9:01 pm Post subject: Re: Java Stream Encryption |
|
|
Ralf Ullrich wrote:
| Quote: | C0) Create a Random symmetric Key (e.g. for DES) at the sending end.
|
Meaning the sending end for the symmetric key, which may or may not
be the sending end for the message.
| Quote: | E) You have now established a secure data transport from the sending
CipherOutputStream to the receiving CipherInputStream, which will
actually transmit data, if you flush it.
|
Well, it's only as secure as DES, which is obsolete. Use AES or
triple-DES.
Don't forget proper RSA padding, such as OAEP or SAEP+, when sending
the symmetric key.
Don't forget to use authentication, such as a MAC or digital
signature, on the message.
--Mike Amling |
|
| Back to top |
|
 |
JMecc Guest
|
Posted: Sat Dec 30, 2006 12:38 am Post subject: Re: Java Stream Encryption |
|
|
Thanks Ralf. I am still struggling to get this working however as I
cannot create CFB8 keys (KeyPairGenerator KPG =
KeyPairGenerator.getInstance("CFB8") . I have only successfully
created RSA ones this way (maybe there are alternative ways to create
keys). Of course with RSA keys I get to the last line of:
PrivateKey KI = Keys.readPrivate("cliPri.key");
cyI = Cipher.getInstance("DES/CFB8/NoPadding");
cyI.init(Cipher.DECRYPT_MODE, KI);
and have the error:
java.security.InvalidKeyException: No installed provider supports this
key: sun.security.rsa.RSAPrivateCrtKeyImpl
at javax.crypto.Cipher.a(DashoA12275)
at javax.crypto.Cipher.init(DashoA12275)
at javax.crypto.Cipher.init(DashoA12275)
at client.setCipher(client.java:75)
at client.<init>(client.java:55)
at client.main(client.java:17)
I guess I am not restricted to RSA, it is just the only one I've heard
of but I need some way of encrypting client-server information passage
after an initial handshake.
Thanks,
Jo |
|
| Back to top |
|
 |
JMecc Guest
|
Posted: Thu Jan 04, 2007 3:25 am Post subject: Re: Java Stream Encryption |
|
|
OK so I am on my way to doing this correctly - following Ralf's
procedure:
1) Client sends RSA public key to server (unencrypted).
2) Server sends its public key back, also unencrypted (now we have
2-way RSA encrypted traffic and this works although slowly for big
files as you told me it would)
3) Client makes password string including machine data, RSA encrypts
it and sends that to the server.
4) Server verifies password and if ok, creates DES key and sends it to
client (RSA-encrypted).
5) Client decrypts this DES key, client and server make input & output
Stream*Ciphers with the DES key as initializer (this is where I am
still having a problem).
6) Any further communication is done using these Stream*Ciphers.
I didn't get how to initialize a key with a byte sequence but I found
on the web that I should be getting the parameters from my encryption
cipher using cipher.getParameters() and then pass those such that they
could be used to initialize the decryption cipher:
decrCipher.init(Cipher.DECRYPT_MODE, key, params);
The AlgorithmParameters class is not serializable though, so I use
params.getEncoded() to get a byte[], pass that over and reconstruct
params as:
params = AlgorithmParameters.getInstance("DES");
params.init(bytearray);
While this conversion performs fine within the same class, when sent
from one machine to another, trying to init a new cipher with the
recieved key and reconstructed params yields an InvalidKeyException:
Illegal Key Size.
Googling this "Illegal Key Size" universally pointed the finger at the
need for Sun's Unlimited Strength Java(TM) Cryptography Extension
Policy (bottom of http://java.sun.com/javase/downloads/index_jdk5.jsp
). I downloaded these and used them to overwrite Java's regular ploicy
files in all JRE (even within JDK) locations on both the client and
server and rebooted both machines. The error persists. I would like
to get this working before I try any triple-DES or anything more
difficult.
Anybody experience this error?
Thanks,
Jo |
|
| Back to top |
|
 |
JMecc Guest
|
Posted: Thu Jan 04, 2007 6:30 am Post subject: Re: Java Stream Encryption |
|
|
Update: I got rid of the InvalidKeyException by changing:
secret = (SecretKey) cyUnwrap.unwrap(wrapped, "DES/CFB8/NoPadding",
Cipher.SECRET_KEY);
to
secret = (SecretKey) cyUnwrap.unwrap(wrapped, "DES",
Cipher.SECRET_KEY);
I'm now trying to set up the stream ciphers to see if the whole thing
works.
Jo |
|
| Back to top |
|
 |
JMecc Guest
|
Posted: Thu Jan 04, 2007 2:41 pm Post subject: Re: Java Stream Encryption |
|
|
OK it seems to be working fine now and man is DES faster than RSA!
Thanks a lot for the help! I was still blocking on readLine() but
changed this in favor of datainputstream.readFully() after a readInt -
I send an int stating how many bytes to expect and then the bytes
themselves. This makes everything run much smoother.
Thanks again for all the replies,
Jo |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|