There were many questions about AES, but I have the following problem.
I'm currently using the following AES implementation to encrypt data
byte [] PRFkey = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
byte [] plaintext = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
SecretKeySpec encryptionKey=new SecretKeySpec(PRFkey, "AES");
Cipher cipher=Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
byte[] encryptedData=cipher.doFinal(plaintext);
And it turned out that result is 32-bytes (256 bits). So I'm using AES-256. This implementation is to slow for me. How can I switch to AES-128? I don't need any padding or mode of operation or key hashing.
Thank you in advance.
You are already using 128-bit AES. This is determined by the length of the key you pass to Cipher.init(), which is sixteen bytes (128 bits) in your example.
The size of your output will depend upon your padding mode (and the size of the input data). Since you neglected to specify an operation mode or padding, your JCE provider has likely defaulted to "AES/ECB/PKCS5Padding". PKCS #5 padding will always add one additional block to the size of your output, hence you received 32 bytes of output and not 16 bytes.
Never allow your provider to select default values for you. Instead, specify the mode and padding explicitly:
Cipher cipher=Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
byte[] encryptedData=cipher.doFinal(plaintext); // will be 16 bytes
Just remember that if you specify no padding, your input data must always be an exact multiple of 16 bytes. Also note that ECB-mode has the property that identical plaintext values will produce identical ciphertext values. This is rarely desirable in a cryptographic system.
A similar question was asked on stack overflow here and will help provide you with the info you are looking for. An important point is that The JCA Reference Guide says that:
(Creating a Cipher Object) 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.getInstance("DES") and Cipher.getInstance("DES/ECB/PKCS5Padding") are equivalent statements.
Thus since you are only specifying "AES" as the transformation, the default cipher for AES for Oracle JDK 7 is "AES/ECB/PKCS5Padding", link.
Additionally, the Cipher class defines the transformation "AES/ECB/NoPadding" that will get you a non-padded 16 byte value for your encryptedData. ECB is the default mode for AES, you could also choose "AES/CBC/NoPadding".
Related
I have created a Cipher using the RSA algorithm and I have a problem when I want to get the block size used. The Cipher is declared with the following snippet :
encrypt=Cipher.getInstance("RSA");
encrypt.init(Cipher.ENCRYPT_MODE,myPublicKey);
When I try to get the block size with encrypt.getBlockSize(), I get "0" which, according to the Javadoc, is the answer when the algorithm used isn't a block cipher, which is not the case of the RSA algorithm.
In debug mode, if I dig into the Cipher, I can see in the 'spi' member that a buffer exists, and it's size seems to correspond to the actual block size.
Does anybody know why getBlockSize() returns "0" instead of the spi's buffer size?
RSA is not considered a block cipher. Block ciphers are symmetric, using the same key to encrypt and decrypt. Unlike AES, where the block size is fixed regardless of the key size, the output of an RSA encryption or signature operation will vary depending on the key size.
If you want to know the size of an RSA cipher text, examine the modulus of the key to be used.
The interface of the Cipher class does not reveal the allowed input data size and it depends in a non-trivial way from the modulus bit length and applied padding algorithm.
There are the options:
[Sun Java] encrypting a block with more than modulus length will raise a IllegalBlockSizeException with a message "Data must not be longer than [n] bytes", may be parsed.
[Sun Java] Use reflection to access the length of member buffer of Cipher implementation class RSACipher
[Common] Use a search algorithm to evaluate the maximum allowed input data size, not raising an IllegalBlockSizeException.
I'm using javax.crypto to do some cryptographic operations in my application. I use AES for encryption/decryption like this:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] cipherText = cipher.doFinal(plaintext);
byte[] iv = cipher.getIV(); //The problematic IV
The generated IV is prepended to the ciphertext after the enncryption.
The Java specification clearly says that the IV must be generated automatically if its is not provided to cipher.init():
If this cipher requires any algorithm parameters that cannot be derived from the given key, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values)
But sometimes I end up with ciphertexts that don't seem very random, such as this one (in base64):
AAAAAAAAAAAAAAAAAAAAAOmI9Qh1fMiG6HV3tKZK3q5sCruaPdYqYnoUOM00rs6YZY3EvecYfR6vTHzZqk7ugknR9ZMipedYWJB1YOLmSYg=
The bunch of A characters at the front is the IV. The IV is actually 16 zero-bytes.
Most of the time, the library generates proper, random IVs, but sometimes, it just pops out zeros. Why is this happening?
Some (most?) providers simply use a zero-byte filled IV as their default. My emphasis of your quote:
If this cipher requires any algorithm parameters that cannot be derived from the given key, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values)
When you look to the front of your ciphertext, you see that it starts with a bunch of "A" characters. It's Base 64 for 0x00 bytes.
If you want to make sure that you have a random IV, you have to generate it yourself:
SecureRandom r = new SecureRandom();
byte[] ivBytes = new byte[16];
r.nextBytes(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));
The IV is not secure. It is prepended to the cyphertext, so any attacker only has to read the cyphertext and look at the first 16 bytes to see what it is.
It is not a good idea to always use the same IV -- it effectively turns the first block of your actual cyphertext into ECB mode, which is insecure.
I recommend generating your own random IV. You do not need to use a cryptographically secure RNG, since an attacker can see it anyway. You just need to avoid using the same IV for different messages. Even something simple like 8 bytes of random with an 8 byte (64 bit) counter will be enough to avoid the ECB effect. Change the key before the counter rolls over, and reset the counter to 0 when you change the key.
I am using AES-256 encryption with CFB mode. I have to use 32 byte key.
But I am little bit confused about the initialization vector.
How many bytes of initialization vector should be used with AES-256 ?
Is it 16 bytes or 32 bytes ?
Any code example will be appreciated.
TL;DR: AES in CFB mode requires a unique (but not necessarily randomized) IV of 16 bytes.
The size of the IV depends on the mode, but typically it is the same size as the block size, which for AES is always 16 bytes. There are modes that differ from this, notably GCM mode which has a default size of 12 bytes but can take any sized IV - although keeping to the default is highly recommended.
The old school modes such as CBC and CFB however simply require an IV of the same size as the block size. Even CTR commonly requires 16 bytes, although in principle the IV can be any size less than 16, in which case it is (again, commonly) right padded with zero valued bytes. Note that CTR is often initialized with an initial counter value which means you must make sure that the counter is not repeated yourself.
The block size of AES is 16 bytes, whatever the key size. Saying that you have AES-256 and a key of 32 bytes is superfluous information. For AES-256 the key size must be 256 bits or 32 bytes.
The IV for CFB mode - as stated earlier - must always be 16 bytes as AES is a 128 bit block cipher. AES is restricted with regards to the block size compared with the Rijndael cipher. Rijndael may be configured with different block sizes.
Could you help me to point out what is the default RSA padding.
Precisely, if I create cipher instance as below, sure java is using some sort of padding as encrypted text bytes length always shows 256 bytes for 2048 RSA key irrespective of plain text is one characters or 10 characters.
Cipher.getInstance("RSA")
I wanted to know what is default padding java use internally if no padding is specified in Cipher.getInstance("RSA"). is that PKCS#1 v 1.5?
Thanks,
Sam
It's identical to "RSA/ECB/PKCS1Padding" where ECB is a bit of a misnomer, as it does not implement a block cipher mode of operation (it doesn't handle plaintext larger than the "block size"). "RSA/None/PKCS1Padding" would have been a better name or "RSA/None/RSASSA-PKCS1-v1_5" as your guess about the padding mechanism is correct.
This means that it uses a older mode of encryption; OAEP is more resistant against attacks and contains a security proof. Unfortunately OAEP can of course not be made the new default because all existing ciphertext would not decrypt anymore. This is one of the reasons why using defaults is stupid in the first place.
PKCS#1 v1.5 padding also means that the input is restricted to a maximum of the key size minus 11 bytes. Note that the size of the resulting ciphertext is always identical to the key size in PKCS#1; even if the resulting integer is smaller it will be left padded with zero bytes. I'm assuming here that the key size is a multiple of 8.
You should not rely on defaults for the algorithm specification. It makes the code harder to understand and defaults may indeed differ per provider (although most will try to follow Oracle's lead, to avoid incompatibilities). So use this only to understand which algorithm is configured in existing code. The only place where a platform default makes sense is SecureRandom in my opinion.
I'm trying to implement AES in Java and this is the code I use:
byte[] sessionKey = {00000000000000000000000000000000};
byte[] iv = {00000000000000000000000000000000};
byte[] plaintext = "6a84867cd77e12ad07ea1be895c53fa3".getBytes();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sessionKey, "AES"), new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(plaintext);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sessionKey, "AES"), new IvParameterSpec(iv));
byte[] deciphertext = cipher.doFinal(ciphertext);
I need this fixed key and IV for test purpose but I get the following exception:
Exception in thread "main"
java.security.InvalidAlgorithmParameterException:
Wrong IV length: must be 16 bytes long at
com.sun.crypto.provider.SunJCE_h.a(DashoA12275) at
com.sun.crypto.provider.AESCipher.engineInit(DashoA12275) at
javax.crypto.Cipher.a(DashoA12275) at
javax.crypto.Cipher.a(DashoA12275) at
javax.crypto.Cipher.init(DashoA12275) at
javax.crypto.Cipher.init(DashoA12275)
How can I use this fixed IV with this implementation of AES? Is there any way?
Firstly,
byte[] iv = {00000000000000000000000000000000};
creates a byte array of size 1 and not a byte array of size 32 (if that is your intention).
Secondly, the IV size of AES should be 16 bytes or 128 bits (which is the block size of AES-128). If you use AES-256, the IV size should be 128 bits large, as the AES standard allows for 128 bit block sizes only. The original Rijndael algorithm allowed for other block sizes including the 256 bit long block size.
Thirdly, if you are intending to use a AES-256, this does not come out of the box. You need to download and install the JCE Unlimited Strength Jurisdiction Policy Files (scroll to the bottom of the page); I would also recommend reading the accompanying license.
This would result in the following change to your code:
byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
Finally, the initialization vector is meant to be unique and unpredictable. A sequence of 16 bytes, with each byte represented by a value of 0, is not a suitable candidate for an IV. If this is production code, consider getting help.
From Advanced Encryption Standard:
The standard comprises three block ciphers, AES-128, AES-192 and AES-256, adopted from a larger collection originally published as Rijndael. Each of these ciphers has a 128-bit block size, with key sizes of 128, 192 and 256 bits, respectively
(Emphasis added)
From Initialization Vector:
For block cipher modes of operation, the IV is usually as large as the block size of the cipher
Combine those two factors together, and you get that the IV is always 128 bits for AES, independent of the key size.
AES here is probably AES-128 not AES-256.You must include extra jar if you want to enable AES-256 as there are export control policies in place. So check that first. AES-128 is enough in most cases.
Your IV can't be more than 128 bits i.e. 16 bytes when it is AES-128. So change the initialization vector length.
That must work. Also, read this http://en.wikipedia.org/wiki/Initialization_vector
Warning: It is not a good practice to have a fixed IV. It must be random or pseudorandom to offer better security.
Why not just use something like that instead of using "magic numbers":
SecureRandom random = new SecureRandom();
byte[] iv = random.generateSeed(16);
So you get 16 random bytes for your iv.
The reason why I had this problem was because I was reading the IV as a String, and converting it to a byte array the wrong way.
Right way:
Hex.decodeHex(initializationVector.toCharArray()
using org.apache.commons.codec.binary.Hex
Wrong way:
initializationVector.getBytes()
The reason this was wrong, is because when you call getBytes(), it just takes all bits that represent the string and cuts them into bytes. So 0 ends up being written as the bits that make up the index of 0 in the Unicode table, which is not 0 but 30, and which would be written on 2 bytes.
Conversely, what you want here is actually that 0 be represented as the byte 00000000.
Not related to this but faced same issue.
IvParameterSpec ivParameterSpec = new IvParameterSpec(key1.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key2.getBytes("UTF-8"),"AES");
The mistake I made was key1 and key2 were Strings of length greater than 16. Changing their length to 16 fixed it.