94

I want to encrypt a string using AES with my own key. But I'm having trouble with the bit length of the key. Can you review my code and see what I need to fix/change.

public static void main(String[] args) throws Exception {
    String username = "bob@google.org";
    String password = "Password1";
    String secretID = "BlahBlahBlah";
    String SALT2 = "deliciously salty";

    // Get the Key
    byte[] key = (SALT2 + username + password).getBytes();
    System.out.println((SALT2 + username + password).getBytes().length);

    // Need to pad key for AES
    // TODO: Best way?

    // Generate the secret key specs.
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    // Instantiate the cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

    byte[] encrypted = cipher.doFinal((secrectID).getBytes());
    System.out.println("encrypted string: " + asHex(encrypted));

    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " + asHex(original));
}

Right now I get an exception "Invalid AES key length: 86 bytes". Do I need to pad my key? How should I do it?

Also do I need to set anything for ECB or CBC?

Thanks

Bernie Perez
  • 12,513
  • 13
  • 46
  • 55
  • 7
    [I find your lack of random salt disturbing](http://d37nnnqwv9amwr.cloudfront.net/photos/images/newsfeed/000/065/003/Darth-Vader-I-FIND-YOUR-LACK-OF-FAITH-DISTURBING.jpg). Now seriously: in the context of cryptography [SALT should be random](http://en.wikipedia.org/wiki/Salt_(cryptography)) – João Portela Jan 02 '12 at 15:40
  • 18
    Haha, funny. I actually do have a random salt, but I cleaned up my code to make my question more clear. That's why the variable is named SALT2. But good reference for others who come across this same problem and like to copy/paste code. – Bernie Perez Mar 23 '12 at 18:40

6 Answers6

128

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"

mknjc
  • 1,577
  • 1
  • 9
  • 9
  • This will work, but its hashing my password, then only using the first few bits. There is no better way to do this? – Bernie Perez Aug 10 '10 at 22:46
  • 4
    There is no better way to generate the key cause AES needs a 128/192/256 bits key. If you don't hash your key and only trim the input it would only use the first 16/24/32 Bytes. So generating a Hash is the only reasonable way. – mknjc Aug 11 '10 at 06:26
  • 13
    Note that this answer does not use a good key derivation function and is thus **not as secure as it should be**. See the [other answer](http://stackoverflow.com/a/3451823/589259) for a slightly outdated key derivation function - and unfortunately still a static salt. – Maarten Bodewes Jan 16 '13 at 22:00
  • I have got this error: javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length – Ahmad Arslan Aug 18 '14 at 14:46
  • Say I generate an rsa key with openssl which has AES256 encryption. I use your method and only use the first few bits of the hashed key, how would someone on the other side of the line decrypt that string again? – atripes Nov 21 '14 at 11:41
  • 2
    Might I suggest deleting this answer as it is **extremely** bad practice. A proper key derivation function should be used - at least PBKDF2. – Boris the Spider Apr 05 '19 at 08:20
  • 1
    Yes, The answer is very bad, as Maarten said years ago. Please check this answer from [Cryptography](https://crypto.stackexchange.com/a/68548/18298) and [Key Derivation Function](https://crypto.stackexchange.com/questions/40757/key-derivation-functions-kdf-what-are-main-purposes-how-can-they-be-used?noredirect=1&lq=1) – kelalaka Apr 05 '19 at 09:50
  • I cannot make the newer answer working. I'm getting `java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required at java.base/com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:86) at java.base/com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:97) at java.base/com.sun.crypto.provider.CipherCore.init(CipherCore.java:593) at java.base/com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)` **OpenJDK 14.0.1.** Any thoughts? – satorg Sep 20 '20 at 23:18
14

You should use a KeyGenerator to generate the Key,

AES key lengths are 128, 192, and 256 bit depending on the cipher you want to use.

Take a look at the tutorial here

Here is the code for Password Based Encryption, this has the password being entered through System.in you can change that to use a stored password if you want.

        PBEKeySpec pbeKeySpec;
        PBEParameterSpec pbeParamSpec;
        SecretKeyFactory keyFac;

        // Salt
        byte[] salt = {
            (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
            (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
        };

        // Iteration count
        int count = 20;

        // Create PBE parameter set
        pbeParamSpec = new PBEParameterSpec(salt, count);

        // Prompt user for encryption password.
        // Collect user password as char array (using the
        // "readPassword" method from above), and convert
        // it into a SecretKey object, using a PBE key
        // factory.
        System.out.print("Enter encryption password:  ");
        System.out.flush();
        pbeKeySpec = new PBEKeySpec(readPassword(System.in));
        keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

        // Our cleartext
        byte[] cleartext = "This is another example".getBytes();

        // Encrypt the cleartext
        byte[] ciphertext = pbeCipher.doFinal(cleartext);
Keibosh
  • 1,237
  • 1
  • 9
  • 18
  • 4
    How do I generate my Key with the password using the KeyGenerator? I want to generate the same key based on the password. So I can decrypt the string later. – Bernie Perez Aug 10 '10 at 23:28
  • What your talking about is password based encryption not AES. I updated my answer with the sample program for PBE – Keibosh Aug 12 '10 at 18:13
  • 5
    Try and use the PBEKDF2 key generator instead, using string "PBKDF2WithHmacSHA1" for the `SecretKeyFactory` for more up to date encryption. – Maarten Bodewes Jan 16 '13 at 21:57
  • 12
    Actually ***all the used cryptographic primitives in this answer are outdated***, MD5 and DES for sure. Take heed. – Maarten Bodewes Jan 16 '13 at 22:02
  • MD5 and DES are weak cipher suites and should be AVOIDED – atom88 Apr 24 '19 at 21:33
5
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;

public class AESFile 
{
private static String algorithm = "AES";
private static byte[] keyValue=new byte[] {'0','2','3','4','5','6','7','8','9','1','2','3','4','5','6','7'};// your key

    // Performs Encryption
    public static String encrypt(String plainText) throws Exception 
    {
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.ENCRYPT_MODE, key);
            byte[] encVal = chiper.doFinal(plainText.getBytes());
            String encryptedValue = new BASE64Encoder().encode(encVal);
            return encryptedValue;
    }

    // Performs decryption
    public static String decrypt(String encryptedText) throws Exception 
    {
            // generate key 
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
            byte[] decValue = chiper.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            return decryptedValue;
    }

//generateKey() is used to generate a secret key for AES algorithm
    private static Key generateKey() throws Exception 
    {
            Key key = new SecretKeySpec(keyValue, algorithm);
            return key;
    }

    // performs encryption & decryption 
    public static void main(String[] args) throws Exception 
    {
        FileReader file = new FileReader("C://myprograms//plaintext.txt");
        BufferedReader reader = new BufferedReader(file);
        String text = "";
        String line = reader.readLine();
    while(line!= null)
        {
            text += line;
    line = reader.readLine();
        }
        reader.close();
    System.out.println(text);

            String plainText = text;
            String encryptedText = AESFile.encrypt(plainText);
            String decryptedText = AESFile.decrypt(encryptedText);

            System.out.println("Plain Text : " + plainText);
            System.out.println("Encrypted Text : " + encryptedText);
            System.out.println("Decrypted Text : " + decryptedText);
    }
}
Shankar Murthy
  • 150
  • 2
  • 3
  • 5
    Maybe add some more explanation text. – DAG Dec 24 '13 at 10:59
  • Question, what is the point of having `keyValue`, with the byte array? I see it being used to make the Key, by why? Can something be done using like `SecretKey` instead? If so, how? – Austin Oct 28 '14 at 18:25
  • @Mandrek, the contents of the file "plaintext.txt" will be encrypted. The above logic encrypts the data/message in the file which is read as argument in FileReader constructor. – Shankar Murthy Jan 30 '19 at 13:50
4

MD5, AES, no padding

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.io.Charsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class PasswordUtils {

    private PasswordUtils() {}

    public static String encrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(ENCRYPT_MODE, key);

            byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
            byte[] encoded = Base64.getEncoder().encode(encrypted);
            return new String(encoded, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot encrypt", e);
        }
    }

    public static String decrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(DECRYPT_MODE, key);

            byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
            byte[] decrypted = cipher.doFinal(decoded);
            return new String(decrypted, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot decrypt", e);
        }
    }
}
Mike
  • 20,010
  • 25
  • 97
  • 140
2

This wll work.

public class CryptoUtils {

    private  final String TRANSFORMATION = "AES";
    private  final String encodekey = "1234543444555666";
    public  String encrypt(String inputFile)
            throws CryptoException {
        return doEncrypt(encodekey, inputFile);
    }


    public  String decrypt(String input)
            throws CryptoException {
    // return  doCrypto(Cipher.DECRYPT_MODE, key, inputFile);
    return doDecrypt(encodekey,input);
    }

    private  String doEncrypt(String encodekey, String inputStr)   throws CryptoException {
        try {

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

            byte[] key = encodekey.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");

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] inputBytes = inputStr.getBytes();     
            byte[] outputBytes = cipher.doFinal(inputBytes);

            return Base64Utils.encodeToString(outputBytes);

        } catch (NoSuchPaddingException | NoSuchAlgorithmException
                | InvalidKeyException | BadPaddingException
                | IllegalBlockSizeException | IOException ex) {
            throw new CryptoException("Error encrypting/decrypting file", ex);
       }
     }


    public  String doDecrypt(String encodekey,String encrptedStr) { 
          try {     

              Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
              dcipher = Cipher.getInstance("AES");
              byte[] key = encodekey.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");

              dcipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // decode with base64 to get bytes

              byte[] dec = Base64Utils.decode(encrptedStr.getBytes());  
              byte[] utf8 = dcipher.doFinal(dec);

              // create new string based on the specified charset
              return new String(utf8, "UTF8");

          } catch (Exception e) {

            e.printStackTrace();

          }
      return null;
      }
 }
Community
  • 1
  • 1
Taran
  • 2,895
  • 25
  • 22
0
    byte[] seed = (SALT2 + username + password).getBytes();
    SecureRandom random = new SecureRandom(seed);
    KeyGenerator generator;
    generator = KeyGenerator.getInstance("AES");
    generator.init(random);
    generator.init(256);
    Key keyObj = generator.generateKey();
sonnykwe
  • 9
  • 1