una encriptar encriptada desencriptar datos contraseña con como php cocoa encryption openssl mcrypt

encriptar - Cifrado de datos en Cocoa, decodificación en PHP(y viceversa)



login con contraseña encriptada php (2)

Resolví mi problema. La respuesta corta: la clave que se utilizaba era de diferentes longitudes en Cocoa y PHP. La respuesta larga ...

Mi consulta original estaba usando Blowfish / CBC, que es un cifrado de longitud de clave variable de 16 bytes a 56. Partiendo de la idea de Boaz de que la clave era de alguna manera culpable, cambié a TripleDES para el cifrado ya que usa una longitud de clave fija de 24 bytes. Fue entonces cuando noté un problema: la clave devuelta por Cocoa / EVP_BytesToKey () tenía 24 bytes de longitud, pero el valor devuelto por md5 () hashing my key era solo 16.

La solución al problema era hacer que PHP creara una clave de la misma manera que EVP_BytesToKey hasta que la longitud de salida fuera al menos (cipherKeyLength + cipherIVLength). El siguiente PHP hace exactamente eso (ignorando cualquier sal o iteraciones de conteo)

$cipher = MCRYPT_TRIPLEDES; $cipherMode = MCRYPT_MODE_CBC; $keySize = mcrypt_get_key_size( $cipher, $cipherMode ); $ivSize = mcrypt_get_iv_size( $cipher, $cipherMode ); $rawKey = "ThisIsMyKey"; $genKeyData = ''''; do { $genKeyData = $genKeyData.md5( $genKeyData.$rawKey, true ); } while( strlen( $genKeyData ) < ($keySize + $ivSize) ); $generatedKey = substr( $genKeyData, 0, $keySize ); $generatedIV = substr( $genKeyData, $keySize, $ivSize ); $output = mcrypt_decrypt( $cipher, $generatedKey, $encodedData, $cipherMode, $generatedIV ); echo "output (hex)" . bin2hex($output);

Tenga en cuenta que lo más probable es que haya un relleno PKCS # 5 al final de esa salida. Consulte los comentarios aquí http://us3.php.net/manual/en/ref.mcrypt.php para pkcs5_pad y pkcs5_unpad para agregar y eliminar dicho relleno.

Básicamente, tome el valor md5 sin formato de la clave y, si no es lo suficientemente largo, agregue la clave al resultado md5 y md5 esa cadena nuevamente. Lave, enjuague, repita. La página de manual para EVP_BytesToKey () explica lo que realmente está haciendo y muestra dónde se pondrían los valores de sal, si es necesario. Este método de regenerar la clave también regenera correctamente el vector de inicialización (iv) por lo que no es necesario pasarlo.

Pero, ¿qué hay de Blowfish?

EVP_BytesToKey() devuelve la clave más pequeña posible para un cifrado, ya que no acepta un contexto por el cual basar un tamaño de clave. Entonces, el tamaño predeterminado es todo lo que obtienes, que para Blowfish es de 16 bytes. mcrypt_get_key_size() , por otro lado, devuelve el tamaño de clave más grande posible. Entonces las siguientes líneas en mi código original:

$keySize = mcrypt_enc_get_key_size( $td ); $key = substr( md5( "ThisIsMyKey" ), 0, $keySize );

siempre devolverá una clave de 32 caracteres porque $ keySize está establecido en 56. Cambiando el código anterior a:

$cipher = MCRYPT_BLOWFISH; $cipherMode = MCRYPT_MODE_CBC; $keySize = 16;

permite que blowfish decodifique correctamente pero arruina el beneficio de una clave de longitud variable. En resumen, EVP_BytesToKey() se rompe cuando se trata de cifrado de longitud de clave variable. Necesita crear una clave / iv de forma diferente cuando usa un cifrado de clave variable. No profundicé mucho porque 3DES funcionará para lo que necesito.

La situación que trato de resolver: en mi aplicación Cocoa, necesito encriptar una cadena con un cifrado simétrico, POSTALIZAR con PHP y hacer que la secuencia de comandos decodifique los datos. El proceso debe funcionar a la inversa para devolver una respuesta (códigos PHP, decodificaciones de Cocoa).

Me falta algo, porque aunque puedo obtener la clave y el vector de inicialización (iv) para que sean iguales en PHP y Cocoa, la decodificación nunca funciona cuando una aplicación envía sus datos codificados a la otra. Ambos funcionan muy bien codificando / decodificando sus propios datos (verificados para asegurarse de que no haya algún problema con PEBKAC). Tengo la sospecha de que hay un problema de relleno en algún lugar, simplemente no lo veo.

Mi aplicación Cocoa codifica el uso de SSCrypto (que es solo un práctico envoltorio para las funciones de OpenSSL). El cifrado es Blowfish, el modo es CBC. (perdona las pérdidas de memoria, el código ha sido despojado de lo esencial)

NSData *secretText = [@"secretTextToEncode" dataUsingEncoding:NSUTF8StringEncoding]; NSData *symmetricKey = [@"ThisIsMyKey" dataUsingEncoding:NSUTF8StringEncoding]; unsigned char *input = (unsigned char *)[secretText bytes]; unsigned char *outbuf; int outlen, templen, inlen; inlen = [secretText length]; unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"/0"}; int cipherMaxIVLength = EVP_MAX_IV_LENGTH; EVP_CIPHER_CTX cCtx; const EVP_CIPHER *cipher = EVP_bf_cbc(); cipherMaxIVLength = EVP_CIPHER_iv_length( cipher ); unsigned char iv[cipherMaxIVLength]; EVP_BytesToKey(cipher, EVP_md5(), NULL, [symmetricKey bytes], [symmetricKey length], 1, evp_key, iv); NSData *initVector = [NSData dataWithBytes:iv length:cipherMaxIVLength]; EVP_CIPHER_CTX_init(&cCtx); if (!EVP_EncryptInit_ex(&cCtx, cipher, NULL, evp_key, iv)) { EVP_CIPHER_CTX_cleanup(&cCtx); return nil; } int ctx_CipherKeyLength = EVP_CIPHER_CTX_key_length( &cCtx ); EVP_CIPHER_CTX_set_key_length(&cCtx, ctx_CipherKeyLength); outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx), sizeof(unsigned char)); if (!EVP_EncryptUpdate(&cCtx, outbuf, &outlen, input, inlen)){ EVP_CIPHER_CTX_cleanup(&cCtx); return nil; } if (!EVP_EncryptFinal(&cCtx, outbuf + outlen, &templen)){ EVP_CIPHER_CTX_cleanup(&cCtx); return nil; } outlen += templen; EVP_CIPHER_CTX_cleanup(&cCtx); NSData *cipherText = [NSData dataWithBytes:outbuf length:outlen]; NSString *base64String = [cipherText encodeBase64WithNewlines:NO]; NSString *iv = [initVector encodeBase64WithNewlines:NO];

base64String y iv son luego POST a PHP que intenta decodificarlo:

<?php import_request_variables( "p", "p_" ); if( $p_data != "" && $p_iv != "" ) { $encodedData = base64_decode( $p_data, true ); $iv = base64_decode( $p_iv, true ); $td = mcrypt_module_open( MCRYPT_BLOWFISH, '''', MCRYPT_MODE_CBC, '''' ); $keySize = mcrypt_enc_get_key_size( $td ); $key = substr( md5( "ThisIsMyKey" ), 0, $keySize ); $decodedData = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encodedData, MCRYPT_MODE_CBC, $iv ); mcrypt_module_close( $td ); echo "decoded: " . $decodedData; } ?>

decodedData es siempre un galimatías.

Intenté revertir el proceso, enviando la salida codificada de PHP a Cocoa pero EVP_DecryptFinal () falla, que es lo que me lleva a pensar que hay un problema de relleno nulo en alguna parte. He leído y releído los documentos de PHP y OpenSSL, pero ahora todo se está difuminando y me faltan ideas para probar.


Creo que su problema es que el método de derivar la clave de cifrado sin formato de la cadena clave es diferente en ambos lados. La función php md5 () devuelve una cadena hexadecimal, es decir, ''a476c3 ...'' que está reduciendo al tamaño de la clave, mientras que EVP_BytesToKey () es una rutina hash bastante complicada que devuelve una cadena de bytes sin formato. Podría, con los parámetros proporcionados simplificar hasta un hash MD5 crudo, pero realmente no puedo decirlo. De cualquier manera, va a ser diferente del php hash.

Si cambia el php a md5 ("ThisIsMyKey", TRUE), eso le dará un hash md5 crudo. En el lado del cacao, el método + getMD5ForData de SSCrypto debería generar el mismo para la misma cadena (dejando de lado los problemas de codificación de texto).

Editar 1: si la cadena de php y los datos de Cocoa se imprimen de forma idéntica, todavía son diferentes en el nivel de bytes. La cadena php está codificada en hexadecimal (es decir, consta únicamente de los caracteres 0-9 y af), mientras que los datos de cacao son los bytes sin procesar (aunque NSData imprime útilmente una cadena codificada en hexadecimal de su contenido cuando está en NSLogged). Aún necesita agregar el segundo parámetro TRUE a la función md5 () de php para obtener la cadena de bytes sin formato.

Editar 2: OpenSSL 1.1.0c cambió el algoritmo de resumen] (El cifrado / descifrado no funciona bien entre dos versiones de openssl diferentes ) utilizado en algunos componentes internos. Anteriormente, se usaba MD5 y 1.1.0 cambiaba a SHA256. Tenga cuidado de que el cambio no le afecte tanto en EVP_BytesToKey como en comandos como openssl enc .