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
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);
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.
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.
When encrypting data in java as following:
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(rawKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key, sr);
// why the sr is necessary to init a Cipher object?
byte data[] = str.getBytes();
byte[] encryptedData = cipher.doFinal(data);
Why the a SecureRandom object sr is necessary to init a Cipher object? As for the decryption side there is also need an SecureRandom object which is not the same as the one generated in the encryption size. Is theseSecureRandom objects just for parity digit?
The SecureRandom object is optional. If you don't select one, Java will use a default source of randomness. See the documentation of this method to understand how one is selected.
The Cipher Javadocs explains the purpose of this argument:
If this cipher (including its underlying feedback or padding scheme) requires any random bytes (e.g., for parameter generation), it will get them from random.
So, in your particular case, you are probably not making use of this item at all. Nothing in your DES usage will require random data (since you appear to be using ECB mode). If you were using CBC-mode, then I assume the IV would be randomly generated from the SecureRandom object.
I have web services implemented that comunicate between client and server with strings. The problem I'm getting is with the conversion of the encripted byte array to string since I can't convert it back to the original content on server side.
KeyPairGenerator keyGen;
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
String publicKeyPath = new String("publicKeys.txt");
publickey = key.getPublic()
byte[] pubEncoded = key.getPublic().getEncoded();
FileOutputStream fout = new FileOutputStream(publicKeyPath);
fout.write(pubEncoded);
fout.flush();
fout.close();
String privateKeyPath = new String("privateKeys.txt");
byte[] privEncoded = key.getPrivate().getEncoded();
fout = new FileOutputStream(privateKeyPath);
fout.write(privEncoded);
fout.flush();
fout.close();
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
And on each method in client:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publickey);
byte[] cipherText = cipher.doFinal(str.getBytes());
port.callX(chiperText.toString());
On server side:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] newPlainText = cipher.doFinal(arg.getBytes());
This gives the padding problem of "Data must start with a zero"
There is only one KeyPair generated, for debug, I tried to encrypt and decript on the same function, and the problem relies on the conversions from byte[] to String.
I don't really wan't to change the passing of arguments to other type since operations are auto generated and all code is to strings. I've tried with different "UTF-8" and "UTF-16Le" but none works :S
Any idea?
Encrypting data directly with asymmetric keys isnt a good idea and it does not work. RSA keys cannot encrypt data bigger than its key length. So, the right way of doing that would be to generate a symmetric key and encrypt the data with the symmetric key and inturn encrypt the symmetric key with the asymmetric key. So, you need to send the encrypted data and the encrypted key both to the other party.
If you try encrypting the data directly with the asymmetric key then your byte[] may not contain the correct data or it may contain nothing at all. This happens if your data is bigger than the key length. For data smaller the key length it works fine but not with bigger data.