Me estoy volviendo loco para cifrar/descifrar entre c y Java, pero hasta el momento la cadena cifrada en Java y en c no tienen el mismo aspecto.
He investigado en base64 de codificación/decodificación, pero después de conseguir loco para encontrar una biblioteca de java y c los respectivos base64 resultados se veía diferente!
Creo que es un problema de la conversión entre Java UTF16 cadena, el ahorro de Bytes de java o tal vez AES opciones (128/256 clave, PK5 relleno o quién sabe qué), o tal vez la UTF8 conversión del terminal o una absurda combinación de los anteriores.
Hasta el momento de recibir:

  [email protected]:~/Desktop$ gcc AES.c /usr/lib/libmcrypt.a -lssl -lcrypto -lpthread
  [email protected]:~/Desktop$ /usr/java/jdk1.6.0_25/bin/javac AES.java
  [email protected]:~/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
  [email protected]:~/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

Creo que realmente necesito la ayuda de alguien en el bajo nivel de codificación, a continuación se muestra el código para Java y c, respectivamente:

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");
}
}

y

#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;
}
Si no fui lo suficientemente claro, quiero que la cadena cifrada impreso a la terminal a buscar el mismo en Java y c de salida, de esta manera voy a administrar más tarde para enviar que entre el cliente de Java y c del servidor y al revés.
también se puede simplemente imprimir los datos cifrados como hexadecimal (mucho más fácil)
He intentado eso, pero la impresión hex en el c no es tan simple (yo vi un debate aquí en stackoverflow con 10 diferentes soluciones para hacer eso y ni siquiera una referencia a un estándar de la función de c!). Aparte de ese hex no es muy eficiente, es por eso que probé por primera vez codificar en base64, si usted puede hacer los dos anteriores trabajos con hex sin embargo, yo estaría agradecido
si se utiliza unix(debe!) puede utilizar el comando hexdump como un filtro.
AES RIJNDAEL de 128, no 256?

OriginalEl autor dendini | 2012-04-20

2 Comentarios

  1. 13

    Resumen

    Después de resolver todos los problemas, me sale:

    $ ./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

    Vea a continuación para el código.

    Problemas

    1. Mal de Cifrado: AES es Rijndael-128, que es lo que el Java de cifrado que utiliza. Sin embargo, su código C especifica Rijndael de 256, que no es AES. Desde el Mcrypt docs:

      Rijndael […] AES si se utiliza en el modo de 128 bits

      Recordar que cuando se refiere a un sistema de cifrado como de CIFRADO-XXX, XXX se refiere a que el tamaño de bloque, no la longitud de la clave. De hecho, Rijndael-128 aceptará claves de 128, 192 y 256 bits. AES, sin embargo, se refiere estrictamente a Rijndael de 128 bits. Encontrará más información en el lugar de siempre.

    2. De memoria incorrecta de inicialización: no activa su memoria correctamente en C, en la que abandona la bytes entre el final de su mensaje y el límite de bloque indefinido.

    3. Incorrecto Relleno: especifica PKCS5Padding en el código java, pero no de la almohadilla de su texto de manera adecuada en la C. PKCS5 en realidad es extremadamente simple, y se describe bastante bien en la wiki. Para rellenar con PKCS#5, simplemente asegurarse de que cada byte de relleno es igual a la longitud total del collar:

      ... | 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...

      Donde DD son los bytes de datos. Hay otros métodos de relleno, que son explicado en otra parte.

    4. Clave incorrecta manipulación: de extraer la clave para el programa de Java a partir de una cadena codificada en hexadecimal, mientras que en su programa en C, se toma la cadena hexadecimal directamente como su clave. Usted debe controlar sus claves de forma coherente para ambos programas a dar el mismo resultado.

    5. Diferentes Inicialización de Vectores en cualquier lado: tendrá el mismo Vector de Inicialización en ambos lados. Al azar de generar el IV es correcta. El IV luego debe ser aprobada junto con el cifrado de texto para el descifrado en el otro lado. IVs no deben ser reutilizados.

    Programas de ejemplo

    Los siguientes están disponibles en 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");
    }
    }

    Y el archivo C:

    #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;
    }

    Probado en Linux con la última Libmcrypt y Java 1.7. Cuidado, desde que escribí la C en un apuro, y que está lleno de pérdidas de memoria y problemas de desbordamiento. (Ejercicio izquierda para el lector de limpiar, como dicen…)

    Traté de NoPadding y yo tengo el error javax.crypto.IllegalBlockSizeException: Entrada de la longitud no múltiplo de 16 bytes, mientras que el c código funciona bien, así que creo que mcrypt hace un poco de relleno en lugar y asumido que es PKCS5Padding! rijndael de 128 256 yo ya respondemos ante lo que está mal en el código, pero ya había comprobado que antes de publicar y, sin embargo, el resultado sigue siendo diferente..
    Actualizado con otro posible motivo. ¿Por qué hay que hacer para convertir la llave de hex de bytes de Java, pero no en C?
    ha keysize de 256 así.Por qué se dice que con 256 no es AES?
    Tamaño de bloque se fija en 128 bits. No keysize (128, 192, 256). Es en la página de la wikipedia.
    Tuviste suerte con el de arriba?

    OriginalEl autor brice

  2. 2

    Seguir a @brice banderas, no veo donde puede inicializar el IV en el código de Java. Crear un `IvParameterSpec’, pero se pasa en un todo de cero de la matriz de bytes.

    Tu código C genera un azar IV por lo que debe producir un texto cifrado cada vez que se ejecute.

    Trate de usar un fijo IV para ambas implementaciones y ver si da resultados consistentes. Por supuesto, usted necesitará generar un azar IV de nuevo cuando se quiere hacer real el cifrado, pero el uso de un fijo IV puede ayudar con la depuración.

    También me gustaría asegurarse de que ambas implementaciones utilizando el mismo relleno (por la definición explícita de la de relleno en lugar de dejar que mcrypt elegir), y en lugar de escribir el raw de texto cifrado en la consola recomiendo escribir los valores hexadecimales, o simplemente escribir cada byte como un número – es mucho más fácil de depurar cuando usted no tiene que preocuparse acerca de los caracteres no imprimibles. Esto es sólo para la depuración, por lo que no importa si no es eficiente o agradable a la vista.

    Buena captura. Si los IVs no son los mismos, esto no va a funcionar.
    En realidad el IV sólo debe ser un azar IV, no es necesario para que sea igual al descifrar y cifrar, de lo contrario, uno debe conocer el texto cifrado, la clave secreta y el IV vector de descifrar, que no es obviamente el caso. He intentado poner una diferente IV en el cifrado y descifrado en Java y no tiene ningún efecto, el texto cifrado sigue siendo el mismo..
    Que es exactamente en el caso. Es necesario descifrar con el mismo IV que se utiliza para cifrar. Si no lo hice en su implementación de Java, entonces estás haciendo algo mal. Tenga en cuenta que su implementación en C un intento de generar un nuevo IV para el descifrado, pero no se utiliza porque ya has inicializado td2 con el cifrado IV.
    creo que necesita otro vistazo a la ¿cómo cifrados de bloque de trabajo. Yo recomiendo el capítulo 9 de Criptografía Aplicada
    El IV vector no necesita ser el mismo, ¿has probado el código que he puesto arriba cambiar el IV después de encriptación para descifrar el texto cifrado? te gustaría ver el texto cifrado se descifra correctamente de todos modos tan IV no es el punto aquí.. (voy a actualizar el IV código de arriba, de todos modos sólo para salir de cualquier perplejidad de distancia)

    OriginalEl autor Cameron Skinner

Dejar respuesta

Please enter your comment!
Please enter your name here