2010-02-17 15 views
25

Estoy encriptando una cadena en Object-C y también encriptando la misma cadena en Java usando AES y estoy viendo algunos problemas extraños. La primera parte del resultado coincide con un cierto punto, pero luego es diferente, por lo tanto, cuando voy a descodificar el resultado de Java en el iPhone, no puede descifrarlo.¿Cómo puedo hacer que mi encriptación AES sea idéntica entre Java y Objective-C (iPhone)?

Estoy usando una cadena fuente de "Ahora, entonces, ¿de qué se trata todo esto? ¿Lo sabías?" Utilizando la clave "123456789"

El código object-c para encriptar es el siguiente: NOTA: es una categoría NSData así que suponga que se llama al método en un objeto NSData para que 'self' contenga los datos de bytes cifrar

- (NSData *)AESEncryptWithKey:(NSString *)key { 
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused) 
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 

// fetch key data 
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

NSUInteger dataLength = [self length]; 

//See the doc: For block ciphers, the output size will always be less than or 
//equal to the input size plus the size of one block. 
//That's why we need to add the size of one block here 
size_t bufferSize = dataLength + kCCBlockSizeAES128; 
void *buffer = malloc(bufferSize); 

size_t numBytesEncrypted = 0; 
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
      keyPtr, kCCKeySizeAES128, 
      NULL /* initialization vector (optional) */, 
      [self bytes], dataLength, /* input */ 
      buffer, bufferSize, /* output */ 
      &numBytesEncrypted); 
if (cryptStatus == kCCSuccess) { 
    //the returned NSData takes ownership of the buffer and will free it on deallocation 
    return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 
} 

free(buffer); //free the buffer; 
return nil; 
} 

Y el código de cifrado de java es ...

public byte[] encryptData(byte[] data, String key) { 
    byte[] encrypted = null; 

    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
    byte[] keyBytes = key.getBytes(); 

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); 

    try { 
     Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); 
     cipher.init(Cipher.ENCRYPT_MODE, keySpec); 

     encrypted = new byte[cipher.getOutputSize(data.length)]; 
     int ctLength = cipher.update(data, 0, data.length, encrypted, 0); 
     ctLength += cipher.doFinal(encrypted, ctLength); 
    } catch (Exception e) { 
     logger.log(Level.SEVERE, e.getMessage()); 
    } finally { 
     return encrypted; 
    } 
} 

La salida hexadecimal del código Objective-C es -

7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0 

y la salida de Java es -

7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d 

Como puede ver, todo está bien hasta -

7a68ea36 8288c73d f7c45d8d 22432577 

que supongo que tienen algunas de las configuraciones diferentes, pero no se puede trabajar en lo que, He intentado cambiar entre el BCE y CBC en el lado de Java y no tuvo ningún efecto.

¿Alguien puede ayudar? por favor ...

+0

Ustedes me salvan de una pesadilla autosuficiente ... ¡Gracias! – Shade

Respuesta

18

Como CCCrypt toma un IV, ¿no usa un método de cifrado de bloque de encadenamiento (como CBC)? Esto sería consistente con lo que ve: el primer bloque es idéntico, pero en el segundo bloque la versión de Java aplica la clave original para encriptar, pero la versión de OSX parece usar algo más.

EDIT:

De here vi un ejemplo. Parece que lo necesario para aprobar el kCCOptionECBMode a CCCrypt:

ccStatus = CCCrypt(encryptOrDecrypt, 
     kCCAlgorithm3DES, 
     kCCOptionECBMode, <-- this could help 
     vkey, //"123456789", //key 
     kCCKeySize3DES, 
     nil, //"init Vec", //iv, 
     vplainText, //"Your Name", //plainText, 
     plainTextBufferSize, 
     (void *)bufferPtr, 
     bufferPtrSize, 
     &movedBytes); 

EDIT 2:

He jugado un poco con un poco de línea de comandos para ver cuál era la derecha. Pensé que podía aportarlo:

$ echo "Now then and what is this nonsense all about. Do you know?" | openssl enc -aes-128-ecb -K $(echo 123456789| xxd -p) -iv 0 | xxd 
0000000: 7a68 ea36 8288 c73d f7c4 5d8d 2243 2577 zh.6...=..]."C%w 
0000010: e66b 32f9 772b 6679 d7c0 cb69 037b 8740 .k2.w+fy...i.{[email protected] 
0000020: 883f 8211 7482 29f4 7239 84be b50b 5aea .?..t.).r9....Z. 
0000030: eaa7 519b 65e8 fa26 a1bb de52 083b 478f ..Q.e..&...R.;G. 
+0

Estaba pensando eso pero cambié el código de Java a lo siguiente ... Cipher.getInstance ("AES/CBC/PKCS7Padding", "BC"); ... pero no hizo ninguna diferencia. ¿A menos que necesite cambiar algo más para hacer que el código Java use CBC? –

+1

¡Absolutamente perfecto! Muchas gracias. Puedo dejar de golpear mi cabeza contra la pared ahora :) Saludos de nuevo. –

+0

No hay problema, me alegro de ser de ayuda. Jugué con openssl tratando de averiguar si Java o Mac estaban rithg. Agregué la línea de comando openssl que utilicé para generar el cifrado. Espero que pueda ser de utilidad. –

9

Para cualquier otra persona que necesita este, disown era absolutamente perfecto ... la llamada revisada para crear la cripta en Objective-C es el siguiente (tenga en cuenta que necesita el modo ECB Y el relleno) ...

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding, 
              keyPtr, kCCKeySizeAES128, 
              NULL /* initialization vector (optional) */, 
              [self bytes], dataLength, /* input */ 
              buffer, bufferSize, /* output */ 
              &numBytesEncrypted); 
+0

Tenga en cuenta que, por seguridad, el modo CBC se considera muy superior al BCE. El ECB filtra información acerca de qué bloques de entrada son idénticos entre sí, y tales bloques idénticos ocurren con bastante frecuencia en "datos normales". El CBC se puede ver como un tipo de aleatorización de datos, lo que hace que la ocurrencia de tales bloques sea mucho menos probable. –

+0

Gracias! Hubiera pasado todo un día tratando de resolver esto. – ebi

+0

¡Esto ayudó !! La respuesta aceptada arrojará un error como "más parámetros en la llamada al método". Esta debería ser la respuesta. – abhi1992

11

pasé unas semanas descifrar un base64, AES256 cadena cifrada. El cifrado fue realizado por CCCrypt (Objective-C) en un iPad. El descifrado debía hacerse en Java (usando Bouncy Castle).

Finalmente tuve éxito y aprendí bastante en el proceso. El código de cifrado era exactamente el mismo que el anterior (supongo que está tomado de la muestra de Objective-C en la documentación del desarrollador de iPhone).

Lo que la documentación de CCCrypt() NO menciona es que usa el modo CBC por defecto (si no especifica una opción como kCCOptionECBMode). Menciona que el IV, si no se especifica, tiene como valor predeterminado todos los ceros (por lo que IV será una matriz de bytes de 0x00, 16 miembros de longitud).

Usando estas dos piezas de información, puede crear un módulo de cifrado funcionalmente idéntico utilizando CBC (y evitar el uso de ECB que es menos seguro) en Java y OSx/iphone/ipad (CCCrypt).

La función de cifrado init se llevará a la matriz de bytes IV como un tercer argumento:

cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV). 
+0

Gracias por esto, salvaste mi día. – Esko

+0

La documentación de CCCrypt habla sobre el uso de CBC por defecto, al menos estoy bastante seguro de haberlo visto en alguna parte. Lo que hice * no * en ninguna parte fue mencionar el incumplimiento de IV. Ese fue el ingrediente que falta, ¡gracias! – David

+0

Para cualquier otra persona que lea esto ... ya no podrá pasar el IV a 'Cipher.init()'. Lo que tienes que hacer es (1) obtener un objeto 'AlgorithmParameters' (usando el método de fábrica de la clase' getInstance() ', (2) almacenar el IV en el objeto de parámetro:' params.init (new IvParameterSpec (iv)) ; 'y (3) pasa el objeto' params' como tercer argumento a 'cipher.init()'. – David

2

sólo para añadir al primer mensaje: en el código Objective C/cacao que utilizó el modo CBC y en el código Java Usaste EBC y no se usó un vector de inicialización IV en ninguno de los dos. El cifrado EBC es bloque por bloque y cadenas CBC en el bloque anterior, por lo que si el texto es más pequeño que 1 bloque (= 16 bytes en su ejemplo), el texto cifrado producido por ambos puede descifrarse por el otro (el mismo).

Si busca una forma de estandarizar el uso de las cifras, la Publicación especial NIST 800-38A, edición de 2001 tiene vectores de prueba. Puedo publicar el código para los vectores AES CBC y EBC si es útil para cualquier persona.

Cuestiones relacionadas