AES Decryption when writing to a file using base64 encoding

Viewed 99

I'm implementing the following steps:

  1. Bob encrypts a message with AES
  2. Bob encodes the encryption to a base64 string
  3. Bob writes the string to a file
  4. Alice reads the file
  5. Alice converts the string to byte
  6. Alice decrypts the message

However, I'm failing at the last step.

Heres the encryption:

public void sendHelloWorld() {
    String msg = "hello world!";
    try {
        SecretKeySpec AesKeySpec = new SecretKeySpec(aesKeyBytes, "AES");
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.ENCRYPT_MODE, AesKeySpec);
        aesEncryptedHello = c.doFinal(msg.getBytes());
    } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    String s = new String(aesEncryptedHello, StandardCharsets.UTF_8);
    System.out.println("Message = " + s);
    sendMessage(Base64.getEncoder().encodeToString(aesEncryptedHello));
}

Here's the decrypt:

public void decryptAESMessage(byte[] encryptedMessage) {
    try {
        String s = new String(encryptedMessage, StandardCharsets.UTF_8);
        System.out.println("Message = " + s);
        SecretKeySpec AesKeySpec = new SecretKeySpec(bobAesKey, "AES");
        Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
        c.init(Cipher.DECRYPT_MODE, AesKeySpec);
        byte[] aesMessage = c.doFinal(encryptedMessage);
        String message = new String(aesMessage, StandardCharsets.UTF_8);
        System.out.println("Message is " + message);
    } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    
}

Here's the write to file:

public void sendMessage(String msg) {
    try {
        
        FileWriter myWriter = new FileWriter(<myredactedfilepath>);
        myWriter.write(msg);
        myWriter.close();
    } catch (IOException e) {
        System.out.println("An error occurred.");}
}

Here's stage 5, just to show the decode isn't missed:

alice.decryptAESMessage(Base64.getDecoder().decode(alice.readEncryptedMessage()));

I print the keys and the encrypted messages at both ends and they match.

1 Answers

In the Encryption part, you used AES/CBC/PKCS5Padding that is CBC mode of operation with PKCS#5 - actually, it is PKCS#7 - the there is an IV is missing. The CBC mode requires and IV and that should be at least random. That can be generated with

SecureRandom randomSecureRandom = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

In the decryption part, you used AES/ECB/PKCS5Padding. Normally both must match. Make both either ECB ( please don't) or CBC with the IV.

The IV in generally, appended to the beginning of the ciphertext. During decryption resolved it and use it.

Also, make the base64 decoding inside of the decryption. This will make your mode more robust.

Final note: if there is no specific reason, prefer AES-GCM which can provide not only confidentiality but also integrity and authentication. If you need to stick CBC you need additionally use HMAC to achieve integrity and authentication.

And for a full code example; See the Maartens answer

Related