Domanda

La situazione che sto cercando di risolvere: nella mia app Cocoa, ho bisogno di crittografare una stringa con una cifra simmetrica, inviarla a PHP e fare in modo che lo script decodifichi i dati. Il processo deve funzionare al contrario per restituire una risposta (codifica PHP, decodifica Cocoa).

Mi manca qualcosa perché anche se riesco a far sì che sia la chiave sia il vettore di inizializzazione (iv) siano gli stessi sia in PHP che in Cocoa, la decodifica non funziona mai quando un'app invia i suoi dati codificati all'altra. Entrambi funzionano semplicemente codificando / decodificando i propri dati (verificato per assicurarsi che non ci siano problemi PEBKAC a portata di mano). Ho il sospetto che ci sia un problema di imbottitura da qualche parte, semplicemente non lo vedo.

La mia app per il cacao codifica usando SSCrypto (che è solo un comodo wrapper per le funzioni OpenSSL). Il codice è Blowfish, la modalità è CBC. (perdona le perdite di memoria, il codice è stato ridotto all'essenziale)

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 e iv vengono quindi POSTATI su PHP che tenta di 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 è sempre incomprensibile.

Ho provato a invertire il processo, inviando l'output codificato da PHP a Cocoa ma EVP_DecryptFinal () fallisce, il che è ciò che mi porta a credere che ci sia un problema di riempimento NULL da qualche parte. Ho letto e riletto i documenti PHP e OpenSSL ma ora tutto è sfocato insieme e non ho più idee da provare.

È stato utile?

Soluzione

Penso che il tuo problema sia che il metodo per derivare la chiave di crittografia grezza dalla stringa di chiavi sia diverso sui due lati. La funzione php md5 () restituisce una stringa esadecimale, ovvero 'a476c3 ...' che stai tagliando in basso alla dimensione della chiave, mentre EVP_BytesToKey () è una routine hash abbastanza complicata che restituisce una stringa di byte non elaborata. Potrebbe, con i parametri forniti semplificare fino a un hash MD5 grezzo, ma non posso davvero dirlo. Ad ogni modo, sarà diverso dall'hash del php.

Se cambi php in md5 (" ThisIsMyKey " ;, TRUE), questo ti darà un hash md5 grezzo. Per quanto riguarda il cacao, SSCrypto's + getMD5ForData: method dovrebbe generare lo stesso per la stessa stringa (a parte i problemi di codifica del testo).

Modifica 1: se la stringa php e i dati Cocoa vengono stampati in modo identico, sono comunque diversi a livello di byte. La stringa php è codificata in esadecimale (ovvero è composta solo da caratteri 0-9 e a-f) mentre i dati del cacao sono i byte grezzi (sebbene NSData stampi utile una stringa in codice esadecimale del suo contenuto quando NSLogged). È ancora necessario aggiungere il secondo parametro TRUE alla funzione md5 () di php per ottenere la stringa di byte non elaborata.

Modifica 2: OpenSSL 1.1.0c ha modificato l'algoritmo digest utilizzato in alcuni componenti interni. In precedenza, è stato utilizzato MD5 e 1.1.0 è passato a SHA256. Fai attenzione, il cambiamento non ti sta influenzando in EVP_BytesToKey e in comandi come openssl enc .

Altri suggerimenti

Ho capito il mio problema. La risposta breve: la chiave utilizzata era di diverse lunghezze in Cocoa e PHP. La lunga risposta ...

La mia richiesta originale stava usando Blowfish / CBC che è un codice di lunghezza variabile da 16 byte a 56. Scendendo dall'idea di Boaz che la chiave fosse in qualche modo da biasimare, sono passato a TripleDES per il codice poiché usa una chiave fissa lunghezza di 24 byte. Fu allora che notai un problema: la chiave restituita da Cocoa / EVP_BytesToKey () era lunga 24 byte, ma il valore restituito da hash md5 () la mia chiave era solo 16.

La soluzione al problema era di fare in modo che PHP creasse una chiave allo stesso modo di EVP_BytesToKey fino a quando la lunghezza dell'output era almeno (cipherKeyLength + cipherIVLength). Il seguente PHP fa proprio questo (ignorando qualsiasi salt o contando iterazioni)

$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);

Nota che molto probabilmente ci sarà il padding PKCS # 5 alla fine di quell'output. Dai un'occhiata ai commenti qui http://us3.php.net/manual/en /ref.mcrypt.php per pkcs5_pad e pkcs5_unpad per aggiungere e rimuovere detta imbottitura.

Fondamentalmente, prendi il valore md5 grezzo della chiave e se non è abbastanza lungo, aggiungi la chiave al risultato md5 e md5 di nuovo quella stringa. Lavare, risciacquare, ripetere. La pagina man di EVP_BytesToKey () spiega cosa sta realmente facendo e mostra dove si inseriscono i valori di sale, se necessario. Questo metodo di rigenerazione della chiave rigenera correttamente anche il vettore di inizializzazione (iv), quindi non è necessario passarlo.

Ma che dire di Blowfish?

EVP_BytesToKey () restituisce la chiave più piccola possibile per un codice poiché non accetta un contesto dal quale basare una dimensione chiave. Quindi la dimensione predefinita è tutto ciò che ottieni, che per Blowfish è di 16 byte. mcrypt_get_key_size () , d'altra parte, restituisce la dimensione della chiave più grande possibile. Quindi le seguenti righe nel mio codice originale:

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

restituisce sempre una chiave di 32 caratteri perché $ keySize è impostato su 56. Modifica del codice sopra in:

$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = 16;

consente la decodifica corretta del pesce palla, ma praticamente rovina il vantaggio di una chiave di lunghezza variabile. Per riassumere, EVP_BytesToKey () è rotto quando si tratta di cifre a lunghezza variabile. È necessario creare una chiave / iv in modo diverso quando si utilizza un codice a chiave variabile. Non ci ho pensato molto perché 3DES funzionerà per quello che mi serve.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top