Poni
Poni

Reputation: 11317

AES with a salt and a static password

I have the following code which almost works:

AES.java

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {

    private static final int    KEY_LENGTH              = 128;
    private static final int    ITERATIONS              = 100;

    private static final String ALGORITHM               = "AES";
    private static final String SECRET_KEY_ALGORITHM    = "PBKDF2WithHmacSHA1";
    private static final String TRANSFORMATION          = "AES/CBC/PKCS5Padding";

    private final Cipher        m_enc_cipher;
    private final Cipher        m_dec_cipher;

    private final byte[]        m_iv;

    public AES(final char[] password, final byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException,
            NoSuchPaddingException, InvalidKeyException,
            InvalidParameterSpecException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException,
            InvalidAlgorithmParameterException {

        // Derive the key, given password and salt
        final SecretKeyFactory factory = SecretKeyFactory
                .getInstance(SECRET_KEY_ALGORITHM);
        final KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS,
                KEY_LENGTH);
        final SecretKey tmp = factory.generateSecret(spec);
        final SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);

        // Build encryptor and get IV
        final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION);
        enc_cipher.init(Cipher.ENCRYPT_MODE, secret);
        final AlgorithmParameters params = enc_cipher.getParameters();
        final byte[] iv = params.getParameterSpec(IvParameterSpec.class)
                .getIV();

        // Build decryptor
        final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION);
        dec_cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

        this.m_enc_cipher = enc_cipher;
        this.m_dec_cipher = dec_cipher;

        this.m_iv = iv;
    }

    public AES(final byte[] iv) throws NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, InvalidParameterSpecException,
            IllegalBlockSizeException, BadPaddingException,
            UnsupportedEncodingException, InvalidAlgorithmParameterException {

        final AlgorithmParameterSpec aps = new IvParameterSpec(iv);

        final KeyGenerator keygen = KeyGenerator.getInstance(ALGORITHM);
        keygen.init(KEY_LENGTH);
        final SecretKey secret = keygen.generateKey();

        // Build encryptor
        final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION);
        enc_cipher.init(Cipher.ENCRYPT_MODE, secret, aps);

        // Build decryptor
        final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION);
        dec_cipher.init(Cipher.DECRYPT_MODE, secret, aps);

        this.m_enc_cipher = enc_cipher;
        this.m_dec_cipher = dec_cipher;

        this.m_iv = iv;
    }

    public byte[] get_iv() {
        return this.m_iv;
    }

    public byte[] encrypt(final byte[] data) throws NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, InvalidParameterSpecException,
            IllegalBlockSizeException, BadPaddingException,
            UnsupportedEncodingException {
        return this.m_enc_cipher.doFinal(data);
    }

    public byte[] decrypt(final byte[] data) throws IllegalBlockSizeException,
            BadPaddingException {
        return this.m_dec_cipher.doFinal(data);
    }
}

AESTest.java

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.junit.Test;
import static org.junit.Assert.*;

public class AESTest {
    @Test
    public void test() throws InvalidKeyException, NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidParameterSpecException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException,
            InvalidAlgorithmParameterException {

        final char[] password = "my_password".toCharArray();
        final byte[] salt = new byte[] { 22, 11 };

        final byte[] original_data = "Hello, World!".getBytes("UTF-8");
        final AES aesA = new AES(password, salt);
        final byte[] encrypted_data = aesA.encrypt(original_data);
        System.out.println("Encrypted:");
        System.out.println(javax.xml.bind.DatatypeConverter
                .printBase64Binary(encrypted_data));

        final AES aesB = new AES(aesA.get_iv());
        final byte[] decrypted_data_B = aesB.decrypt(encrypted_data);
        System.out.println("Decrypted B:");
        System.out.println(javax.xml.bind.DatatypeConverter
                .printBase64Binary(decrypted_data_B));
        assertTrue(Arrays.equals(original_data, decrypted_data_B));
    }
}

On the test (AESTest.java), it gives me this error:

javax.crypto.BadPaddingException: Given final block not properly padded

My final purpose is to be able to send an encrypted data block and later get it back.
The term "later" could refer to a day/week/year.
Then, using the same password I want to decrypt it.

Because "later" might be a month I'll need to create a new AES object.

Now, this new AES object must be able to decrypt the data with the same fixed password/salt.
That's all.

I've tried to use the same IV but it doesn't work.

What am I doing wrong here?

Edit #1: Please pay attention to ITERATIONS as well.

Upvotes: 0

Views: 4339

Answers (2)

emboss
emboss

Reputation: 39620

As already stated, the second constructor creates a new AES key, so it won't match with the one created in the first constructor. I don't really get your intention in having two different constructors, where in each you create an AES instance fit for encryption as well as another one for decryption? It might make sense to rethink that design and use just one single instance only, depending on whether you want to encrypt or decrypt.

While the key you generate in the first constructor is to be kept secret, the IV being produced is public information and can be safely published publicly, this won't harm the security. However, you should create unique IVs for every encrypted message, otherwise there's attacks that are possible against the CBC mode you are using.

So here's how I would recommend you do it:

Encryption:

Basically the first constructor, leaving out the second instance for decryption. Retrieve the IV that was created.

Decryption:

Again basically the first constructor, with an additional IV parameter. Create the key again from scratch with exactly the same parameters (salt, password, iterations), and this time init the Cipher in decryption mode, additionally passing the IV parameter.

That should do it!

Upvotes: 2

dash1e
dash1e

Reputation: 7807

When you init AES by iv you create a different secret. Isnt's it wrong?

You are not using the same password and salt.

Upvotes: 3

Related Questions