I generate randomly IV value everytime I encrypt when doing AES/CBC.
private static IvParameterSpec getRandomIvParameterSpec() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
And I concat IV Value to cipher byte everytime I encrypt.
Is there any secure improvement if I hash (SHA-256) IV value before concat to cipher byte?
SHA-256 is injective. You give it the same input, it will give you the same output. It is not surjective, however. If m1 and m2 both hash to h, you cannot conclude that m1 = m2, even if you know that |m1| = |m2| (both messages are of the same length).
Therefore, applying SHA-256 (or any deterministic function) cannot increase the entropy of your data. At best, it won't decrease it. In other words: If your data is 16 purely random bytes, it won't be “more than purely random” after you hash it. And if your data was not purely random to begin with, then hashing it won't help making it random. You have to use a better entropy source in the first place.
Another problem that you didn't mention is that you currently have 16 random bytes but if you put them into your SHA-256 hash function, you'll get 32 bytes out. Which ones are you going to use? If you only use every second byte – due to injectivity – you won't get all possible bit patterns even if your input was perfectly random and the hash function was flawless. (If you did, then this would – by the pidgin hole principle – mean that the other half of the bytes would always be a function of the bytes you did chose. Only a really crappy hash function, which SHA-256 of course is not, would have such property.) If you try to be clever and combine the bytes in some “smart” way, chances are that you'll make things even worse.
So the short answer is: just don't do it. Generate as many random bytes as you need using the strongest non-deterministic entropy source you have available and use them directly.
Related
what if the input key is less than 16 bytes? i found a solution, but its not a best practice.
//hashing here act as padding because any input given, it will generate fixed 20 bytes long.
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
//trim the code to only 16 bytes.
key = Arrays.copyOf(key, 16);
I'm not planning to use salt because it is not necessary in my project. Is there any better way?
There are three approaches:
Pad the key out to 16 bytes. You can use any value(s) you want to as padding, just so long as you do it consistently.
Your scheme of using a SHA-1 hash is OK. It would be better if you could use all of the bits in the hash as the key, but 128 bits should be enough.
Tell the user that the key needs to be at least N characters. A key that is too short may be susceptible to a password guessing attack. (A 15 character key is probably too long to be guessed, but 8 characters is tractable.) In fact, you probably should do some other password quality checks.
My recommendation is to combine 1. or 2. with 3 ... and password quality checks.
I'm not convinced that seeding the hash will make much difference. (I am assuming that the bad guy would be able to inspect your file encryption app and work out how you turn passwords into keys.) Seeding means that the bad guy cannot pre-generate a set of candidate keys for common / weak passwords, but he still needs to try each of the generated keys in turn.
But the flip-side is that using a crypto hash doesn't help if the passwords you start with are weak.
Don't confuse keys and passwords. Keys are randomly generated and may consist of any possible byte value. Passwords on the other hand need to be typable by a human and usually rememberable. If the key is too short then either emit an error to the user or treat it as a password.
A key should then only be entered in encoded format such as hex or Base64. Only check the length when you successfully decode it.
A password has all kinds of issues that makes it brute forceable such as short length or low complexity. There you would need to use a password-based key derivation function such as PBKDF2 and a sufficiently large work factor (iterations) in order to make a single key derivation attempt so slow that an attacker would need much more time to check the whole input space.
You should combine that with some message to the user to give some hints that the password is too short or doesn't include some character classes and is therefore not recommended.
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 trying to understand OpenSSL encryption. The requirement for me is to decrypt the data encrypted using the below code. It is creating an anonymous ID for receiver. I need to get received ID back.
Based on the documentation I understood encryption is happening using EVP_EncryptFinal. But what is PKCS5_PBKDF2_HMAC_SHA1 doing ?
The code is using KEY and IV.
int create_anon_id(uint32_t recvr_id, uint32_t smartcard_id, const char *hw_id, unsigned char *anon_id)
{
EVP_CIPHER_CTX ctx;
unsigned char ibuf[sizeof(recvr_id) + sizeof(smartcard_id)] = {};
unsigned char obuf[sizeof(ibuf) * 2] = {};
int olen = sizeof(obuf);
/* Convert to big endian. */
recvr_id = bswap32(recvr_id);
smartcard_id = bswap32(smartcard_id);
hw_id = bswap32(hw_id);
/* Fill input buffer with recvr_id | smartcard_id */
memcpy(ibuf, &recvr_id, sizeof(recvr_id));
memcpy(ibuf + sizeof(recvr_id), &smartcard_id, sizeof(smartcard_id));
if (EVP_EncryptInit(&ctx, EVP_des_ede3_cbc(), KEY, IV) == 1)
{
if (EVP_EncryptUpdate(&ctx, obuf, &olen, ibuf, sizeof(ibuf)) == 1)
{
olen = sizeof(obuf) - olen;
if (EVP_EncryptFinal(&ctx, obuf + sizeof(obuf) - olen, &olen) == 1)
{
return PKCS5_PBKDF2_HMAC_SHA1((const char *)obuf, olen, (unsigned char *)hw_id, HW_ID_SIZE, ROUNDS, ANON_ID_BIN_SIZE, anon_id);
}
}
}
}
That code is quite poor, although by bad luck some of the errors are masked by using an input which is exactly one triple-DES data block (64 bits or 8 bytes). If coded correctly the encryption would be done by both EVP_EncryptUpdate and EVP_EncryptFinal; as written the only encryption actually used, of the unpadded block, is done by Update.
But the value returned in anon_id is NOT (JUST) ENCRYPTED. PKCS5_PBKDF2_HMAC[_SHA1] is an implementation of the PBKDF2 algorithm and as used here it computes essentially a slow hash of
recvr_id concatenated with smartcard_id (update: apparently not swapped) then triple-DES encrypted with KEY and IV whatever those are in this code, as the 'passphrase' with
hw_id (update: apparently not swapped, and thus 'accidentally' type-valid) as the salt,
and PRF = HMAC-SHA1 for ROUNDS (update: 10001) iterations
(update) expanded slightly to ANON_ID_BIN_SIZE (22) bytes. (This requires an extra 'pass' of PBKDF2 which actually hurts security; it slows the defender without slowing the attacker.)
PBKDF2 with a decent PRF (HMAC-SHA1 is) is a one-way function and cannot be reversed, as encryption can if you know or guess the key. It is also designed to be costly to bruteforce if the salt and iteration count are chosen well. 32-bits of salt is definitely on the low side, and (update) ROUNDS 10001 is somewhat on the low side but not crazy.
As a better-case example (for you as attacker) if the 32-bit recvr_id and smartcard_id each contain only 16-bits of entropy, and so does hw_id, and ROUNDS was 1000 which was the recommended value when rfc2898 was written at the turn of the century, you'd have to try up to 256 trillion possibilities and a typical desktop computer can do maybe 30 thousand per second in CPU, and with some luck and skill maybe a thousand times that in GPU, so that will take about 3 months.
On the other hand, if the 32-bit inputs are fully entropic and ROUNDS was 1000000, it is impossible with just a computer. If you have a few million dollars to spend on FPGAs (or even better ASICs), and the output of a power plant (or maybe your own hydroelectric dam) to run them, it will still take many millions of years.
Update: with ROUNDS 10001 you are most likely in between these situations, but just where depends strongly on the entropy of the inputs.
And in general this may not produce the desired result anyway. If the output size is less than or even roughly equal to the entropy of the inputs, there will be collisions -- multiple input values (combinations) that produce the desired output, possibly a great many (millions, billions, etc) of such inputs. If you need the original input, rather than merely some input that produces (forges) a known output, you'll need some other method or criteria to select among the possible inputs that bruteforcing finds.
Update: for output 22 bytes with at most 96 bits entropy, this risk is negligible.
From this on how to achieve password based encryption it is clear that i need to save salt, IV and cipher text in order to decrypt it later.
From this iv and salt can be stored along with cipher text
I am storing the hex value in this format
DatatypeConverter.printHexBinary(salt) + DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(ciphertext);
Do i need to store the values in Binary format ?
DatatypeConverter.printBase64Binary(salt) + DatatypeConverter.printBase64Binary(iv) + DatatypeConverter.printBase64Binary(ciphertext));
output clearly indicates the where the salt , iv is ending which is awful
lIvyAA/PZg4=fE4gTZUCPTrKQpUKo+Z1SA==4/gAdiOqyPOAzXR69i0wlC7YFn9/KOGitZqpOW2y3ms=
Will storing in hex format have any effects of data loss ?
Will the length of IV is constant ? in my case it is always 32 characters (hexadecimal)
Or i need to even store length of IV as well ? as the salt length is fixed initially to 8 bits (16 hexadecimal characters)
(I am using PBKDF2WithHmacSHA1 algorithm for key generation and AES/CBC/PKCS5Padding for cipher)
I think it is worth emphasizing again what the accepted answer above mentioned in passing.
That is, it is unnecessary and unwarranted to make any attempt to hide the salt or the IV. The security of your cryptography is entirely dependent on the secrecy of the secret key, and that of the secret key alone. The IV and the salt can be handed out in clear text along with the ciphertext, and as long as the secret key remains a secret, the ciphertext remains secure.
It's important to understand and accept that, or you will wind yourself about an axle trying to obfuscate things that don't matter. There is no security in obscurity.
It is important to note, however, that the salt should be generated in a cryptographically strong pseudorandom number generator. A new salt should be generated for each new plain text that is being encrypted. Likewise, the IV should be randomly generated for each new ciphertext.
Those parameters need to be independent and unpredictable but need not be secret.
So you can store them in separate fields or delimit them in a single field, or use fixed lengths for the first two of three fields. For maximum flexibility and future proofing, though, I suggest delimited fields, and include all parameters needed to deal with the data. If you are using PBE, I would include the algorithm name and the iteration count, too, rather than rely on default values.
Base64 encodes in chunks of 3 bytes into 4 base64 chars. If the number of bytes that needs to be encoded ain't a multiplum of 3 the last block is padded with one or two =, to indicate that this block ain't full 3 bytes.
As neither the salt nor the IV needs to be kept secret, there really ain't any problem about being able to detect where they start or stop. The base64 padding char = ain't a problem - but you ought to have a way to separate the three encoded strings. You could e.g. simply seperate the parts with a :.
The size of the IV is the same as the block size of your encryption algorithm. In this case you use AES that have a block size of 128 bits, which is 16 bytes. This would give 32 bytes if hex encoded, or 24 bytes if base64 encoded. Salt don't really have a fixed length, and will depend on your implementation.
I'm currently working on a protocol, which uses Diffie-Hellman for an key exchange.
I receive a packet, which consists of an aes-128 encrypted part and a 128 Bit DH Public Key.
In a very last step in the protocol, the aes key is sent to another peer in the network.
This aes-key should be encrypted with a cipher using a 128 bit strong secretkey.
I plan to use Blowfish (can also be another cipher, doesn't really matter for the problem)
Now to encrypt the aes-key, with lets say blowfish, I have to build a secretkey for the encryption with a class called SecretKeySpec (I'm using javax.crypto stuff), which takes an byteArray in the constructor to build the secretKey.
The sharedkey from DH is a 128 Bit BigInteger.
Well, now I can interpret my shared-key as a byteArray (wich gives me still 128 Bit in 16Bytes [where the numbers are interpreted as frames of 8 Bit data])
So my question is, how strong is my key really?
Is there any impact because I only use numbers as input for the byteArray (so does this limit the keyspace in any way?)
I think this is not the case, but I'm not 100% sure.
Maybe someone can do the math and proof me right or wrong.
If I'm wrong what keysize for the shared key give me piece of mind to finally get to the 128Bit SecretKey for the encryption?
The Crypto++ website suggests using a minimum p of 3072 bits (or 256 bits for an ECC implementation) to transport a 128 bit AES key.
You might wish to study the references provided at http://www.keylength.com/en/compare/ for further information about comparing key lengths among different algorithms.
Not an expert in DH here, but to me it seems that DH's keyspace for the shared key represented in n bits is somewhat smaller than 2^n.