Java Aes and Using My Own Key

Java AES and using my own Key

Edit:

As written in the comments the old code is not "best practice".
You should use a keygeneration algorithm like PBKDF2 with a high iteration count.
You also should use at least partly a non static (meaning for each "identity" exclusive) salt. If possible randomly generated and stored together with the ciphertext.

    SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] salt = new byte[16];
sr.nextBytes(salt);

PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8);
SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.ENCRYPT_MODE, key);

===========

Old Answer

You should use SHA-1 to generate a hash from your key and trim the result to 128 bit (16 bytes).

Additionally don't generate byte arrays from Strings through getBytes() it uses the platform default Charset. So the password "blaöä" results in different byte array on different platforms.

byte[] key = (SALT2 + username + password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

Edit:
If you need 256 bit as key sizes you need to download the "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" Oracle download link, use SHA-256 as hash and remove the Arrays.copyOf line.
"ECB" is the default Cipher Mode and "PKCS5Padding" the default padding.
You could use different Cipher Modes and Padding Modes through the Cipher.getInstance string using following format: "Cipher/Mode/Padding"

For AES using CTS and PKCS5Padding the string is: "AES/CTS/PKCS5Padding"

AES encryption in Java for given key

Yes, Java - or rather the security provider supplying the AES in CBC mode implementation - may default to a random IV (which you then have to retrieve afterwards and include with your ciphertext) if you don't explicitly specify it you get secure, randomized ciphertext.

If you want to use a zero IV you will have to specify it explicitly:

cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]);

This is only slightly more secure than ECB mode, as any repetition in different messages in the initial ciphertext blocks will be instantly visible to an attacker.

If you want to have a more secure mode without random IV - which is required for CBC mode to obtain CPA security - then you could check out synthetic IV (SIV) mode or GCM-SIV. For those modes the entire message needs to be identical with a previous one to leak info to an attacker. It's however slowe, not included in the standard VM and the ciphertext could be larger than AES/CBC (inclusion of IV/tag vs padding requirements).

How to perform AES encryption with custom secret key stored in keystore?

Answers to your doubts:

  1. How/where should I store my secret key i.e. "mysecretkey_123456"?

That depends. What I'm looking at seems to be a password rather than a key. So storing it inside your head or a password manager would be advised.


  1. Why is there a need to "hash" the combination of "(SALT2 + username + password)" using SHA-1/2 and pass the byte[] array to the SecretKeySpec?

This is because an AES key consists of exactly 16, 24 or 32 bytes that should be random to an attacker. A password neither has a consistent length nor the randomness required.

What the code segment does is to create a password hashing function or password based key derivation function (PBKDF). Of course just using SHA-1 doesn't cut it, you should use PBKDF2 or one of bcrypt, scrypt or Argon2. Then configure one of these functions with a high work factor (or iteration count) to provide for key strengthening. PBKDF2 - albeit not the greatest - is part of the Java runtime environment, making it relatively easy to deploy.

In case your "cleartext key" called my_custom_secret_key already has a strength of 128 bits or more then you could use a Key Based Key Derivation Function such as HKDF instead.


  1. Why can't I send the cleartext secret key as byte[] ?

Who says you can't? The code sample seems to mistaken a key and a password, so I presume that's where the confusion comes from. You would generally use bytes for high entropy secret keys.

For passwords using char[] is advised; this is because you cannot destroy the contents of a String after you've used it for verifying the password or deriving the key. A char[] on the other hand can be cleared by filling it with zeros directly after use. This works equally well on keys stored in a byte[] of course.


  1. I am trying to ensure the "key" is dynamic so that it is based on a salt+username+my_custom_secret_key, so that the same encrypted string will have different output.

That will work as long as the salt is large and random enough, say 16 bytes SecureRandom regenerated every time a salt is required for encryption.

That way the generated key will always be random enough and your encryption would be safe when using a secure mode (i.e. any mode build in Java but ECB, preferably something like GCM mode encryption).

AES custom password key

why is there a need to specify the key size 256

PBKDF2 is a flexible password-based key derivation function. It uses an underlying hash function with many iterations. It can output any size of key that you want. It is common to use SHA-256 even when generating AES-128 keys, because SHA-256 is not known to be broken and it is relatively slow compared to other hash functions such as MD5 and SHA-512 (only on x64). The slowness is an important factor for a PBKDF, because it directly impacts the attacker when they try to brute-force the password. Of course, you also have the adjustable iteration count.

Additionally, PBKDF2 can output even more key material than the underlying hash function output size. For example, it is common to request the output of PBKDF2 to include the IV. In your case, the output should be 384 bits long.

Generally, it is not recommended to request more than the underlying hash function from PBKDF2. You should use SHA-512 if you want to derive the IV, too. As long as the salt is randomly generated for each encryption and stored alongside the ciphertext, this should be enough to achieve semantic security.

So, to answer your question, PBKDF2 has no idea how you want to use the output. You are responsible for that. You have to know what you're doing. There are a million different ways to solve the PBKDF part of encrypting something.

why do I need to associate it with the SecretKeySpec as AES algorithm.

If you want encrypt something with AES using the Cipher instance, you need to pass in a java.security.Key object that would resolve to AES at runtime. The Key#getAlgorithm() method is used for that. If you don't specify "AES" when creating the SecretKeySpec, you will get an InvalidKeyException.

Java AES: Generate Key using a single id as source

Hope this can help you.

package com.lahiru.security;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AESEncryptionDecryption {

private static SecretKeySpec secretKey;
private static byte[] key;

public static void main(String[] args){
String id = "00001";
String plainText = "This is plain text";

String cipherText = encrypt(plainText, id);
System.out.println("Cipher Text after encrption ::: " + cipherText);
System.out.println("Plain Text after decryption ::: " + decrypt(cipherText, id));

}

public static void setKey(String myKey)
{
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

public static String encrypt(String strToEncrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}

public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
}


Related Topics



Leave a reply



Submit