Following on this question and its answer, I am creating an application that given a password string, will convert a plaintext and store its ciphertext, the salt generated and the initialization vector in a text file.
In the following code :
public String decrypt(CryptGroup cp) throws Exception {
String plaintext = null;
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, cp.getSalt(), ITERATIONS, KEY_SIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(cp.getIv()));
plaintext = new String(cipher.doFinal(cp.getCipher()), "UTF-8");
return plaintext;
}
public CryptGroup encrypt(String plainText) throws Exception {
byte[] salt = generateSalt();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_SIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(plainText.getBytes("UTF-8"));
return new CryptGroup(ciphertext, salt, iv);
}
The CryptGroup object contains those 3 parameters (ciphertext, salt, iv : byte arrays for that matter).
Is it safe to store the initialization vector?
The answer in that question clearly states that the salt doesn't need to be secret, obviously the ciphertext can be also available, but what about the iv parameter?
Edit
If it is not safe to share, is it possible to retrieve the original iv from the salt alone?
Yes, IV's can be public information. You can use a calculation as IV, as long as you never use the combination of key and IV twice. In other words, you should be able to share just the salt, as long as you change the salt for each encryption.
Furthermore, for CBC it is required that IV "looks like random" to an attacker, especially when used for the same key. So a usual scheme is to use some output of PBKDF2 as IV data. Those particular bits should of course not be used to create the key as well, but it is OK to split the output size.
This has some drawbacks as PBKDF2 will use many more rounds if you request more than 160 bits of information (for SHA1). So you may concatenate the output of PBKDF2 and a counter (0 for the key, 1 for the IV) and use e.g. SHA256 to generate a 128 bit key (the 16 leftmost bytes) and 128 bit IV (the 16 rightmost bytes).
Let's explore some variants to this scheme:
You could also use a different hash such as SHA-512 to create a larger output and split it to get a key and IV, but beware that such a function may not be available everywhere. Java 8 should have "PBKDF2WithHmacSHA512" though (for the SecretKeyFactory).
You can also generate one PBKDF2 output and then use HKDF or HKDF-Expand to derive both a key and IV. Trouble is that HKDF / HKDF-Expand is not directly available from Java. Bouncy Castle does have that method though, because I send in an implementation of various KDF's.
Yet another way is to generate a new IV using SecureRandom, but in that case you need to store both the salt and IV. This might be useful if you need to encrypt multiple messages using the same key. In that case you can generate and store an IV for each separate message. This is a fine method if you are able to simply store 16 additional bytes.
In principle you could also use an all zero IV as long as you never reuse the same key (i.e. never reuse the same password/salt combination). However, in that case you might want to use AES-256 to avoid multi-target attacks.
Related
Let me explain shortly. I have this encryptor in python:
It uses PyCrypto library.
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Util import Counter
iv = Random.new().read(8)
encryptor = AES.new(
CRYPTOGRAPHY_KEY, // 32 bytes
AES.MODE_CTR,
counter=Counter.new(64, prefix=iv),
)
and I want to have a decryptor for it in java.
I wrote this code but it raises java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long.
SecretKeySpec key = new SecretKeySpec(KEY, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
P.S. I should mention that I'm not an experienced Java developer.
Update.
The problem was with initialization vector.
Special thanks to #Andy for his time.
Solution:
byte[] nonceAndCounter = new byte[16];
System.arraycopy(iv, 0, nonceAndCounter, 0, 8);
nonceAndCounter[15] = (byte) 1; // PyCrypto's default initial value is 1
IvParameterSpec ivSpec = new IvParameterSpec(nonceAndCounter);
If you are trying to use AES-256 (you are using 32 bytes key), then you need to have Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files installed. And this has to be installed wherever this application needs to run. If you plan to distribute this application and thus you don't have control over possible java runtimes, then you have to use other libraries (like Bouncy Castle) through their own API, not through JCE API. (i.e. using Bouncy Castle as a provider like "BC" in JCE would result in the same problem.)
EDIT: First you said you are getting invalid key size exception, now changed the question. For the java.security.InvalidAlgorithmParameterException case, the problem is AES-256 has block size of 16, not 32. 256 represents the key size, not block size. AES (128, 192, 256) always has block size of 128. Thus, the iv must be 16 bytes.
EDIT 2: Not the cause of this exception but another possible problem with your code is, where do you get the iv in Java? Do you generate it random as you do in Python? That would be a big mistake since you have to use the same IV during decryption to make it work. Keep this in mind.
I believe you are encountering an issue where Java expects the IV to be fully-formed (16 bytes) when using AES/CTR/NoPadding while Python accepts an 8 byte prefix and generates its own counter in the other 64 bits (8 bytes).
I have example code for cross-platform compatibility in this answer. The relevant section is:
byte[] cipher_key = org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210");
final int HALF_BLOCK = 64;
byte[] salt = org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEF");
byte[] nonceAndCounter = new byte[16];
System.arraycopy(salt, 0, nonceAndCounter, 0, ((int) (HALF_BLOCK / 8)));
IvParameterSpec iv = new IvParameterSpec(nonceAndCounter);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
SecretKeySpec key = new SecretKeySpec(cipher_key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
Im new to cryptography in Java and I am trying to write a program to encrypt and decrypt a phrase using DES symmetric cipher, based on CBC mode of operation.
Can anyone tell me how to go about creating an 8-byte initialization vector and how to cast the new IV into AlgorithmParameterSpec class?
Also, which packages should I import?
Edit: Right now I have these lines:SecureRandom sr = new SecureRandom(); //create new secure random
byte [] iv = new byte[8]; //create an array of 8 bytes
sr.nextBytes(iv); //create random bytes to be used for the IV (?) Not too sure.
IvParameterSpec IV = new IvParameterSpec(iv); //creating the IV
Is my above approach correct?
Thanks.
Yes. Till Now You are right.
the Class IvParameterSpec is used to pass Initial Vector to Class Cipher
After this create a Cipher as below
Cipher cipherInstance = Cipher.getInstance("DES/CBC/NoPadding");
here DES/CBC/NoPadding is used because you are useing DES encryption in CBC mode.
Next is initializing it.
cipherInstance.init(Cipher.DECRYPT_MODE, Key, ivParameterSpec);
Parameters are:
1st is mode of encryption either Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
2nd is Secret Key You have generated using Class SecretKey
3rd is generated ivParameterSpec
and last line is
outData = cipherInstance.doFinal(input);
if mode is Decrypt it will return decrypted data and if mode is Encrypt it will return encrypted data.
Last but important catch all the Exceptions properly.
And you are done
I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).
I'm working on some interoperable code for encrypting/decrypting strings between Java and node.js and have managed to get node.js to decrypt what Java has encrypted with this being the final part to successful decryption: the secret key.
To derive a secret key in Java, we write:
private static Key deriveSecretKey(String secretKeyAlgorithm, String secretKey, String salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), char2byte(salt), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), secretKeyAlgorithm);
return secret;
}
Notice the key length passed to PBEKeySpec() is 128 here. In node.js, however, I get an "Invalid key length" if I try to use 128 and actually have to use 16 here instead:
crypto.pbkdf2(key_value, salt_value, 65536, 16, function(err, key) {
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
// decipher.setAutoPadding(false);
var decoded = decipher.update(ciphertext, 'binary', 'utf8');
decoded += decipher.final('utf8');
console.log('Result: ' + decoded);
});
Console output:
Result: Super secret stuff -- right here.
Curious as to why the difference when specifying key lengths between these two functions. Thanks!
Normally, key sizes are defined in bits. However, most cryptographic libraries don't handle bit sizes that cannot be divided by 8 particularly well - the output is almost always in octets (8-bit bytes). So it is up to the designer of the API if the user has to specify the size in bits, or in the number of octets in the octet string (byte array).
The only way to really know why bits or bytes are being chosen is to ask the person who designed the library. In my own code, I do try to keep to (ad-hoc) standards - so bits for key sizes. If it's unclear from the context which is which, it is probably best to use names such as blockSizeBits or blockSizeBytes. Documentation may be of help too of course, but using specific identifiers is best in my opinion.
I have the following decryption algorithm implemented in Java. Is information "AES128" enough for other people to implement it in other languages or a more accurate definition is required?
// input data
byte[] secretKey = { ... };
byte[] encryptedData = { ... };
// decryption
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(secretKey);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, random);
SecretKey sk = keyGen.generateKey();
SecretKeySpec keySpec = new SecretKeySpec(sk.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec IV16 = new IvParameterSpec(keySpec.getEncoded());
cipher.init(Cipher.DECRYPT_MODE, keySpec, IV16);
byte[] decrypted = cipher.doFinal(encryptedData);
The name of the encryption algorithm itself is AES-128. It's being used in the cipher block chaining mode (this describes how to connect the encryption of multiple sequential blocks), with PKCS #5 padding (this describes how to pad messages up to the size of a full block). Other languages might have different techniques for specifying mode and padding.
AES is the algo, and 128bit the key size. I would say it's pretty clear ...
Essential properties of that algorithm are:
AES
128-bit key
CBC blocks
PKCS-5 aka PKCS-7
Also the following custom tactics used:
custom IV generation from a pass phrase used (bad style)
custom key generation from a pass phrase
Changing any point, changes everything: bytes generated and decryption code.