I am trying to write a native module to generate/store AES keys for realm encryption. For the test application I wrote (when I used 512 key size), I get the following exception:
Exception in thread "main" java.security.InvalidParameterException: Wrong keysize: must be equal to 128, 192 or 256
at com.sun.crypto.provider.AESKeyGenerator.engineInit(AESKeyGenerator.java:93)
at javax.crypto.KeyGenerator.init(KeyGenerator.java:517)
at javax.crypto.KeyGenerator.init(KeyGenerator.java:494)
The code I am using is:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(512);
SecretKey secretKey = keyGen.generateKey();
final int AES_KEYLENGTH = 512;
byte[] iv = new byte[AES_KEYLENGTH / 8];
SecureRandom prng = new SecureRandom();
prng.nextBytes(iv);
Cipher aesCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS7PADDING");
Can someone please let me know what am I doing wrong here?
Thanks.
If you want to store a 512-bit key "as an AES key", you can just store two 256-bit AES keys, call them LEFT and RIGHT, for example, and concatenate them before passing to Realm.
As others have pointed out in comments, AES does not have 512-bit keys. It may say they are using AES-256 encryption, but I doubt it says anywhere that they are using 512-bit, AES keys.
I suspect, as #Artjom B. mentions in the comment, that it really is a pair of 256-bit keys, one for encryption and one for mac (e.g. HMAC).
If you want to create a 512-bit key, you can just use SecureRandom. There is nothing special about KeyGenerator that SecureRandom cannot provide for AES keys. You just want to be sure that you are using cryptographically strong random data.
Related
I would like to use AES256 to encrypt a text, I'd like to use my email test#gmail.com as the key to encrypt it.
This is what I tried:
String key = "test#gmail.com";
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
byte iv[] = SOME_RANDOM_32_BYTES;
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] encryptedResult = cipher.doFinal(text.getBytes("UTF-8"));
When I run above code, I got InvalidKeyException:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
I checked on internet, the reason is my key is not 128/192/256 bits. My question is, how can I generate a 256bits(32bytes) key from my email string test#gmail.com ?
You can hash your key string (test#gmail.com) to a 256bit value using SHA256.
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(yourEmail.getBytes());
byte[] encryptionKey = md.digest();
There can be multiple reasons for this. One of them is below.
Usually this error comes when you don't have the update policy in your JRE.
Java by default provide AES with 128, for 256 we have to put new policies provided by Java.
You should not bake your own crypto! (Unless you are very knowledgeable on the matter, that is.)
You should use an existing (and audited) encryption library.
Also, you should not use a guessable string such as your e-mail address as a password. Please, look for advice on how to choose a good password.
Now that I have said this, here are more details.
The proper way to implement password based encryption is to use a KDF (Key Derivation Function) to generate an encryption key from your password. Here are a few KDFs that you can use for this task: Argon2, Scrypt, Bcrypt and PBKDF2.
Key derivation functions include mechanisms to defend against know attacks such as rainbow tables and dictionary attacks, notably a "salt" and a work factor. Modern KDFs such as Argon2 also attempt to prevent attackers from gaining an advantage by using hardware more suitable to the task.
Generally speaking, here how this is used:
Select a work factor (the largest you can afford)
Generate the salt using a CSPRNG
Generate the encryption key and a MAC secret using your chosen KDF with the password, salt and work factor.
Generate an IV (initialization vector) using a CSPRNG
Encrypt the data to be protected using the generated encryption key.
Compute the MAC of the encrypted message using the generated secret.
Serialize the salt, the work factor, the computed MAC and the encrypted data. (Optionally, identifiers indicating what are the chosen KDF, encryption scheme and MAC should also be included if these are not fixed.)
Your encrypted message is the serialized data produced in step 7. Get any of the steps wrong (and that is easy) and your encryption code will probably break in horrible ways.
Perhaps now you get a sense of why you should use an existing library?
Note: the current best practice is to use AEAD (Authenticated Encryption with Associated Data) instead of encrypt-then-MAC as described above. Look this up if you are interested: I am not going to discuss this here.
I'm confused as to why I need to specify an algorithm such as "AES" when generating a key for encryption, e.g...
KeyGenerator kg = KeyGenerator.getInstance("AES");
It clearly is not used for specifying the size of the key since AES keys can be 128, 192, or 256-bits. That part would be done via init()...
kg.init(256, new SecureRandom());
SecretKey key = kg.generateKey();
For what it's worth, the above example code was borrowed from http://android-developers.blogspot.de/2013/02/using-cryptography-to-store-credentials.html
Furthermore, NIST FIPS-197 states...
No weak or semi-weak keys have been identified for the AES algorithm,
and there is no restriction on key selection.
...so that would lead me to believe that any 128, 192, or 256 bits could be used as a key.
Clearly, specifying "AES" when I get a cipher instance, e.g...
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
...is necessary to indicate the cipher algorithm to be use. I just don't get what the purpose of specifying it for the key generation does.
Thanks.
As mentioned in the comments, other keys than AES may require more attention. And it is best to have a symmetrical method for DES and AES so you can switch between the algorithms.
Furthermore, not all cryptographic providers may create keys in memory. The Java JCA is also compatible with hardware key stores. For PKCS#11 providers (for instance) it is required to know the type of the key when it is being generated.
I'm tring to encrypt/decrypt my files using AES. I followed this tutorial to encrypt my data, but I modified the code a little bit, so that I could use the same key to encrypt many files.
In addition to encrypting my files, my AES key is also saved using RSA (this page , saveKey() method).
I encrypted files on my PC, and tried to decrypt them on Android. However, I always got BadPaddingException: pad block corrupted. I printed out the AES keys, and found out that with the same private key, the decrypted AES keys were different on PC and Android.
It worked fine if I decrypt the same files on PC.
Why?!
Is there anything wrong with Android's cipher?! Help needed.
your RSA padding cipher could be on the wrong padding scheme
try this?
pkCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
The code you copied is wrong. It may or may not work depending on Android version. My guess is that it doesn't on newer ones. The part which converts from seed to raw key is flawed (see below): SecureSeed.setSeed() is not guaranteed to set the random generator state, it just adds to it. What this means is that you are not guaranteed to get the same key. To reliably get the same key based on a password, you need to use PBE (Password Based Encryption)
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
// this is wrong!
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
Generally, first make sure you can reliably encrypt/decrypt using AES, then you might move on to using RSA. You might want to tell us what you are trying to achieve, you might be going at it the wrong way. Inventing your own cryptographic protocol is rarely a good idea.
I'm trying to understand this comment from AES implementation code:
/**
* This program generates a AES key, retrieves its raw bytes, and
* then reinstantiates a AES key from the key bytes.
* The reinstantiated key is used to initialize a AES cipher for
* encryption and decryption.
*/
I don't understand following points:
What does it mean by raw bytes of AES key?
What does it mean by reinstantiates a AES key from the key bytes?
I think you are reading this web page,
http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html
We have code that generates AES keys according to this example also but I think the author is a little bit confused. It generates keys like this,
kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
skey is already an AES key you can use. The second step does nothing. If you look inside skey and skeySpec, they are identical.
So you can ignore the explanation. It doesn't make it any more clear.
Raw bytes probably refers to the storage representation of the key object.
Usually you create a key object and then get a form in which to store it (have a look at java.security.Key#getEncoded(). That storage/exchange representation is a byte array without encoding information - therefore "raw bytes".
These raw bytes then can be used to instantiate the key object again.
Unclear to me is why the program reinstantiates the key object instead of using the one it created earlier.
That probably can only be answered by seeing the code of the program.
AES: Advanced Encyption Standard
The key is a string. So this seems to generate a string.
It likely then converts it to an array of bytes (perhaps for storage).
It then recreates the string from the array of bytes.
when you want to encrypt something dont you want the key to decrypt to be decided by you and not generator by some random number generator ?
i see this code in stackoverflow post. but i dont want the key to be generated randomly i want to the user to be asked to enter the key and on that bases the encryption should happen..
any suggestions how should i modify the code ?
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
The whole idea of encryption is that noone except the needed parties can ever deduce the key since the key is the only secret.
If you choose keys yourself you're likely to follow some habitual pattern, so if you compomise one key you expose that pattern and the attacker can use that information to simplify finding other keys you use. Using a good random number generator eliminates this possibility and makes the encryption much more efficient.
The code you show doesn't generate a random key. The generated key is a function of the password, and will be exactly the same every time a given password is used.
In this case, the user should be asked to enter the password. That password is used as the seed for an algorithm that deterministically produces a string of bytes which can be used as a cryptographic key.
There is something that should be chosen randomly for each operation: the initialization vector used for ciphers in CBC mode. This produces different ciphertexts even when the same key is used to encrypt the same plaintext.