1

I am trying to do a cross-component encryption working between different system. Currently, I can't really match the outputs. I am using openssl/aes/h library for C and javax.crypto for Java/Scala. I don't have much flexibility in changing the code in the C system, so that leaves me in the need of modifying the Java/Scala code.

#include <string.h> 
#include <openssl/aes.h>

char key[] = "aaaaaaaaaaaaaaaa";

int main(){
 unsigned char text[]="hello";
 unsigned char enc_out[80];
 unsigned char dec_out[80];

 AES_KEY enc_key, dec_key;

 AES_set_encrypt_key(key, 128, &enc_key);
 AES_encrypt(text, enc_out, &enc_key);
 return 0
}

The C code roughly looks like that.Using the AES_set_encrypt_key and AES_encrypt calls except there is padding with 0s when needed (when key is not 128 bits). The padding shouldnt matter for this simple example.

In the scala code I have:

  def aes_encrypt(value: String, k: String): Array[Byte] = {
    val cipher = Cipher.getInstance("AES")
    val key = Arrays.copyOf(k.getBytes("UTF-8"), 16)
    val secretKey: SecretKeySpec = new SecretKeySpec(key, 0, key.length, "AES")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    cipher.doFinal(value.getBytes("UTF-8"), 0, value.getBytes("UTF-8").length)
  }

Using the key "aaaaaaaaaaaaaaaa" and message "hello" , in base64

The C code gives "XaiMOCo7KteRUkejpd8JLA=="

in Scala/Java "7GHRfFgKVdaAiwti36tqDQ=="

I am trying to match the outputs, but I have not been able to do it. I have tried different algorithm for the Scala code, but I am not sure how to match the C output.

I have tried to look into the documentation of AES_set_encrypt_key and AES_encrypt calls, but I couldn't dig too much out of it. I know that there are better user friendly calls from the openssl library like evp_encryption calls, but I can't get away from AES_set_encrypt_key and AES_encrypt

Andrew
  • 23
  • 3
  • 1
    You really should be using the `EVP_*` functions, because they include padding by default. Whereas the functions from aes.h are rather basic and are probably not supposed to be used at all. – Artjom B. Aug 10 '16 at 21:26
  • 1
    You should *not* use `AES_encrypt` and friends. That's a software-only implementation, so you will not enjoy hardware support, like AES-NI. You should be using `EVP_*` functions. See [EVP Symmetric Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption) on the OpenSSL wiki. In fact, you should probably be using authenticated encryption because it provides *both* confidentiality and authenticity. See [EVP Authenticated Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption) on the OpenSSL wiki. – jww Aug 10 '16 at 21:40

1 Answers1

1

I am trying to match the outputs, but I have not been able to do it...

For OpenSSL, you are operating the cipher in ECB mode, and you are responsible for the padding. I believe OpenSSL is encrypting 16 bytes composed of 6 characters (hello\0) and 10 garbage characters.

For Java, I believe you are using the cipher with AES/ECB/PKCS5Padding. value: String is unconditionally padded using PKCS padding. The padding also means 16 bytes of plaintext will expand to 32 bytes of ciphertext while OpenSSL remains at 16.

In cases like this, its often good to go back to test vectors to see which implementation is diverging. For AES, you often use Brian Gladman's vectors.


I have tried to look into the documentation of AES_set_encrypt_key and AES_encrypt calls, but I couldn't dig too much out of it. I know that there are better user friendly calls from the openssl library like evp_encryption calls, but I can't get away from AES_set_encrypt_key and AES_encrypt

OpenSSL does not document them because they don't want you using them. If you stay with AES_encrypt and friends, then you are married to a software-only implementation. You will also suffer endian-ness issues on occasion because EVP interfaces handle that for you. Finally, you will not enjoy hardware acceleration, like AES-NI on i686 or AES on ARMv8.


The C code gives XaiMOCo7KteRUkejpd8JLA==, in Scala/Java 7GHRfFgKVdaAiwti36tqDQ==

Related, the Scala/Java is correct if you are aiming for ECB mode with PKCS padding. I can duplicate the Scala/Java results with Crypto++ (its StreamTransformationFilter uses PKCS padding by default):

int main(int argc, char* argv[])
{
  byte key[] = "aaaaaaaaaaaaaaaa";
  ECB_Mode<AES>::Encryption enc(key, 16);
  string result;

  StringSource ss(string("hello"), true,
                  new StreamTransformationFilter(enc,
                       new Base64Encoder(
                           new StringSink(result))));

  cout << result << endl;

  return 0;
}

From Comments

I will try to do AES/ECB/NoPadding and the do the padding myself in Scala.

You will still need to avoid the garbage characters in the C program. Maybe something like the following will help you match Java's PKCS #7 padding (0x0b applied 11 times):

unsigned char text[] = "hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";

Here's a larger string padded with PKCS #7 padding (0x06 applied 6 times):

unsigned char text[] = "hellohello\x06\x06\x06\x06\x06\x06";

If the string is 16 in length, then apply 0x10 16 times.


When using ECB, does it require an IV vector?

No. ECB mode does not require an IV. It encrypts a 16-byte block under a key (and nothing more).

jww
  • 97,681
  • 90
  • 411
  • 885
  • Thanks for the info. I will try to do AES/ECB/NoPadding and the do the padding myself in Scala. When using ECB, does it require an IV vector? and if so what would be the default openssl IV vector be? – Andrew Aug 10 '16 at 22:02
  • @Andrew - be sure to verify the provider's default AES implementation is `AES/ECB/PKCS5Padding`. Also see [Java default Crypto/AES behavior](http://stackoverflow.com/q/6258047). – jww Aug 10 '16 at 22:35
  • Thanks jww! I changed to `Cipher.getInstance("AES/ECB/NoPadding") ` and padded the message byte array with 0s until it's a multiple of 16. `Arrays.copyOf(value.getBytes("UTF-8"), paddedLength)` That gave me the same outputs. I will do some more testing if filling it with 0s is just as good as filling it with `0x0b ` – Andrew Aug 10 '16 at 22:46