2

I would like to encrypt bytes in Java using AES/CFB/NoPadding.

I found the following question on Stackoverflow, but it only covers the decrypt function: AES Encryption in Golang and Decryption in Java

How would I go about writing a similar encryption function in Java as the following Go code?

package main

import (
    "io"
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "crypto/rand"
)

func encrypt(key, data []byte) string {
    block, err := aes.NewCipher(key)
    if err != nil {
      return nil, err
    }
    encoded := base64.StdEncoding.EncodeToString(data)
    ciphertext := make( []byte, aes.BlockSize+len(encoded) )
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
      return nil, err
    }
    cfb := cipher.NewCFBEncrypter(block, iv)
    cfb.XORKeyStream( ciphertext[aes.BlockSize:], []byte(encoded) )
    return ciphertext, nil
}

My Golang decryption function looks like this (it should return base64 code):

func decrypt(key, data []byte) ([]byte, error) {
  blockcipher, err := aes.NewCipher(key)
  if err != nil {
    return nil, err
  }
  if len(data) < aes.BlockSize {
    return nil, errors.New("ciphertext too short")
  }
  iv := data[:aes.BlockSize]
  data = data[aes.BlockSize:]
  cfb := cipher.NewCFBDecrypter(blockcipher, iv)
  cfb.XORKeyStream(data, data)
  return data, nil
}

My current Java encryption code (which I can't seem to decrypt) looks like this:

private byte[] encrypt(byte[] payload) {
    try {
        SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
        byte[] encoded_payload = Base64.encode(payload, Base64.DEFAULT);
        IvParameterSpec iv = new IvParameterSpec( new byte[16] );
        cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv);
        return cipher.doFinal(encoded_payload);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new byte[0];
}

My encryption code looks like this (and works fine across both Golang and Java):

private byte[] decrypt(byte[] payload) {
    try {
        SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
        int block_size = cipher.getBlockSize();
        IvParameterSpec iv = new IvParameterSpec( Arrays.copyOf(payload, block_size) );
        byte[] decryption_data = Arrays.copyOfRange(payload, block_size, payload.length);
        cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
        byte[] decrypted_payload = cipher.doFinal(decryption_data);
        return Base64.decode(decrypted_payload, Base64.DEFAULT);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new byte[0];
}

When I encrypt something in Java, and then try to use my decryptor in Java, I get the following decryption error:

04-13 14:16:48.382 3791-3791/com.domain.interpretest W/System.err: java.lang.IllegalArgumentException: 16 > 9
04-13 14:16:48.388 3791-3791/com.domain.interpretest W/System.err:     at java.util.Arrays.copyOfRange(Arrays.java:3447)
tarmek
  • 59
  • 4
  • Possible duplicate of [How to encrypt data using AES in Java](https://stackoverflow.com/questions/5108926/how-to-encrypt-data-using-aes-in-java) – Trevor V Apr 05 '18 at 02:11
  • Trevor, please read my question properly. I do not want CBC encryption, I want CFB encryption. My question also does not show any indication of me wanting to use my own IV – tarmek Apr 05 '18 at 02:32
  • 1
    Stackoverflow is not a code translation service. Ciphers are standardized, give it a try yourself, why don't you? One hint for the trouble: try "CFB8" if "CFB " doesn't work in Java. Good luck. – Maarten Bodewes Apr 07 '18 at 20:28
  • Hi Maarten, thanks for trying to answer my question. It did not work however. I can successfully decrypt data which was encrypted using the above Golang code. I'm not sure why you tell me this is no code translation service, I think adding examples of my code is the best/only way to describe my question in absolute detail. – tarmek Apr 13 '18 at 04:09
  • @MaartenBodewes even if you would just comment simple steps (not code) as to how I should go about creating a AES/CFB/NoPadding using XORstream in Java, I would be so appreciative. – tarmek Apr 13 '18 at 04:25
  • I have updated my question in order to indicate that I am not too lazy to code or figure it out myself. I have spent countless hours trying to figure this out, my utmost final resort is asking people on Stackoverflow. – tarmek Apr 13 '18 at 04:32

1 Answers1

1

Summary of your Go encryption:

  • encrypt the base64-ed data with a random IV (and key you don't describe), putting the IV followed by the ciphertext in a single buffer

Summary of your Java decryption:

  • take the first block from the buffer and use it as IV to decrypt the remainder of the buffer, and de-base64 it

These match.

Summary of your Java encryption:

  • encrypt with a fixed (all-zero) IV, and return a buffer containing only the ciphertext, not the IV anyplace

This does not match. The decryption tries to remove the IV from a buffer that doesn't have the IV in it. In fact your ciphertext is only 9 bytes (although I don't understand why it's not a multiple of 4); this is shorter than one AES block, so the Arrays.copyOfRange fails outright.

Solution: your Java encryption should use a random IV and return a buffer containing the IV followed by the ciphertext, like your Go encryption does. One approach that fairly closely mimics your Go:

// once, during initialization 
SecureRandom rand = new SecureRandom(); // or .getInstance* as you prefer 

// unchanged 
SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
byte[] encoded_payload = Base64.encode(payload, Base64.DEFAULT);
// changed 
int block_size = cipher.getBlockSize();
// create random IV
byte[] buffer = new byte[block_size];
rand.nextBytes(buffer);
IvParameterSpec iv = new IvParameterSpec (buffer);
// expand buffer already containing IV to make room for ciphertext
buffer = Arrays.copyOf (buffer, block_size+encoded_payload.length);
// unchanged 
cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv);
// changed
// do encryption into correct part of existing buffer
cipher.doFinal(encoded_payload,0,encoded_payload.length, buffer,block_size);
return buffer;

PS: why are you bothering to base64 encode, and decode, your plaintext? AES, like all modern ciphers, can handle any combination of bits just fine. It is more common to base64 the ciphertext and IV when other code can't handle them as bits/binary.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • 1
    Thank you very much, but I'm still not able to use the Golang function to decrypt data that was encrypted using the Java code you proposed. Decryption and encryption in Java works fine now as far as I can tell. – tarmek Apr 15 '18 at 03:32
  • I have added my Golang decrypt function for clarity. My key is 32 bytes. – tarmek Apr 15 '18 at 03:52