16

I've read the following threads and they've helped a little, but I'm looking for a little more info.

How to write AES/CBC/PKCS5Padding encryption and decryption with Initialization Vector Parameter for BlackBerry

Java 256bit AES Encryption

Basically, what I am doing is writing a program that will encrypt a request to be sent over TCP/IP, and then decrypted by a server program. The encryption will need to be AES, and doing some research I found out I need to use CBC and PKCS5Padding. So basically I need a secret key and an IV as well.

The application I'm developing is for a phone, so I want to use the java security packages to keep the size down. I've got the design done, but unsure of the implementation of the IV and the shared key.

Here's some code:

// My user name
byte[] loginId = "login".getBytes();

byte[] preSharedKey128 = "ACME-1234AC".getBytes();
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes();
// 256 bit key
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes();
byte[] preSharedKey = preSharedKey256;

// Initialization Vector
// Required for CBC
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ips = new IvParameterSpec(iv);


byte[] encodedKey = new byte[loginId.length + preSharedKey.length];

System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);

// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.

// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");

// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher;

encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips);

// Our cleartext
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330";
byte[] cleartext = clearString.getBytes();

// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);

// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);

// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);

In a nutshell what it should do is encrypt some message that can decrypted by the server without the server needing to get a key or IV from the phone. Is there a way I could do this where I could secure the IV and key on the phone, and still have the key and IV known by the server as well? Feel free to tell me to make things more clear if they're not.

Community
  • 1
  • 1
Steven
  • 691
  • 3
  • 10
  • 26
  • 1
    The hardcoded keys and passwords here are for my understanding only. I don't know a lot about encryption so I'm basically in make-it-compile-and-run mode. – Steven Sep 17 '09 at 18:02
  • 1
    What's your threat model? I mean, if you are thinking encryption is needed, you must have some idea about who'd want this information and why, how much their getting it would hurt you, benefit them, etc. – erickson Sep 17 '09 at 18:17
  • I guess my threat model is we have certain account/login information necessary to do a bank transaction and we don't want that exposed. Is that what you're looking for? – Steven Sep 17 '09 at 18:46
  • If the server has a table of user names and passwords ("account/login information"), why doesn't it use that password as the key? This way, every user has a different key, and better yet, it's stored in their brain, not the phone itself. – erickson Sep 18 '09 at 22:16
  • @erickson - Ok if I do that, how do I make it so the server knows which key (account/login information) to use for each request? – Steven Sep 18 '09 at 22:42
  • Each request is essentially "user name" + "encrypt_with_password(message + compute_mac_with_password(message))". At the server, look up the password corresponding to the user name (which is "in the clear"), decrypt the content, and validate the MAC. If the MAC is valid, the sender knows the password. I'm not totally happy with this approach though, because it requires storing a password at the server. Ephemeral-Static Diffie-Hellman seems best. If you want an example, post another question. – erickson Sep 21 '09 at 06:27
  • Ok, I posted another question at http://stackoverflow.com/questions/1455410/phone-to-server-communication-encryption-in-java – Steven Sep 21 '09 at 16:23

2 Answers2

12

There are a few problems with the code. First of all, you really should use a key generator to generate secret keys. Just using some text directly might work for some algorithms, but others have weak keys and so forth that need to be tested.

Even if you want to do password-based encryption, the password should be run through a key-derivation algorithm to produce a key, as shown in my answer to the question that you cited already.

Also, you shouldn't use the no-arg getBytes() method of String. This is platform dependent. If all of the strings that you are encoding contain only characters from the US-ASCII character set, make it clear by specifying that encoding explicitly. Otherwise, if the phone and server platforms use different character encodings, the key and IV won't turn out the same.

For CBC mode, it's best to use a new IV for every message you send. Usually, CBC IVs are generated randomly. Other modes like CFB and OFB require unique IVs for every message. IVs are usually sent with along the ciphertext—IVs don't need to be kept secret, but many algorithms will break if a predictable IV is used.

The server doesn't need to get the secret or IV directly from the phone. You can configure the server with the secret key (or password, from which the secret key is derived), but in many applications, this would be a bad design.

For example, if the application is going to be deployed to the phones of multiple people, it isn't a good idea for them to use the same secret key. One malicious user can recover the key and break the system for everyone.

A better approach is to generate new secret keys at the phone, and use a key agreement algorithm to exchange the key with the server. Diffie-Hellman key agreement can be used for this, or the secret key can be encrypted with RSA and sent to the server.


Update:

Diffie-Hellman in "ephemeral-static" mode (and "static-static" mode too, though that's less desirable) is possible without an initial message from the server to the phone, as long as the server's public key is embedded in the application.

The server public key doesn't pose the same risk as embedding a common secret key in the phone. Since it is a public key, the threat would be an attacker getting his hands on (or remotely hacking into) the phone and replacing the real public key with a fake key that allows him to impersonate the server.

Static-static mode could be used, but it's actually more complicated and a little less secure. Every phone would need its own unique key pair, or you fall back into the secret key problem. At least there would be no need for the server to keep track of which phone has which key (assuming there is some authentication mechanism at the application level, like a password).

I don't know how fast phones are. On my desktop, generating an ephemeral key pair takes about 1/3 of a second. Generating Diffie-Hellman parameters is very slow; you'd definitely want to re-use the parameters from the server key.

Community
  • 1
  • 1
erickson
  • 265,237
  • 58
  • 395
  • 493
  • Right now, the design I'm being told to use says we can use the same key for every phone but don't agree with it. Is the Diffie-Hellman approach easy to implement? – Steven Sep 17 '09 at 17:29
  • No, Diffie-Hellman is a bit confusing, to me at least. It uses the KeyAgreement class. RSA encryption is easier. It uses a Cipher like the AES encryption you are currently using. One thing to note with RSA is that the ciphertext will be hundreds of bytes, which is a bit of a waste if your plaintext is only a few bytes; not sure if that matters in your application. Another option to consider is that if the communication is very short messages (100 to 200 bytes) from phone to server only, you can just encrypt the message itself with RSA. – erickson Sep 17 '09 at 17:45
  • Yea I think the level of security I'm looking for doesn't involve something really complicated. Will keep it in mind though. – Steven Sep 17 '09 at 18:29
  • Getting back to the Diffie-Hellman thing, is it required for the sender to first send something to the server to establish the key and then send again after encrypting with the key? – Steven Sep 17 '09 at 21:39
  • I like this approach, just don't know how to handle the whole issue with securing the key by generating a password. How do I keep a person from reverse engineering the application and finding the password? – Steven Sep 18 '09 at 22:38
  • Which approach do you mean? I listed a few. Also, I am suggesting you don't use a password. Use key agreement, or public key encryption to get the server and phone to exchange a key. If you are interested in one of those approaches, post another question and I'll give some code (this answer is getting a bit lengthy already). – erickson Sep 21 '09 at 06:22
2

Done similar projects in a midlet before, I have following advice for you:

  1. There is no secure way to store shared secret on the phone. You can use it but this falls into a category called Security through Obscurity. It's like a "key under mat" kind of security.
  2. Don't use 256-bit AES, which is not widely available. You might have to install another JCE. 128-bit AES or TripleDES are still considered secure. Considering #1, you shouldn't worry about this.
  3. Encryption using a password (different for each user) is much more secure. But you shouldn't use password as the key like you are showing in the example. Please use PBEKeySpec (password-based encryption) to generate the keys.
  4. If you are just worried about MITM (man-in-the-middle) attacks, use SSL.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • Why can't 256-bit AES be used? I can already create a 256-bit key in my test setup, but it's done manually. Mostly, management is worried about someone intercepting a transmission and either using the info in the transmission or getting a phone and reverse-engineering the algorithm and basically be an imposter in our network. – Steven Sep 17 '09 at 18:01
  • Lots of phones we tried don't support it. 128-bit AES is still used by my bank. It's secure. – ZZ Coder Sep 17 '09 at 18:13
  • 1
    Actually some weaknesses have been found in AES 256. AES-128 is plenty strong, and is recommended by Bruce Schneier over AES 256 when it's not required for interoperability. (http://www.schneier.com/blog/archives/2009/07/another_new_aes.html) However, the algorithm really doesn't matter if the key isn't secure. – erickson Sep 17 '09 at 18:14
  • 1
    How do you know you use 256-bit AES? Check the value of cipher.getBlockSize(). It should return 32. Some JCE providers don't give errors when AES-256 is not supported. It simply uses first 128-bit of the key. – ZZ Coder Sep 17 '09 at 18:18
  • 1
    Ohh, with the code I have above, I did that .getBlockSize() and it returns 16, not even 32 or 64. So what do that mean? AES isn't in 64 bit is it? – Steven Sep 17 '09 at 18:25
  • "256" refers only to the size of the key. The cipher block is 128 bits in either case. Rijndael has variable block sizes, but it's not AES. – erickson Sep 17 '09 at 18:27
  • Original AES is 128-bit (16 bytes). Currently, most JCE providers don't support AES. Some do it but in a very confusing way so you never know what you are using unless you check the block size. – ZZ Coder Sep 17 '09 at 18:28
  • 2
    The AES block size is always 128 bits. If the cipher reports a block size of 32 bytes, it might be Rijndael, but it's certainly not AES. See http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf "The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt and decrypt data in blocks of 128 bits." – erickson Sep 17 '09 at 18:34
  • 1
    @erickson, you are right. Checking block size isn't the right way. I have to figure out another way ... – ZZ Coder Sep 17 '09 at 18:35
  • 1
    @Steven, forget about my findings on the AES 256 support on phones. My test was wrong. So you might want give it a shot. – ZZ Coder Sep 17 '09 at 19:00
  • Ok. But I do like what you're saying about the PBEKeySpec. @erickson I think you had this is in your example as well. – Steven Sep 17 '09 at 19:03
  • 1
    You might want consider 128 bit after all :( Just bumped into the guy who did tests on phones. He said most phones are installed with a restricted (limited strength) JCE policy which restricts the key length to 128-bit. – ZZ Coder Sep 17 '09 at 21:46