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.
Related
I see many examples where the secret key is generated this way:
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_LEN);
SecretKey secretKey = generator.generateKey();
I'm in doubt if there's a difference (even conceptual) with the following:
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
Stated that both methods are 3 lines, is there some practical impact in preferring the first over the second?
Thanks
You could look at the actual source code for generateKey() to see the difference but ultimately they are both going to do the same steps to generate an AES key. I would argue the latter
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
is a little more brittle for the average coder, requiring them to understand the SecureRandom class. If you eliminate the second line altogether the code runs just fine with an all zero key, an obvious vulnerability that's also easy for an attacker to check. Also, using generateKey() can produce a properly formatted key if the algorithm has some particular requirements. For example, the now obsolete DES and Triple DES algorithms had a weird parity bit in each byte that some DES implementations expected to see.
There are many reasons why you would want to use the KeyGenerator method, which was designed for the purpose:
readability: generateKey tells you exactly what the algorithm is doing;
portability: e.g. when choosing a different algorithm);
correctness: SecretKeySpec may not validate the key entirely;
security: you would leave the key material exposed in the material variable, which may not be cleared or even garbage collected after the key is not required anymore;
hardware support: importing key material is often not supported for hardware devices; keys should be generated on the device itself (using a specialized KeyFactory implementation).
There is no reason at all to use the second method. If you want to use a very specific random number generator for generating the key then you can use one of the specialized init methods but beware that this may not be compatible with hardware devices.
The two code examples seem to be doing the same and as already answered usually they are.
However using security devices (e. g. HSM, smartcards or other crypto devices) by default they won't allow exposing the key bytes so you would be able to generate a key from arbitritrary byte array only in very limited cases..
i've got the following problem. My application is divided in two different parts: 1) the first part encrypts some data using AES/CBC (Java), 2) the second part must retrieve the data and decrypt (Android).
To generate the secret key i use the following code
SecureRandom saltRand = new SecureRandom(new byte[] { 1, 2, 3, 4 });
byte[] salt = new byte[16];
saltRand.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 1024, 128);
SecretKey key = factory.generateSecret(spec);
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(key.getEncoded());
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr);
sksCrypt = new SecretKeySpec((kg.generateKey()).getEncoded(), "AES");
My program doesn't need differents "source key" (the string password), however it needs to compute the same secret key as long as source key is the same. unfortunately, the key generated by the two parts of the program are different and the decryption phase fails.
Any suggestion on how to solve this issue?
You are using a random key generator to generate a secret key from given input key material. Key derivation functions are functions that derive keys from secrets. There are password based key derivation functions such as PBKDF2 which use a password (plus a salt and a specific iteration count) as secret input. And there are key based key derivation functions such as HKDF that use a key and possibly a label or other output key specific information as input material.
Java does provide you with a set of PBKDF's, PBKDF1 and PBKDF2 where PBKDF2 is the newer one that you are currently using. Unfortunately it doesn't provide a KBKDF out of the box. You would need to use the (lightweight) Bouncy Castle API to do provide this kind of functionality. I know because I provided the initial implementation of the various KBKDF's for Bouncy.
Unfortunately using SecureRandom as replacement KBKDF doesn't work, as you've found out. The SHA1PRNG algorithm is not specified well; it is a function that depends on the SHA-1 secure hash function, but that's all. So implementations can and do differ, for instance between Android (which was loosely based on GNU classpath) and Oracle's Java. SHA1PRNG may or may not rely entirely on the seed. And in newer Android versions it may even be replaced by something entirely different.
As you only derive a single key from your input key material, you might as well wrap key.getEncoded() with SecretKeySpec directly and use that as a key. There is no need to perform an additional key generation at all; you already derived a key using PBKDF2. The additional wrapping doesn't do anything with the keying material. It may just be required to set the algorithm to, e.g., "AES".
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.
To generate AES key in java i had used as fallow
public byte[] GenerateAESKey() {
// Generate a new AES key
SecretKey key = null;
try {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
key = keygen.generateKey();
return key.getEncoded();
} catch (NoSuchAlgorithmException e) {
return null;
}
}
In iOS I tried it in this way using iOS-Crypto-API.
id<SecretKey> key=[[[AESKeyGenerator alloc] init] generate:128 onError:&error];
NSLog(#"Key: %# ",key.key);
When I print both keys looks different, Is that above generated iOS Key is correct or not? Help me to solve this out.
AES keys are essentially bit strings. So when you generate a new AES key, it will be created using some randomness of the system or the library. Even if you generate two keys in the same library, they will be different as collisions for 128-bit keys are really unlikely to happen.
For encryption and decryption you need the same key at both ends, so you need to transport it in some way. Depending on your system, you could do this at system setup (key embedded in the source code or in some way static) or using asymmetric encryption to send the key from one machine to the next (possibly secured with Diffie-Hellman Key Exchange).
You will have to encode the key in some way that is acceptable by the library. Popular encodings are Base 64 and Hex. You may need to convert between encodings.
Symmetric keys are, basically, byte arrays which are obtained from random source. The length of the keys depends on the cipher algorithm that is going to use such keys.
When you generate a secret key (i.e., a symmetric key) you get a random byte array. If you generate it again, you will get a different one.
Therefore, it is totally normal to get two different keys from two different key generations.
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.