I'm getting crazy to encrypt/decrypt between c and Java but so far the encrypted string in Java and the one in c don't look the same. I've investigated base64 encoding/decoding but after getting crazy to find a library for java and c the respective base64 results looked different! I think it's a problem of converting between Java UTF16 string, saving Byte in java or perhaps AES options (128/256 key, PK5 padding or who knows what), or maybe the UTF8 conversion of the terminal or an absurd combination of the above. So far I get:
user1@comp1:~/Desktop$ gcc AES.c /usr/lib/libmcrypt.a -lssl -lcrypto -lpthread
user1@comp1:~/Desktop$ /usr/java/jdk1.6.0_25/bin/javac AES.java
user1@comp1:~/Desktop$ ./a.out
Before encryption: test text 123
After encryption: 49 -60 66 43 -8 66 -106 0 -14 -44 3 47 65 127 -110 117
After decryption: test text 123
user1@comp1:~/Desktop$ java AES
Before encryption: test text 123
After encryption: -110 21 23 59 47 120 70 -93 -54 -93 -12 -70 -91 83 -113 85
After decryption: test text 123
I think I really need help here from someone into low level coding, below is the code for Java and c respectively:
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
public static void main(String [] args) {
try {
String text = "test text 123";
/*fixed here now it is 128 bits = 16 Bytes*/
String encryptionKey = "E072EDF9534053A0";
System.out.println("Before encryption: " + text);
byte[] cipher = encrypt(text, encryptionKey);
System.out.print("After encryption: ");
for (int i=0; i<cipher.length; i++)
System.out.print(new Integer(cipher[i])+" ");
System.out.println("");
String decrypted = decrypt(cipher, encryptionKey);
System.out.println("After decryption: " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes("UTF-8"));
}
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
}
and
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mcrypt.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
int main()
{
MCRYPT td, td2;
const char * plaintext = "test text 123";
int i;
char *key; /* created using mcrypt_gen_key */
char *IV;
char * block_buffer;
int blocksize;
int keysize = 16; /* 128 bits == 16 bytes */
size_t* sizet;
key = calloc(1, keysize);
/*below dirty trick to be sure the entire key has been padded with \0's */
strcpy(key, "E072EDF9534053A0");
memset(key, '\0', sizeof(key));
strcpy(key, "E072EDF9534053A0");
/* MCRYPT mcrypt_module_open( char *algorithm, char* algorithm_directory, char* mode, char* mode_directory);
* This function normally returns an encryption descriptor, or MCRYPT_FAILED on error.
*/
td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
/*we need two encryption descriptors td and td2 for decryption*/
td2 = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
blocksize = mcrypt_enc_get_block_size(td);
block_buffer = calloc(1, blocksize);
/*below to be sure the entire block_buffer has been padded with \0's */
memset(block_buffer, '\0', blocksize);
IV = malloc(mcrypt_enc_get_iv_size(td));
if ((block_buffer == NULL) || (IV == NULL)) {
fprintf(stderr, "Failed to allocate memory\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < mcrypt_enc_get_iv_size(td); i++) {
IV[i] = 0;
}
/*as we can see both td and td2 get same key and IV*/
mcrypt_generic_init(td, key, keysize, IV);
mcrypt_generic_init(td2, key, keysize, IV);
memset(block_buffer, '\0', sizeof(plaintext));
strcpy(block_buffer, plaintext);
printf("Before encryption: %s\n", block_buffer);
mcrypt_generic (td, block_buffer, blocksize);
printf("After encryption: ");
for (i=0; i < blocksize; i++)
printf("%d ", block_buffer[i]);
printf("\n");
mdecrypt_generic (td2, block_buffer, blocksize);
printf("After decryption: %s\n", block_buffer);
/* deinitialize the encryption thread */
mcrypt_generic_deinit (td);
mcrypt_generic_deinit(td2);
/* Unload the loaded module */
mcrypt_module_close(td);
mcrypt_module_close(td2);
return 0;
}
After resolving all the issues, I get:
$ ./a.out
==C==
plain: test text 123
cipher: 16 -124 41 -83 -16 -123 61 -64 -15 -74 87 28 63 30 64 78
decrypt: test text 123
$java AES
==JAVA==
plain: test text 123
cipher: 16 -124 41 -83 -16 -123 61 -64 -15 -74 87 28 63 30 64 78
decrypt: test text 123
See below for the code.
Wrong Cipher: AES is Rijndael-128, which is what the Java encryption uses. However, your C code specifies Rijndael-256, which is not AES. From the Mcrypt docs:
Rijndael [...] AES if used in 128-bit mode
Remember that when referring to a cipher as CIPHER-XXX, XXX refers to the block size, not the key length. Indeed, Rijndael-128 will accept keys of 128, 192 and 256 bits. AES, however, refers strictly to 128 bit Rijndael. You will find more information at the usual place.
Incorrect memory initialisation: You do not initialise your memory properly in C, which leaves the bytes between the end of your message and the block limit undefined.
Incorrect Padding: You specify PKCS5Padding
in your java code, but you do not pad your plaintext appropriately in C. PKCS5 is actually trivially simple, and is described quite well on the wiki. To pad with PKCS#5, simply ensure that every padding byte is equal to the total length padded:
... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD 03 03 03 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD 02 02 |
etc...
Where DD are data bytes. There are other padding methods, that are explained elsewhere.
Incorrect key handling: You extract the key for your Java program from an hexadecimal encoded string, whereas in your C program, you take the hexadecimal string directly as your key. You must handle your keys consistently for both programs to give the same output.
Different Initialization Vectors on either side: You will need the same Initialization Vector on both sides. Randomly generating your IV is correct. The IV should then be passed along with the cipher-text for decryption on the other side. IVs should not be reused.
The following are available on github
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
/*
* Please realise that the following IV is terrible.
* (As easy to crack as ROT13...)
* Real situations should use a randomly generated IV.
*/
static String IV = "AAAAAAAAAAAAAAAA";
/*
* Note null padding on the end of the plaintext.
*/
static String plaintext = "test text 123\0\0\0";
static String encryptionKey = "0123456789abcdef";
public static void main(String [] args) {
try {
System.out.println("==JAVA==");
System.out.println("plain: " + plaintext);
byte[] cipher = encrypt(plaintext, encryptionKey);
System.out.print("cipher: ");
for (int i=0; i<cipher.length; i++){
System.out.print(new Integer(cipher[i])+" ");
}
System.out.println("");
String decrypted = decrypt(cipher, encryptionKey);
System.out.println("decrypt: " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String encryptionKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainText.getBytes("UTF-8"));
}
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
}
And the C file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* MCrypt API available online:
* http://linux.die.net/man/3/mcrypt
*/
#include <mcrypt.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
int encrypt(
void* buffer,
int buffer_len, /* Because the plaintext could include null bytes*/
char* IV,
char* key,
int key_len
){
MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
int blocksize = mcrypt_enc_get_block_size(td);
if( buffer_len % blocksize != 0 ){return 1;}
mcrypt_generic_init(td, key, key_len, IV);
mcrypt_generic(td, buffer, buffer_len);
mcrypt_generic_deinit (td);
mcrypt_module_close(td);
return 0;
}
int decrypt(
void* buffer,
int buffer_len,
char* IV,
char* key,
int key_len
){
MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
int blocksize = mcrypt_enc_get_block_size(td);
if( buffer_len % blocksize != 0 ){return 1;}
mcrypt_generic_init(td, key, key_len, IV);
mdecrypt_generic(td, buffer, buffer_len);
mcrypt_generic_deinit (td);
mcrypt_module_close(td);
return 0;
}
void display(char* ciphertext, int len){
int v;
for (v=0; v<len; v++){
printf("%d ", ciphertext[v]);
}
printf("\n");
}
int main()
{
MCRYPT td, td2;
char * plaintext = "test text 123";
char* IV = "AAAAAAAAAAAAAAAA";
char *key = "0123456789abcdef";
int keysize = 16; /* 128 bits */
char* buffer;
int buffer_len = 16;
buffer = calloc(1, buffer_len);
/*
* Note that calloc() will null-initialise the memory. (Null padding)
*/
strncpy(buffer, plaintext, buffer_len);
printf("==C==\n");
printf("plain: %s\n", plaintext);
encrypt(buffer, buffer_len, IV, key, keysize);
printf("cipher: "); display(buffer , buffer_len);
decrypt(buffer, buffer_len, IV, key, keysize);
printf("decrypt: %s\n", buffer);
return 0;
}
Tested on Linux with the latest Libmcrypt and Java 1.7. Watch out, since I wrote the C in a rush, and it is full of memory leaks and overflow problems. (Exercise left to the reader to clean it up, as they say...)