Java string encryption/decryption

D

dj_uncas

Hello group,

I have a problem, and I hope you can help me. I'm building an RMI
client/server solution, which requires encrypted parameters. I'm
mostly sending objects with String-type parameters, and therefore just
need to be able to encrypt and decrypt a String.

I have been looking at Java Cryptographic Extension, and had a running
sample that worked, however in the sample, I did not convert the
encrypted byte-array to a String before decrypting it. When I try
that, I get different exceptions regarding the length of the String
I'm trying to decrypt. For example, with the Triple DES algorithm I
get:
"javax.crypto.IllegalBlockSizeException: Input length must be multiple
of 8 when decrypting with padded cipher"

Some other algorithms just tell me, that the incomming string is not
padded correctly...

I think my issue is in converting the encrypted byte-array to a
String, and back - and this is where I fall short. Can you give me
some pointers on this?


Regards,

Mads
 
D

dj_uncas

You probably need to convert the cyphertext to Base64 rather than to a
String.  Cyphertext will contain all bytes from -128 to 127 which will
make for a very strange string if you convert it directly.  Base64 is
a form of armouring so you can effectively transmit raw bytes over a
text stream.


3DES is obsolescent.  Unless you are using it for backwards
compatibility you should change to AES.  The blocksize for AES is 128
bits (16 bytes).  What padding did you specify?  The JCE default is no
padding so you will get a blocksize error if your plaintext is not a
multiple of 8 bytes (DES) or 16 bytes (AES).  Also if you are using
ECB mode, do not because it is not safe.  Use CBC or CTR instead.




Check what padding you are specifying at both ends.  They must be the
same.




The cyphertext will not make sense as a String and should not be
converted to a String as such.  If you need to be able to transmit the
cyphertext over a text stream or similar then you will need to use
something like Base64 to enable this.  Of course you will need to
change from Base64 back into a byte array before decrypting.





Start with your plaintext as a String.
Convert the plaintext to a byte array.
Encrypt the byte array to produce the cyphertext as a byte array.
Convert the cyphertext byte array into a Base64 String for
transmission.
After transmission convert the Base64 String back into the cyphertext
byte array.
Decrypt the cyphertext byte array to give the plaintext byte array.
Convert the plaintext byte array back into a String.

Remember to specify the same padding at both ends.

rossum


Well, it makes perfect sense. And Base64 was probably the step i
missed. I still get a "Given final block not properly padded"
exception though. I get a cipher instance like this:

Cipher c = Cipher.getInstance( "AES" );

What do I do to specify the padding? I'm a total Java crypto novice :-(
 
D

dj_uncas

  Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");

Just one more thing: Which JCE provider can I use for the above
Cipher? I had com.sun.crypto.provider.SunJCE (dynamically) installed,
but it cannot find the algorithm.
 
A

Arne Vajhøj

dj_uncas said:
I have a problem, and I hope you can help me. I'm building an RMI
client/server solution, which requires encrypted parameters. I'm
mostly sending objects with String-type parameters, and therefore just
need to be able to encrypt and decrypt a String.

I have been looking at Java Cryptographic Extension, and had a running
sample that worked, however in the sample, I did not convert the
encrypted byte-array to a String before decrypting it. When I try
that, I get different exceptions regarding the length of the String
I'm trying to decrypt. For example, with the Triple DES algorithm I
get:
"javax.crypto.IllegalBlockSizeException: Input length must be multiple
of 8 when decrypting with padded cipher"

Some other algorithms just tell me, that the incomming string is not
padded correctly...

I think my issue is in converting the encrypted byte-array to a
String, and back - and this is where I fall short. Can you give me
some pointers on this?

Hex:

private static String toHex(byte[] ba) {
StringBuffer sb = new StringBuffer("");
for (int i = 0; i < ba.length; i++) {
sb.append(Integer.toHexString((ba >> 4) & 0x0F));
sb.append(Integer.toHexString(ba & 0x0F));
}
return sb.toString();
}
private static byte[] fromHex(String s) {
int n = s.length() / 2;
byte[] res = new byte[n];
for(int i = 0; i < n; i++) {
res = (byte)(Integer.parseInt(s.substring(2 * i, 2 * i +
2), 16));
}
return res;
}


Base64:

public static String b64encode(byte[] b) throws MessagingException,
IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream b64os = MimeUtility.encode(baos, "base64");
b64os.write(b);
b64os.close();
return new String(baos.toByteArray());
}
public static byte[] b64decode(String s) throws
MessagingException, IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
InputStream b64is = MimeUtility.decode(bais, "Base64");
byte[] tmp = new byte[s.length()];
int n = b64is.read(tmp);
byte[] res = new byte[n];
System.arraycopy(tmp, 0, res, 0, n);
return res;
}

Arne
 
A

Arne Vajhøj

dj_uncas said:
Just one more thing: Which JCE provider can I use for the above
Cipher? I had com.sun.crypto.provider.SunJCE (dynamically) installed,
but it cannot find the algorithm.

All SUN Java versions >= 1.4.2 should have it.

Arne
 
A

Arne Vajhøj

rossum wrote:

[a ton of good advice that I will not comment on]
You probably need to convert the cyphertext to Base64 rather than to a
String. Cyphertext will contain all bytes from -128 to 127 which will
make for a very strange string if you convert it directly.

(new String(b, cs)).getBytes(cs) will even return b for all
character sets.

Arne
 
R

Roedy Green

I have a problem, and I hope you can help me. I'm building an RMI
client/server solution, which requires encrypted parameters. I'm
mostly sending objects with String-type parameters, and therefore just
need to be able to encrypt and decrypt a String.

Encryption works with bytes. So you must first encode your string as
byte[] to encrypt it, e.g. to UTF-8. see
http://mindprod.com/jgloss/encoding.

You will get a mishmash of bytes once encrypted. You can't simply
decode this as a UTF-8 string since it will include bytes that are not
legit UTF-8 sequences. You must "armour" it to convert it to String,
usually a string of limited character set, so it won't be mangled by
email. There are many ways to armour. BASE64 is common.

See http://mindprod.com/jgloss/armouring.html

Code provided.

At the other end, you string the armour, and get back the bytes. Then
you decrypt. You get back bytes. Then you decode the UTF-8. Whew!

For code to do all this without JCE see:
http://mindprod.com/products1.html#TRANSPORTER

For code to do it with JCE see:
http://mindprod.com/jgloss/cipher.html
--
Roedy Green Canadian Mind Products
http://mindprod.com
Your old road is
Rapidly agin'.
Please get out of the new one
If you can't lend your hand
For the times they are a-changin'.
 
R

Roedy Green

Just one more thing: Which JCE provider can I use for the above
Cipher? I had com.sun.crypto.provider.SunJCE (dynamically) installed,
but it cannot find the algorithm.

To see which algorithms are available with your default provider, see
http://mindprod.com/jgloss/jce.html
which has the code
..
--
Roedy Green Canadian Mind Products
http://mindprod.com
Your old road is
Rapidly agin'.
Please get out of the new one
If you can't lend your hand
For the times they are a-changin'.
 
D

dj_uncas

If that doesn't work then show more code.

It didn't... I found that it isn't the Cipher that throws the
NoSuchAlgorithmException, but the KeyGenerator.getInstance(). My code
looks like this:

KeyGenerator kg = KeyGenerator.getInstance( "AES/CBC/PKCS5Padding" );
System.out.println( kg.getProvider().getName() );
SecretKey key = kg.generateKey();

Cipher c = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
c.init( mode, key );

byte[] result = c.doFinal( input );


Can you spot something?
 
D

dj_uncas

Are you retrieving the generated key?

   AlgorithmParameters ap = c.getParameters();

Without it you will not be able to decrypt.  The same key is needed on
the decrypting machine.

I don't think I am, no. I've added the following to the method:

AlgorithmParameters parameters = c.getParameters();
c.init( mode, key, parameters );

But decrypting throws a
"java.security.InvalidAlgorithmParameterException: Parameters missing"

I'm sure it's just that I don't get it.
Without it you will not be able to decrypt. The same key is needed on
the decrypting machine.

I'm not quite sure what you mean by this? The previously posted code
is used for both encrypting and decrypting - just with different
Cipher modes.
 
D

dj_uncas

<Lots of code>

Awesome dude! It's working for me now :)

That is, until I implement it in my application. It's an RMI
application, which means everything I send has to be serialized.
AlgorithmParameters is not serializable. I've experimented with
serializing it manually, but it seems pretty weird, especially when
deserializing it. Have you tried it before?

The particular code that is failing is this:

String algorithm = in.readUTF();
Provider provider = (Provider)in.readObject();
_parameters = AlgorithmParameters.getInstance( algorithm, provider );

The last line throws this exception:
java.security.NoSuchAlgorithmException: no such algorithm: AES for
provider SunJCE
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top