Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java AES encryption/decryption procedure and usage of Initialization Vector

I want to learn the basics of AES encryption so I started to make a very simple Java program. The program loads a text file in to a String and asks for a key from the user. The program then uses AES to encrypt the text creating a new text file with the encrypted text. The program prints the Initialization Vector (IV) to the user.

The program also has the decryption function. The user specifies the encrypted text file along with the Initialization Vector and the key to decrypt it back to the original text in a new text file.

However I think I'm doing something wrong. Is it normal procedure in AES encryption that the user needs to have both key and IV to decrypt the file? I have browsed through the internet and almost in every example, the encrypted data can be decrypted by the user specifying only the key but in my case the user needs to have both the key and the IV. The program is working fine but I think it isn't efficient.

So should I use a constant, known IV which is used in all the encryptions and decryptions or what? Also some tutorials are using "salt", what is it and should I use it?

Here are my encrypt and decrypt methods:

public String encrypt(String stringToEncrypt, String userKey)
        throws NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

    // User gives string key which is formatted to 16 byte and to a secret
    // key
    byte[] key = userKey.getBytes();
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key, 16);
    SecretKeySpec secretKey = new SecretKeySpec(key, "AES");

    // Cipher initialization
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    // Encryption and encoding
    String encryptedData = new BASE64Encoder().encode(cipher
            .doFinal(stringToEncrypt.getBytes()));

    // IV is printed to user
    System.out.println("\nENCRYPTION IV: \n"
            + new BASE64Encoder().encode(cipher.getIV()) + "\n");

    // Function returns encrypted string which can be writed to text file
    return encryptedData;

}

public String decrypt(String stringToDecrypt, String userKey, String userIv)
        throws NoSuchAlgorithmException, IOException,
        NoSuchPaddingException, InvalidKeyException,
        InvalidAlgorithmParameterException, IllegalBlockSizeException,
        BadPaddingException {

    // User gives the same string key which was used for encryption
    byte[] key = userKey.getBytes();
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key, 16);
    SecretKeySpec secretKey = new SecretKeySpec(key, "AES");

    // Decode string iv to byte
    byte[] iv = new BASE64Decoder().decodeBuffer(userIv);

    // Cipher initialization
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));

    // Decryption and decoding
    String decryptedData = new String(cipher.doFinal(new BASE64Decoder()
            .decodeBuffer(stringToDecrypt)));

    // Function returns decrypted string which can be writed to text file
    return decryptedData;

}

UPDATE

I updated my code now to use "PBKDF2WithHmacSHA256" algorithm with salt and etc. I also combined the Initialization Vector (IV) byte array to the cipher text byte array as prefix so I can split them in decrypt method and get the IV there (That's working fine).

However there's now an issue with the key, because I'm generating new encrypted key also in decryption method which of course is a wrong key for encrypted data. I want to be able to close the program so I can't store the key as a class variable. It's very hard to explain the issue but I hope you understand the problem...

public static byte[] getEncryptedPassword(String password, byte[] salt,
        int iterations, int derivedKeyLength)
        throws NoSuchAlgorithmException, InvalidKeySpecException {

    KeySpec mKeySpec = new PBEKeySpec(password.toCharArray(), salt,
            iterations, derivedKeyLength);

    SecretKeyFactory mSecretKeyFactory = SecretKeyFactory
            .getInstance("PBKDF2WithHmacSHA256");

    return mSecretKeyFactory.generateSecret(mKeySpec).getEncoded();

}

public String encrypt(String dataToEncrypt, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {

    byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
            16384, 128);

    SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");


    Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec);

    byte[] ivBytes = mCipher.getIV();
    byte[] encryptedTextBytes = mCipher.doFinal(dataToEncrypt.getBytes());

    byte[] combined = new byte[ivBytes.length+encryptedTextBytes.length];       
    System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
    System.arraycopy(encryptedTextBytes, 0, combined, ivBytes.length, encryptedTextBytes.length);

    return Base64.getEncoder().encodeToString(combined);

}

public String decrypt(String dataToDecrypt, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {


    byte[] encryptedCombinedBytes = Base64.getDecoder().decode(dataToDecrypt);
    byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
            16384, 128);

    byte[] ivbytes = Arrays.copyOfRange(encryptedCombinedBytes,0,16);

    SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");

    Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, new IvParameterSpec(ivbytes));    

    byte[] encryptedTextBytes = Arrays.copyOfRange(encryptedCombinedBytes, 16, encryptedCombinedBytes.length);

    System.out.println(encryptedTextBytes.length);
    byte[] decryptedTextBytes = mCipher.doFinal(encryptedTextBytes);



    return Base64.getEncoder().encodeToString(decryptedTextBytes);

}

public byte[] generateSalt() {
    SecureRandom random = new SecureRandom();
    byte saltBytes[] = new byte[16];
    random.nextBytes(saltBytes);
    return saltBytes;
}}

I hope somebody knows how to make this better. Thanks!

like image 753
Leevi Lehtonen Avatar asked Feb 20 '15 11:02

Leevi Lehtonen


People also ask

Does AES use initialization vector?

The key file must work only on the corresponding data file. It should not work on any other file, either from the same user or from any other user. AES algorithm requires two different parameters for encryption, a key and an initialization vector (IV).

What is Java AES encryption and decryption?

AES, Advanced Encryption Standard is a block ciphertext encryption and decryption algorithm that processes a block of 128 bits of data using secret keys of 128, 192, or 256 bits. We will also discuss how this algorithm can be implemented using the Java programming language.

How encryption and decryption is done in AES?

AES includes three block ciphers: AES-128 uses a 128-bit key length to encrypt and decrypt a block of messages. AES-192 uses a 192-bit key length to encrypt and decrypt a block of messages. AES-256 uses a 256-bit key length to encrypt and decrypt a block of messages.

Is initialization vector needed for decryption?

Save this answer. Show activity on this post. Yes, you must provide the same IV for encryption and decryption.


1 Answers

Just save the IV in the file before the encrypted data.

You should never use the same IV more than once (it's ok-ish, if you roll a new IV every time, and it just so happens that you roll the same twice, so you don't have to store and check that). Using the same IV many times poses a great security risk, as encrypting the same content twice reveals that it's - in fact - the same content.

Storing IV alongside the encrypted data is a common, and secure procedure, as it's role is to introduce "randomness" to the encryption scheme, and it shouldn't be secret, just securely (and in some schemes randomly) generated.

like image 168
Marandil Avatar answered Sep 30 '22 08:09

Marandil