This is the hex format of a secret key used for AES encryption
00010203050607080A0B0C0D0F101112
Can i generate the original SecretKey format or byte array from this?
If so how?
You can use Apache Commons Codec to perform the hex decoding,
http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html#decodeHex(char[])
If you don't want use any libraries, you can also do this,
byte[] bytes = new BigInteger("7F" + str, 16).toByteArray();
SecretKeySpec key = new SecretKeySpec(bytes, 1, bytes.length-1, "AES");
You have to add an extra byte 0x7F to prevent BigInteger from stripping leading zeros or adding signed bytes.
A SecretKey is an interface. What type was the implementation? J2SE has two implementing classes, KerberosKey and SecretKeySpec, both which have constructors with a byte array as a parameter.
What is the original SecretKey format?
Are you aware byte[] contains signed bytes valued from -128 to 127?
Would it hurt if you tried this:
byte[] key = {
0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,
0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12
};
Note: If you have values like 0x80 - 0xFF you'll need to cast them as (byte)0x80 - (byte)0xFF to avoid a warning about the range.
Related
Given an arbitrary Java byte array for example 1024 byte array I would like to derive an AES-256 bit key. The array is generated from ECHD via javax.crypto.KeyAgreement using byte[] secret = keyAgreement.generateSecret()
My current solution is to treat the input byte array as a password. Use the PBKDF2 key derivation function the input array as both the password and the salt as shown below.
UPDATE: I have set UTF-8 as the encoding to address issues pointed out in comments and answers.
private byte[] deriveAes256bitKey(byte[] secret)
throws NoSuchAlgorithmException, InvalidKeySpecException {
var secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var password = new String(secret, UTF_8).toCharArray();
var keySpec = new PBEKeySpec(password, secret, 1024, 256);
return secretKeyFactory.generateSecret(keySpec).getEncoded();
}
Is there a better way to take a byte array in Java and turn it into an AES-256 bit key?
I would be wary of using new String(input).toCharArray() to create the password. It's not portable (it uses the platform default encoding), and its behaviour is undefined if there are invalid character sequences in the input.
Consider this:
System.out.println(new String(new byte[] {(byte) 0xf0, (byte) 0x82, (byte) 0x82, (byte) 0xac}, StandardCharsets.UTF_8));
f08282ac is an over long encoding of the Euro sign (€). It's decoded to the replacement character (�; 0xfffd) because it's an illegal sequence. All illegal UTF-8 sequences will end up as the replacement char, which is not what you want.
You could avoid decoding problems by serialising the byte array before passing it to the SecretKeyFactory (base64 encode it, or simply new BigInteger(input).toString(Character.MAX_RADIX)). However, this can be avoided if you don't use the SecretKeyFactory. It's unnecessary.
PBKDF2 (Password-Based Key Derivation Function 2) is designed to make brute force attacks against user supplied passwords harder by being computationally expensive and adding salt.
You don't need that here (your input is large and random; nobody will be mounting dictionary attacks against it). Your problem is just that the input length doesn't match the required key length.
You can just hash the input to the correct length:
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = md.digest(input);
What is required here is a KBKDF or Key Based Key Derivation Function. A KBKDF converts a secret value that contains enough entropy into a different key of a specific size. A PBKDF is used when you have a passphrase with potentially too little entropy into a key using key strenghtening (using the salt and work factor or iteration count). The work factor / iteration count doesn't need to be used if the input value is already strong enough not to be guessed / brute forced.
SHA-256 in general suffices if you only want a resulting 128 bit value. However, using a key derivation function may still offer benefits. First of all, it is a function that is explicitly defined for the function, so it is easier to prove that it is secure. Furthermore, it is generally possible to add additional data to the key derivation function so that you can e.g. derive more keys or a key and an IV. Or you can expand the configurable output size to output enough data for different keys or key / IV.
That said, most cryptographers won't frown too much if you use SHA-256 (or SHA-512 in case you require more bits for key / IV). The output is still supposed to be randomized using all possible bits from the input, and it is impossible to inverse the function.
I am using the following code in my android application to encrypt a string in Triple DES using the Encrypted Code Book (ECB) mode with three independent keys (aka 3DESede), which are provided as a 24 byte sized key array. Therefore I use the Java Crypto API. This works pretty well, but if I encrypt an eight character string I get a 16 byte cipher text, which should not happen as 3DES operates on chunks of 64 bit (resp. 8 byte). Same holds for the PKCS5 padding as this also operates on chunks of 64 bit. So my question is what causes this problem?
private static byte[] encryptText(String plaintext, byte[] keyBytes) throws Exception {
// Get plaintext as ASCII byte array
final byte[] plainBytes;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
plainBytes = plaintext.getBytes(StandardCharsets.US_ASCII);
} else {
plainBytes = plaintext.getBytes("US-ASCII");
}
// Generate triple DES key from byte array
final DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyFactory.generateSecret(keySpec);
// Setup the cipher
final Cipher c3des = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c3des.init(Cipher.ENCRYPT_MODE, key);
// Return ciphertext
return c3des.doFinal(plainBytes);
}
PKCS5Padding adds 1-8 bytes of padding when used with DES. If you encrypt 8 bytes you will get 8 additional bytes of padding to get to an even number of blocks.
If you used Cipher.getInstance("DES/ECB/NoPadding") and encrypted 8 bytes, you will get 8 bytes of cipher text.
When PKCS#5 padding is used it must always add padding otherwise on decryption there would be no way to determine if padding was added. So, even if the input data is an exact multiple of the block size padding must be added and that will be 8 bytes.
See PKCS padding:
If the original data is an integer multiple of N bytes, then an extra block of bytes with value N is added. This is necessary so the deciphering algorithm can determine with certainty whether the last byte of the last block is a pad byte indicating the number of padding bytes added or part of the plaintext message. Consider a plaintext message that is an integer multiple of N bytes with the last byte of plaintext being 01. With no additional information, the deciphering algorithm will not be able to determine whether the last byte is a plaintext byte or a pad byte. However, by adding N bytes each of value N after the 01 plaintext byte, the deciphering algorithm can always treat the last byte as a pad byte and strip the appropriate number of pad bytes off the end of the ciphertext; said number of bytes to be stripped based on the value of the last byte.
PKCS#5 padding is identical to PKCS#7 padding, except that it has only been defined for block ciphers that use a 64-bit (8 byte) block size. In practice the two can be used interchangeably.
I was given this key in hex String: "140b41b22a29beb4061bda66b6747e14" and requested to decrypt an one block cipher with an AES implementing a ECB.
As we know, the key must be 16 bytes long. But the given key contains elements which correspond to characters larger than one byte (e.g. 0xb2 whose char value is ² corresponding to 2 bytes).
In fact, if I convert the hex String key into a key String I obtain " A²*)¾´Úf¶t~ ", then if I apply the method key.getBytes().length what I get is that the key is 21 bytes long.
My question is: is there any way to encrypt a 16 bytes long ciphertext with an AES given this key in Java?
Your key looks correctly sized - just don't think of it as a string with meaningful characters. Instead, use a hex conversion method to convert it into a 16 byte array and use that as the key.
E.g.
String keyString = "140b41b22a29beb4061bda66b6747e14";
byte[] keyBytes = DatatypeConverter.parseHexBinary(keyString);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
I implement this via java , BouncyCastle Provider
use Block mode = ECB and Padding mode = PKCS7Padding
I noticte that if i encrypt data that has 32 byte length(such as 61626162616261626162616261626162 which is hex value of abababababababab) i get 64 bytes length cipher text(f21ee0564ebd5274e10bf4590594b1e16a19592b917b19ee106f71d41d165289) is this cipher text valid? from what i read it look like if you encrypt less than 32 byte length data the algorithm will padding it to 32 byte length and produce a 32 byte length cipher text but if you put in exactly 32 byte length data shouldn't you receive the 32 byte length cipher text ,also if i put in data with more length than 32 byte it will padded to 64 byte cipher text correctly
this is what my code look like :
public static byte[] encrypt(byte[] plainText, byte[] keyBytes)
throws GeneralSecurityException {
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(plainText);
return cipherText;
}
thx for your reply
You are confusing hexadecimal characters and bytes. Two hexadecimal characters can be used to represent a single byte. So your input is likely 16 bytes and your output then becomes 32 bytes. AES is a 128 bits = 16-byte block cipher.
PKCS#7 padding is defined to pad 16 byte block ciphers. To make sure that the last byte of the plain text is not mistaken for padding, PKCS#7 padding always pads. This means that in the unfortunate circumstance that the plain text is dividable by 16, it will add an entire block of padding. The padding consists of a string of bytes indicating the length of the string in each byte, so for your case the padding consists of the following bytes, represented by hexadecimals: 10101010101010101010101010101010.
Note that the default provider of Java uses "PKCS5Padding" instead of "PKCS7Padding". Both are identical, although PKCS#5 padding officially is only for 64 bit = 8 byte block ciphers. Bouncy Castle will accept that string as well, so it is better to specify "PKCS5Padding" for the sake of compatibility.
There is no padding specified for AES that would use 32 bytes as it is only a 16 byte block cipher. It would make sense for Rijndael-256 which is a 32 byte block cipher, but that algorithm hasn't been standardized by NIST and should therefore be avoided.
The PKCS7 padding is explained here:
http://en.wikipedia.org/wiki/Padding_(cryptography)#Byte_padding
It adds between 1 and blockLength bytes, all of which are equal to the size of the padding. This means your message will be padded by 16 16's, creating a 32-byte message that gets subsequently encoded with AES.
Note that your message is 16 bytes, not 32.
In general, a padding of zero length is never used, because it cannot be distinguished from non-zero padding (and the padding must be done in a reversible fashion).
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.