When I try to encrypt the ciphertext again with the same key, it produces the original plaintext.
Algoritm used is AES with COUNTER MODE. Key and IV remains the same.
Is this the way the algorithm is supposed to behave? And if, what is the use of Cipher.ENCRYTMODE which is to be given as the first parameter of Cipher.init()?
Here is the sample program with which I tested,
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionTest {
public static void main(String[] args) throws Exception {
SecretKeySpec key = null;
IvParameterSpec ivSpec = null;
byte[] keyBytes = "usethiskeyusethiusethiskeyusethi".getBytes();
byte[] ivBytes = "usethisIusethisI".getBytes();
key = new SecretKeySpec(keyBytes, "AES"); //No I18N
ivSpec = new IvParameterSpec(ivBytes);
Cipher AesCipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] byteText = "Your Plain Text Here".getBytes();
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] byteCipherText = AesCipher.doFinal(byteText);
System.out.println("Encrypted : " + new String(byteCipherText));
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] bytePlainText = AesCipher.doFinal(byteCipherText);
System.out.println("Double Encrypted : " + new String(bytePlainText));
}
}
Yes, that is expected behavior. The CTR mode of operation for block ciphers makes a stream cipher out of a block cipher. Since stream ciphers work in a way that they generate a keystream and XOR the keystream with the plaintext to produce the ciphertext:
plaintext XOR AES-CTR(nonce, key) = ciphertext
The XOR operation works in a way where XORing x with a key k twice results in x again:
x ^ k ^ k = x
This is the reason why encryption and decryption are exactly the same operation for block ciphers in CTR mode (sans nonce generation and putting it into the ciphertext).
If you don't want that the encryption and decryption algorithm are the same, then you should use a different mode such as CBC, but there is nothing wrong with this kind of thing.
Beware that for CTR mode to be secure, you have to use a different nonce/IV under the same key for every encryption.
Related
I'm using openssl's aes-256-cfb algorithm (from NodeJS's crypto module).
While both the NodeJS and the Java code successfully encrypt and decrypt data, the ciphertext is different, even when the iv, key and plaintext are all identical.
openssl/NodeJS cipherText:
05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d
Java cipherText:
05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D
I have guessed that openssl's aes-256-cfb translates to Java's AES/CFB/NoPadding.
The ciphertexts both share the same initial 9 bytes, which is odd - I would have expected them to share the first 16 bytes if there had been some difference in the common mode used. (I hope that "common mode" is the collective term for CFB/CBC/CTR/etc.)
Is Java's AES/CFB/NoPadding the correct translation of OpenSSL's aes-256-cfb?
Does the existence of the common first nine bytes imply that Java's AES/CFB/NoPadding is at least using AES256, as opposed to AES128?
If so, what else might account for the differing ciphertext?
Test cases follow:
OpenSSL/NodeJS:
var assert = require('assert');
var crypto = require('crypto');
describe('crypto', function() {
it('should work', function () {
var plainText = new Buffer('a secret plainText which has more than sixteen bytes');
var key = new Buffer('fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210', 'hex');
var iv = new Buffer('0123456789abcdef0123456789abcdef', 'hex');
var cipher = crypto.createCipheriv('aes-256-cfb', key, iv);
var decipher = crypto.createDecipheriv('aes-256-cfb', key, iv);
var cipherText = cipher.update(plainText);
cipher.final(); // no need to use this value with cfb, it seems to always be empty?
assert.equal(plainText.length, cipherText.length);
assert.equal('05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d', cipherText.toString('hex'));
var deciphered = decipher.update(cipherText);
decipher.final(); // no need to use value this with cfb, it seems to always be empty?
assert.equal(plainText, deciphered.toString('utf8'));
});
});
Java/Android:
import android.test.InstrumentationTestCase;
import com.google.protobuf.ByteString;
import bit.Twiddling;
import java.security.AlgorithmParameters;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoTest extends InstrumentationTestCase {
public void test_encrypt_and_decrypt() throws Exception {
byte[] plainText = "a secret message which has more than sixteen bytes".getBytes();
byte[] key = Twiddling.hexStringBEToBytes("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
byte[] iv = Twiddling.hexStringBEToBytes("0123456789abcdef0123456789abcdef");//0123456789abcdef0123456789abcdef");
SecretKeySpec aesSecret = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
Cipher decipher = Cipher.getInstance("AES/CFB/NoPadding");
IvParameterSpec ivps = new IvParameterSpec(iv);
assertEquals(16, iv.length);
cipher.init(Cipher.ENCRYPT_MODE, aesSecret, ivps);
byte[] cipherText = cipher.doFinal(plainText);
assertEquals(plainText.length, cipherText.length);
assertEquals("05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D", Twiddling.bytesToHexStringBE(cipherText));
decipher.init(Cipher.DECRYPT_MODE, aesSecret, ivps);
byte[] deciphered = decipher.doFinal(cipherText);
assertEquals(new String(plainText), new String(deciphered));
}
}
It was a find/replace error - the two plainTexts differ after the first nine bytes.
Java's name for OpenSSL's aes-256-cfb is AES/CFB/NoPadding.
I need to implement AES-CBC encryption algorithm, but the example that I have creates the initialize IV manually:
//initialize IV manually
byte[] ivBytes = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
The AES-CBC that I have:
package xxxx;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES_CBC
{
public static void main(String[] args)
{
try
{
//Lookup a key generator for the AES cipher
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey();
SecretKeySpec keySpec = new
SecretKeySpec(key.getEncoded(), "AES");
//Lookup an instance of a AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//initialize IV manually
byte[] ivBytes = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//create IvParameterSpecobject
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//Initialize the cipher using the secter key
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
//Message to encrypt
String plainText = "This is a secret!";
//Sequence of byte to encrypt
//Encrypt the message
byte[] cipherText = cipher.doFinal(plainText.getBytes());
System.out.println("Resulting Cipher Text:\n");
for(int i=0;i<cipherText.length;i++)
{
System.out.print(cipherText[i] + " ");
}
System.out.println("");
} catch (Exception e)
{
e.printStackTrace();
} } }
Instead of setting IV manually, I would like to use the output of SHA-1
as the initialize IV, as each time I have different integer value I use SHA-1 to get fixed length and reuse it as initialize IV in AES, for example:
input : 10
digest : b1d5781111d84f7b3fe45a0852e59758cd7a87e5
So, AES code needs 16Byte, and SHA-1 produces 20 Bytes, how can I transfer 20B to be fitted as 16 Byte in AES code?
How to reuse :b1d5781111d84f7b3fe45a0852e59758cd7a87e5 instead of 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
It's unclear what you mean by "use the output of SHA-1 as the initialize IV". What information do you hash? It sounds like security through obscurity.
A good IV should be absolutely random and unpredictable in order to be secure.
Just get random bytes from a SecureRandom to use as IV:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
rnd.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParams);
byte[] ciphertext = cipher.doFinal(input.getBytes());
Then store the generated IV in the encrypted output together with the ciphertext.
During decryption, first read in the IV, then the ciphertext, and then decrypt.
I wanted to try my hand at encrypting a file and utilized the following stack overflow response. However, while testing out the initialization vector, I found it only effected the first 16 bytes.
When I pass an empty iv to the decrypt cipher (other than the first 16 bytes) the data was unexpectedly decrypted. [I assume the library isn't broke and that I'm doing something improperly; but it's a scary thought that others may be in the same boat and not realize it.]
Example:
Initial bytes ..... 2222222222222222222222222222222222222222222222222222
Encrypted bytes ... b35b3945cdcd08e2f8a65b353ff754c32a48d9624e16b616d432
Decrypted bytes ... 3c4154f7f33a2edbded5e5af5d3d39b422222222222222222222
Q: Why isn't the entire decryption failing?
Speculation: I suppose I could do the encryption by iterating over the data 16 bytes at a time and updating the iv each round by hashing the prior encrypted 16 byte block. However, that seems to be busy work that I would have expected the library to do. And I would have expected it to have been mentioned by the experts that present implementation guidelines. But I'm just grasping at straws here. For all I know, maybe the security community is only worried about a hack on the first block.
Note: Just now I found a 5.5 year old stack overflow post that identified this same issue; and unfortunately it still does not have a response.
package test;
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import java.util.Formatter;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/*
* Issue: Using "AES/CBC/PKCS5Padding" encryption, the Initialization Vector
* appears to only affect the first block?!?
*
* Example Output
* iv 1e6376d5d1180cf9fcf7c78d7f1f1b96
* bv 00000000000000000000000000000000
* I: 222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
* E: b35b3945cdcd08e2f8a65b353ff754c32a48d9624e16b616d432ee5f78a26aa295d83625634d1048bf2dbb51fc657b7f796b60066129da5e1e7d3c7b51a30c1d962db75ac6666d4b32513c154b47f18eb66f62d7417cfd77f07f81f27f08d7d818e6910ca5849da3e6cff852bc06317e2d51907879598c8d3ae74074f4c27f7b8e2f74ca04d3ed6ac839b819a0f4cb462d0a4d9497cd917b8bd0aafb590ddd593b5b652cf8f642d3b2cd9dc0981dc1c913d52d065a844ea65e72cd7738eee3b488c4304e884109320dc54668ac4659d6014de9cf19422f7f68157d4330478589533571434d07b1939e56259fb8828823361bc912b84dc6ccdd5878b1d05801e0a6ce099bc86f1356fd145338163d59a07f2efdb1a6f91f4a35e6304f2d15d9972b0dda3c2275b5942a7f032ab6f90138
* D: 3c4154f7f33a2edbded5e5af5d3d39b42222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
*/
public class IvBug {
public static void main(String[] args) throws Exception {
// Initialize.
final char[] password = "foo".toCharArray();
final byte[] salt = "bar".getBytes();
byte[] iData = new byte[300];
java.util.Arrays.fill(iData, (byte)0x22); // Make the issue easy to see.
// for (int i=0; i<msg.length; i++) msg[i] = (byte) i; // Alternate fill.
// Perform the test.
SecretKey sKey = generateKey(password,salt);
byte[] iv = generateIv(sKey);
byte[] eData = encrypt(sKey, iData, iv);
byte[] badIv = new byte[iv.length]; // Discard initialization vector.
byte[] dData = decrypt(sKey, eData, badIv);
// Display the results.
System.out.println("iv " + hexStr(iv));
System.out.println("bv " + hexStr(badIv));
System.out.println("I: " + hexStr(iData)); // Initial
System.out.println("E: " + hexStr(eData)); // Encrypted
System.out.println("D: " + hexStr(dData)); // Decrypted
}
static SecretKey generateKey(char[] password, byte[] salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
static byte[] generateIv(SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameters params = cipher.getParameters();
return params.getParameterSpec(IvParameterSpec.class).getIV();
}
static byte[] encrypt(SecretKey key, byte[] data, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(data);
}
static byte[] decrypt(SecretKey key, byte[] data, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(data);
}
static String hexStr(byte[] bytes) {
try (Formatter formatter = new Formatter()) {
for (byte b : bytes) formatter.format("%02x", b);
return formatter.toString();
}
}
}
You seem to misunderstand why we use an IV. The behavior you have observed is the intended behavior. Consider this diagram of how CBC mode works taken from Wikipedia:
When we decrypt, the IV is only used to derive the first block of encrypted data. Notice that, in the diagram, we don't actually need the IV to decrypt any subsequent blocks.
I feel like you might (mistakenly) be viewing the IV as relevant to the security of the cipher. This is not the case. The key is the secret value. An IV does not need to be kept secret. The only requirement for an IV is that it be cryptographically random and unpredictable.
I'm trying to encrypt a file using AES in Java, but it's not working. I put in some prints to see where it failed, the first two are printing but not the third. I checked that the key and IV are the right sizes, 256 bits and 128 bits respectively. If they are both valid I don't know what else could be going wrong. Are there any glaring mistakes in my code? I haven't done this before. Any help is greatly appreciated!
public static String encryptAES(byte[] data, byte[] key, byte[] iv) throws Exception {
Key k = new SecretKeySpec(key, "AES");
System.out.println("key set");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
System.out.println("cipher created");
c.init(Cipher.ENCRYPT_MODE, k, new IvParameterSpec(iv));
System.out.println("cipher initialised");
byte[] encryptedDataBytes = c.doFinal(data);
String encryptedData = Base64.getEncoder().encodeToString(encryptedDataBytes);
return encryptedData;
}
I got an exception in the following code for AES algorithm in java.
Code decryptes an encrypted string and returns the original string.
Plz help me to fix this.
Code:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
javax.crypto.BadPaddingException: Given final block not properly padded at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..)
According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):
Cipher objects are obtained by using
one of the Cipher getInstance() static
factory methods. Here, the algorithm
name is slightly different than with
other engine classes, in that it
specifies not just an algorithm name,
but a "transformation". A
transformation is a string that
describes the operation (or set of
operations) to be performed on the
given input to produce some output. A
transformation always includes the
name of a cryptographic algorithm
(e.g., DES), and may be followed by a
mode and padding scheme.
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding"
"DES"
If just a transformation name is specified, the system will
determine if there is an
implementation of the requested
transformation available in the
environment, and if there is more than
one, returns there is a preferred one.
If both a transformation name and a
package provider are specified, the
system will determine if there is an
implementation of the requested
transformation in the package
requested, and throw an exception if
there is not.
If no mode or padding is specified,
provider-specific default values for
the mode and padding scheme are used.
For example, the SunJCE provider uses
ECB as the default mode, and
PKCS5Padding as the default padding
scheme for DES, DES-EDE and Blowfish
ciphers. This means that in the case
of the SunJCE provider:
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
Using modes
such as CFB and OFB, block ciphers can
encrypt data in units smaller than the
cipher's actual block size. When
requesting such a mode, you may
optionally specify the number of bits
to be processed at a time by appending
this number to the mode name as shown
in the "DES/CFB8/NoPadding" and
"DES/OFB32/PKCS5Padding"
transformations. If no such number is
specified, a provider-specific default
is used. (For example, the SunJCE
provider uses a default of 64 bits for
DES.) Thus, block ciphers can be
turned into byte-oriented stream
ciphers by using an 8 bit mode such as
CFB8 or OFB8.
Appendix A of this document contains a
list of standard names that can be
used to specify the algorithm name,
mode, and padding scheme components of
a transformation.
The objects returned by factory
methods are uninitialized, and must be
initialized before they become usable.
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16.
Have you checked the length of buf[]?
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
Your exception handling is incorrect.
Furthermore, your code runs without exceptions for me.