AES encryption in Java for given key - java

I am trying to do AES encryption in Java. I have the following function:
public static String encrypt(String plainText, String key) throws Exception {
if (plainText == null || plainText.length() == 0) {
return plainText;
}
// get aes key
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
// encrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(plainText.getBytes("UTF-8"));
//encode
String encoded = Base64.encodeToString(bytes, Base64.NO_PADDING | Base64.NO_WRAP);
return encoded;
}
My aim is to be able to encrypt some text with a given key the same way every time. That is, if I call this function with the same two parameters many times, I want the same string to be returned on every call. I'll say in advance, I know that is not how cryptography is supposed to be done, but I need this functionality for my project.
Unfortunately, that is not the case. The key generated in line 7 seems to encrypt my string differently every time around. I assume there is some kind of extra random automatic salting occurring on the lower levels of this library, preventing me from achieving my goal.
Does anyone know a way in Java how I could go about encrypting a given string with a given key to the same value every time? Thanks.
UPDATE/CLARIFICATION: This is not for security. This is for the encryption of data for it to be obfuscated for certain people that might come in contact with working on the app itself. The information is not highly sensitive, but I do want it encrypted and then decrypted by the same key. I have others working with me on this with libraries in their respective languages, e.g. Ruby, and their libraries allow them to encrypt a value with a given key the same way every time. We all want to use the same parameters of the same encryption algorithm: Key length: 128 bits
Mode of operation: CBC
Initialization Vector: None (all zeros)
Is it perhaps that if one does not set an initialization vector, it is randomly assigned? I've got to check that out.

Yes, Java - or rather the security provider supplying the AES in CBC mode implementation - may default to a random IV (which you then have to retrieve afterwards and include with your ciphertext) if you don't explicitly specify it you get secure, randomized ciphertext.
If you want to use a zero IV you will have to specify it explicitly:
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]);
This is only slightly more secure than ECB mode, as any repetition in different messages in the initial ciphertext blocks will be instantly visible to an attacker.
If you want to have a more secure mode without random IV - which is required for CBC mode to obtain CPA security - then you could check out synthetic IV (SIV) mode or GCM-SIV. For those modes the entire message needs to be identical with a previous one to leak info to an attacker. It's however slowe, not included in the standard VM and the ciphertext could be larger than AES/CBC (inclusion of IV/tag vs padding requirements).

Related

How to decrypt data in Java that was encrypted in Obj-c

I am encrypting in ojb-c with SecKeyEncryptedData and trying to decrypt in Java with javax.Cipher and hitting a problem.
I recently moved to doing long blocks and have needed to use a symmetric encryption with the AES key encrypted with the asymmetric key pair. I am having problems decoding.
I have the iOS key kSecKeyAlgorithmRSAEncryptionPKCS1 working for asymmetric data matched with Cipher.getInstance("RSA/ECB/PKCS1Padding") in Java. This decodes the short blocks.
As I need to send longer blocks, and am trying to switch to kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM on iOS and it encrypts fine, but i cannot find the method to use in Cipher to decrypt it and do not understand if it needs to be done in 2 steps in the cloud in Java.
OBJ-C:
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM;
NSData* cipherText = nil;
cipherText = (NSData*)CFBridgingRelease( // ARC takes ownership
SecKeyCreateEncryptedData(self.pubKey, algorithm,
(__bridge CFDataRef)data, &error));
Java:
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, priv);
byte[] dog = decoder.decode(encString);
dec = cipher.doFinal(dog);
res = new String(dec);
} // handle errors
The decode obviously fails.
So my question is in 2 parts.
is there a Cipher type that will do the decode needed or do i need to break out the encrypted AES key and decrypt it first?
If i need to break it up, how long is that encrypted AES key part of the data block and, if you know the ciphers for that it would be fantastic.
is there a Cipher type that will do the decode needed
You may read the Cipher documentation. I believe you are looking for RSA/ECB/OAEPWithSHA-256AndMGF1Padding
I see the designation doesn't exacly match with the Obj-C name, but this is a common standard so it may worth a try
As I need to send longer blocks, and am trying to switch to kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
You may try to search for "hybrid encryption". Asymmetric ciphers are VERY slow comparing to symmetric ciphers and intended to encrypt only limited amount of data.
Some implementation may encrypt longer data anyway (for each 256 bit input providing 2048 o 4096 bit output), Java will simply complain and stop
So proper encryption would be
encrypt data with a radom key (DEK - data encryption key) using a symmetric cipher
encrypt the DEK using an asymmetric public key
If the kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM would be not counterpart (compatible) with RSA/ECB/OAEPWithSHA-256AndMGF1Padding, you may still use the PKCS#1 1.5 padding (the old one) with this approach.
Edit: this asnwer may be useful when working with OAEP too RSA/ECB/OAEPWithSHA-256AndMGF1Padding but with MGF1 using SHA-256?

Is it secure to build a Cipher object with a SecureRandom object which has a fixed seed?

A colleague of mine asked me to check if his code is secure enough. I saw some code snippet like this:
private static byte[] encrypt(String plain, String key) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(key.getBytes());
kg.init(128, secureRandom);
SecretKey secretKey = kg.generateKey();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plain.getBytes());
}
#Test
public void lcg() throws Throwable {
String plain = "abc";
String key = "helloworld";
byte[] c1 = encrypt(plain, key);
byte[] c2 = encrypt(plain, key);
Assert.assertArrayEquals(c1, c2);
}
This encrypt function is used to encrypt sensitive data, and encrypted data will be stored into database. I thought it won't work firstly because SecureRandom won't generate same random number twice even initialzed by the same seed, but it just works in the test.
I think it is not secure to encrypt something in this way, but I can't tell what is the problem about this code snippet.
My question:
is the encrypt function secure?
if it's not secure, what is the problem to do so?
is the encrypt function secure?
No, it is not secure for any good definition of secure.
First of all, it uses ECB, which is not secure unless the plaintext blocks are not related.
More importantly, new SecureRandom() simply gets the first random number generator from the provider list. Usually this was "SHA1PRNG" but currently - for the Oracle Java 11 SE runtime - it returns a much faster and better defined DRBG. Those are not compatible, so one ciphertext cannot be decrypted with another runtime; the code is not portable at all.
Different runtimes may return completely different random number generators - possibly optimized for the runtime configuration. These random number generators may depend completely on the given seed if it is set before random numbers are extracted from it. It may also mix the seed into the state. That will produce a completely random key which you will never be able to regenerate unless you save it somewhere.
Basically, this method may both be insecure because of ECB and overly secure - not a small feat in itself. You may never be able to decrypt the ciphertext again, ever, but you can still distinguish identical plaintext blocks.
A small other problem is that getBytes uses the platform default encoding. This differs e.g. between Windows (Windows-1252) and Linux (UTF-8) and the Android platform (also UTF-8, of course). So decode the plaintext on another system - if you can - and you may still get surprised afterwards.
The procedure is so bad that it should be archived in the round rubbish receiver and implement something new. For that it is a good idea to use a key and IV consisting of random bytes and a modern cipher such as AES in GCM mode at the very least. If you have a password you should use a password hash (or key-based key derivation function) like PBKDF2 or a more modern one to derive a key from it.
Kudo's for finding a worse way to derive keys from passwords. getRawKey was bad, but this one is worse. Good that you asked, in other words.

Too much data for RSA block fail. What is PKCS#7?

Talking about javax.crypto.Cipher
I was trying to encrypt data using Cipher.getInstance("RSA/None/NoPadding", "BC") but I got the exception:
ArrayIndexOutOfBoundsException: too much data for RSA block
Looks like is something related to the "NoPadding", so, reading about padding, looks like CBC is the best approach to use here.
I found at google something about "RSA/CBC/PKCS#7", what is this "PKCS#7"? And why its not listed on sun's standard algorithm names?
Update:
I'm wondering, if is a padding problem, why this example run just fine?
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
/**
* Basic RSA example.
*/
public class BaseRSAExample
{
public static void main(
String[] args)
throws Exception
{
byte[] input = new byte[] { (byte)0xbe, (byte)0xef };
Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
// create the keys
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger("d46f473a2d746537de2056ae3092c451", 16),
new BigInteger("11", 16));
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(
new BigInteger("d46f473a2d746537de2056ae3092c451", 16),
new BigInteger("57791d5430d593164082036ad8b29fb1", 16));
RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec);
RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);
// encryption step
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherText = cipher.doFinal(input);
// decryption step
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] plainText = cipher.doFinal(cipherText);
}
}
Update 2:
I realized that even if I use just Cipher.getInstance("RSA", "BC") it throws the same exception.
If you use a block cipher, you input must be an exact multiple of the block bit length.
In order to encipher arbitrary length data, you need first to pad you data to a multiple of the block length. This can be done with any method, but there are a number of standards. PKCS7 is one which is quite common, you can see an overview on the wikipedia article on padding.
Since block cipers operate on blocks, you also need to come up with a way of concatenating the encrypted blocks. This is very important, since naive techniques greatly reduce the strength of the encryption. There is also a wikipedia article on this.
What you did was to try to encrypt (or decrypt) data of a length which didn't match the block length of the cipher, and you also explicitly asked for no padding and also no chaining mode of operation.
Consequently the block cipher could not be applied to your data, and you got the reported exception.
UPDATE:
As a response to your update and GregS's remark, I would like to acknowledge that GregS was right (I did not know this about RSA), and elaborate a bit:
RSA does not operate on bits, it operates on integer numbers. In order to use RSA you therefore need to convert your string message into an integer m: 0 < m < n, where n is the modulus of the two distinct primes chosen in the generation process. The size of a key in the RSA algorithm typically refers to n. More details on this can be found on the wikipedia article on RSA.
The process of converting a string message to an integer, without loss (for instance truncating initial zeroes), the PKCS#1 standard is usually followed. This process also adds some other information for message integrity (a hash digest), semantical security (an IV) ed cetera. With this extra data, the maximum number of bytes which can be supplied to the RSA/None/PKCS1Padding is (keylength - 11). I do not know how PKCS#1 maps the input data to the output integer range, but
my impression is that it can take any length input less than or equal to keylength - 11 and produce a valid integer for the RSA encryption.
If you use no padding, your input will simply be interpreted as a number. Your example input, {0xbe, 0xef} will most probably be interpreted as {10111110 +o 11101111} = 1011111011101111_2 = 48879_10 = beef_16 (sic!). Since 0 < beef_16 < d46f473a2d746537de2056ae3092c451_16, your encryption will succeed. It should succeed with any number less than d46f473a2d746537de2056ae3092c451_16.
This is mentioned in the bouncycastle FAQ. They also state the following:
The RSA implementation that ships with
Bouncy Castle only allows the
encrypting of a single block of data.
The RSA algorithm is not suited to
streaming data and should not be used
that way. In a situation like this you
should encrypt the data using a
randomly generated key and a symmetric
cipher, after that you should encrypt
the randomly generated key using RSA,
and then send the encrypted data and
the encrypted random key to the other
end where they can reverse the process
(ie. decrypt the random key using
their RSA private key and then decrypt
the data).
RSA is a one-shot asymmetric encryption with constraints. It encrypts a single "message" in one go, but the message has to fit within rather tight limits based on the public key size. For a typical 1024 bit RSA key, the maximum input message length (with RSA as described in the PKCS#1 standard) is 117 bytes, no more. Also, with such a key, the encrypted message has length 128 bytes, regardless of the input message length. As a generic encryption mechanism, RSA is very inefficient, and wasteful of network bandwidth.
Symmetric encryption systems (e.g. AES or 3DES) are much more efficient, and they come with "chaining modes" which allow them to process input messages of arbitrary length. But they do not have the "asymmetric" property of RSA: with RSA, you can make the encryption key public without revealing the decryption key. That's the whole point of RSA. With symmetric encryption, whoever has the power to encrypt a message also has all the needed information to decrypt messages, hence you cannot make the encryption key public because it would make the decryption key public as well.
Thus it is customary to use an hybrid system, in which a (big) message is symmetrically encrypted (with, e.g., AES), using a symmetric key (which is an arbitrary short sequence of random bytes), and have that key encrypted with RSA. The receiving party then uses RSA decryption to recover that symmetric key, and then uses it to decrypt the message itself.
Beyond the rather simplistic description above, cryptographic systems, in particular hybrid systems, are clock full of little details which, if not taken care of, may make your application extremely weak against attackers. So it is best to use a protocol with an implementation which already handles all that hard work. PKCS#7 is such a protocol. Nowadays, it is standardized under the name of CMS. It is used in several places, e.g. at the heart of S/MIME (a standard for encrypting and signing emails). Another well-known protocol, meant for encrypting network traffic, is SSL (now standardized under the name of TLS, and often used in combination with HTTP as the famous "HTTPS" protocol).
Java contains an implementation of SSL (see javax.net.ssl). Java does not contain a CMS implementation (at least not in its API) but Bouncy Castle has some code for CMS.
This error indicates that the input data size is greater than the key modulus size. You will need a bigger key size to encrypt the data. If changing the key length is not an option, alternatively you may need to investigate if you are really expecting that big input data.
RSA can only be used to encrypt, when the number of bits being used to encrypt is greater then the size of the thing you are tying to encrypt + 11 bytes
Public-Key Cryptography Standards - PKCS
Scroll down a bit and you'll see it. It's not an cypher algortihm (like RSA) or a cypher mode like CBC, but a description of the way the certificate is encoded as bytes (i.e., a data structure syntax). You can find the spec for it here.
PKCS#7 is listed (referring to your link). It's encoding is PKCS7
Description
A PKCS#7 SignedData object, with the
only significant field being
certificates.
Use java.security.cert.CertificateFactory or CertPath when using PKCS7.
RSA is a block cipher. It encrypts block of the same key size.
Therefore, BouncyCastle RSA gives an exception if you try to encrypt a block
which is longer than the key size.
That's all I can tell you so far.
You should not encrypt your data using RSA directly. Encrypt your data with a random symmetric key (i.e. AES/CBC/PKCS5Padding) and encrypt the symmetric key using RSA/None/PKCS1Padding.

What's the Java JCE equivalent for this C OpenSSL encryption function?

I am writing a Java implementation of an app originally written in C. I can't modify the C version, and the Java version must share encrypted data with the C version.
Here's the relevant part of the C encryption code:
makekeys(password,&key1,&key2); /* turns password into two 8 byte arrays */
fill_iv(iv); /* bytes 8 bytes of randomness into iv */
des_key_sched(&key1,ks1);
des_key_sched(&key2,ks2);
des_ede2_ofb64_encrypt(hashed,ctext,hashedlen,ks1,ks2,
&iv,&num);
I can see that the JCE equivalent is something like:
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/?????/?????"); // transformation spec?
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] cipherTextBytes = cipher.doFinal(plaintext);
Questions:
The C code takes two keys, JCE takes one. How do I reconcile this? Just append the two into one array? In which order?
What transformation spec (if any!) is equivalent to OpenSSL's des_ede2_ofb64_encrypt? How would I find out, other than by asking strangers on the Internet? ;)
In answer to your last question, you'd find out by reading the documentation on the specific algorithms themselves. The Sun docs do generally assume you already are familiar with the subject matter. In this case, you would know that: triple DES is the application of three independently keyed DES ECB instances in sequence; that the most common way to this is something called DES ede, which means the 1st and 3rd DES instances are run in the encrypt direction but the 2nd DES instance is run in the decrypt direction; that ede3 three means that each DES instance is keyed independently and ede2 means that the 1st and 3rd instances use the same key; that OFB64 means 64-bit output feedback mode.
You should get the the same result with getInstance("DESede/OFB64/NoPadding"), and by making the key1 the 1st 8 bytes of the DESede key, key2 the 2nd, and key1 the 3rd.

How to implement Java 256-bit AES encryption with CBC

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.
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.
Done similar projects in a midlet before, I have following advice for you:
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.
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.
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.
If you are just worried about MITM (man-in-the-middle) attacks, use SSL.

Categories