I'm working on a server and client program. Data will be exchanged between the server and client. This data will be encrypted.
The server will always generate the salt and the IV. The client will receive the salt and IV and use it to encrypt / decrypt the data that it will send / recieve.

The only problem is that when I try to encrypt data with my client, I get java.lang.NullPointerException - I dont know why.

Here is my code:

Main:

public static void main(String[] args) throws Exception {
        String password = "weak_password";
        String message = "TOP SECRET DATA.";
        byte[] cipherText;
        byte[] iv;
        byte[] salt;
        String decrypted;

        AESCipher server = new AESCipher(password);
        salt = server.getSalt();
        iv = server.getIV();
        AESCipher client = new AESCipher(password, salt, iv);

        System.out.println("Original data: " + message);
        cipherText = server.encrypt(message.getBytes());
        System.out.println("Cipher text from server: " + new String(cipherText));
        decrypted = new String(client.decrypt(cipherText));
        System.out.println("Data decrypted by client: " + decrypted);

        cipherText = client.encrypt(message.getBytes());
        System.out.println("Cipher text from client: " + new String(cipherText));
        decrypted = new String(server.decrypt(cipherText));
        System.out.println("Data decrypted by server: " + decrypted);
    }

AES Cipher:

public class AESCipher {
    private String password;
    private byte[] iv;
    private byte[] salt;
    private final int passwordIterations = 78882;
    private final int keySize = 128;
    private SecretKeyFactory factory;
    private PBEKeySpec spec;
    private SecretKey secretKey;
    private SecretKeySpec secret;
    private Cipher encryptCipher;
    private Cipher decryptCipher;

    /**
     * Generates a secure random salt.
     */
    public void generateSalt() {
        byte[] randomBytes = new byte[16];

        SecureRandom random = new SecureRandom();
        random.nextBytes(randomBytes);

        this.salt = randomBytes;
    }

    /**
     * Generates an IV.
     * @throws Exception 
     *   If something went wrong with padding scheme or IV generation.
     */
    public void generateIV() throws Exception {
        AlgorithmParameters params;

        encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        encryptCipher.init(Cipher.ENCRYPT_MODE, secret);
        params = encryptCipher.getParameters();

        iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    }

    /**
     * Generates the key from the password.
     * @throws Exception 
     *   If something went wrong with the key generation.
     */
    private void generateKey() throws Exception {
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        spec = new PBEKeySpec(this.password.toCharArray(), this.salt, 
                                         this.passwordIterations, this.keySize);
        secretKey = factory.generateSecret(spec);
        secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    }

    /**
     * Gets the salt so that it can be sent to the client.
     * @return 
     *   The salt.
     */
    public byte[] getSalt() {
        return this.salt;
    }

    /**
     * Gets the IV so that it can be sent to the client.
     * @return 
     */
    public byte[] getIV() {
        return this.iv;
    }

    /**
     * Encrypts data.
     * @param plainText
     *   The data to encrypt.
     * @return
     *   Encrypted data.
     * @throws Exception
     *   If something went wrong with the encryption.
     */
    public byte[] encrypt(byte[] plainText) throws Exception {
        byte[] encrypted;

        encrypted = encryptCipher.doFinal(plainText);

        return encrypted;
    }

    /**
     * Decrypts data.
     * @param cipherText
     *   The data to decrypt.
     * @return
     *   Decrypted data.
     * @throws Exception
     *   If something went wrong with the decryption.
     */
    public byte[] decrypt(byte[] cipherText) throws Exception {
        byte[] decrypted;

        decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

        decrypted = decryptCipher.doFinal(cipherText);

        return decrypted;
    }

    /**
     * Class initializer for server encryption.
     * @param password
     *   The password that will be used for the key.
     * @throws Exception 
     *   If something went wrong with the key generation.
     */
    public AESCipher(String password) throws Exception {
        this.password = password;

        generateSalt();
        generateKey();
        generateIV();
    }

    /**
     * Class initializer for client encryption.
     * @param password
     *   The password that will be used for the key.
     * @param salt
     *   The salt that was received from the server.
     * @param iv
     *   The IV that was received from the client.
     * @throws Exception 
     *   If something went wrong with the key generation.
     */
    public AESCipher(String password, byte[] salt, byte[] iv) throws Exception {
        this.password = password;
        this.salt = salt;
        this.iv = iv;

        generateKey();
    }
}

Sample output:

Original data: TOP SECRET DATA.
Cipher text from server: v~vTȟzPIiGHE
Exception in thread "main" java.lang.NullPointerException
    at confusion.encryption.AESCipher.encrypt(AESCipher.java:93)
    at central.control.system.CentralControlSystem.main(CentralControlSystem.java:29)
Java Result: 1

Edited 1 Year Ago by CodeWarrior14

Thanks. That resolved that isue, but now the program doesn't decrypt the data correctly.

Sample output:
Original data: TOP SECRET DATA.
Cipher text from server: {OdUxJ,']a~6#w㨳K
Data decrypted by client: TOP SECRET DATA.
Cipher text from client: bxAF@ز\Z[]u(k&
Data decrypted by server: ,~ݜG9h-<)

I dont know why this happens as I have set the IV and salt on both sides to be the same.

I would take a few minutes to print out ALL the variables after the server & client have been initialised. My guess is that you will find something that does not get initialised to the same value.

No, my salt and IV is the same, the key must be the same then

Output:

Server iv | salt: cs<gd,h | (2?*_W?v
Client iv | salt: cs<gd,h | (2?*_W?v

Edited 1 Year Ago by CodeWarrior14

I was able to fix it, for some strange reason, I had to create two new variables that hold my clientCipherText and my clientDecryptedText... I dont know why that is...

Code:

String password = "weak_password";
        String message = "TOP SECRET DATA.";
        byte[] cipherText;
        byte[] iv;
        byte[] salt;
        String decrypted;

        // The two new variables:
        byte[] clientCipher;
        String clientD;

        AESCipher server = new AESCipher(password);
        salt = server.getSalt();
        iv = server.getIV();
        AESCipher client = new AESCipher(password, salt, iv);

        System.out.println("Original data: " + message);
        cipherText = server.encrypt(message.getBytes());
        System.out.println("Cipher text from server: " + new String(cipherText));
        decrypted = client.decrypt(cipherText);
        System.out.println("Data decrypted by client: " + decrypted);

        clientCipher = client.encrypt(message.getBytes());
        System.out.println("Cipher text from client: " + new String(clientCipher));
        clientD = server.decrypt(cipherText);
        System.out.println("Data decrypted by server: " + clientD);

        System.out.println("Server iv | salt: " + new String(server.getIV()) + " | " + new String(server.getSalt()));
        System.out.println("Client iv | salt: " + new String(client.getIV()) + " | " + new String(client.getSalt()));
    }

Also, is a salt of 2048 bytes strong enough?
And what is the optimal number of times for password iteration to make it really secure.

Edited 1 Year Ago by CodeWarrior14

This question has already been answered. Start a new discussion instead.