2008-11-26 4 views
2

La situation que j'essaie de résoudre: dans mon application Cocoa, j'ai besoin de crypter une chaîne avec un chiffrement symétrique, de l'envoyer en PHP, et d'avoir ce script décoder les données. Le processus doit fonctionner à l'envers pour renvoyer une réponse (PHP code, Cocoa décode). Il me manque quelque chose car même si je peux obtenir à la fois la clé et le vecteur d'initialisation (iv) identiques dans PHP et Cocoa, le décodage ne fonctionne jamais lorsqu'une application envoie ses données codées à l'autre. Les deux fonctionnent très bien pour coder/décoder leurs propres données (vérifiées pour s'assurer qu'il n'y avait pas de problème avec PEBKAC). Je soupçonne qu'il y a un problème de rembourrage quelque part, je ne le vois tout simplement pas.Cryptage des données dans Cocoa, décodage en PHP (et vice versa)

Mon application de cacao code en utilisant SSCrypto (qui est juste un wrapper pratique autour des fonctions OpenSSL). Le chiffre est Blowfish, le mode est CBC. (Pardonner les fuites de mémoire, le code a été dépouillé à l'essentiel)

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 et iv sont ensuite à PHP qui POSTées tente de le décoder:

<?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 est toujours charabia.

J'ai essayé d'inverser le processus, en envoyant la sortie encodée de PHP vers Cocoa mais EVP_DecryptFinal() échoue, ce qui me porte à croire qu'il y a un problème de remplissage NULL quelque part. J'ai lu et relu les docs PHP et OpenSSL, mais tout est devenu flou maintenant et je n'ai plus d'idées à essayer.

+0

[OpenSSL 1.1.0c a modifié l'algorithme de résumé] (http://stackoverflow.com/q/39637388/608639) utilisé dans certains composants internes. Auparavant, MD5 était utilisé et 1.1.0 passait à SHA256. Veillez à ce que la modification ne vous affecte pas à la fois dans 'EVP_BytesToKey' et dans les commandes comme' openssl enc'. – jww

Répondre

3

Je pense que votre problème est que la méthode de dériver la clé de cryptage brut de la chaîne de clé est différente des deux côtés. La fonction php md5() renvoie une chaîne hexadécimale, c'est-à-dire 'a476c3 ...' que vous réduisez à la taille de la clé, alors que EVP_BytesToKey() est une routine de hachage assez compliquée qui renvoie une chaîne d'octets bruts. Il pourrait, avec les paramètres fournis simplifier jusqu'à un hachage MD5 brut, mais je ne peux pas vraiment le dire. De toute façon, ça va être différent du hachage php.

Si vous changez le php en md5 ("ThisIsMyKey", TRUE), cela vous donnera un hachage md5 brut. Du côté du cacao, la méthode + getMD5ForData: de SSCrypto devrait générer la même chose pour la même chaîne (les problèmes d'encodage du texte mis à part).

Édition 1: Si la chaîne PHP et les données Cocoa impriment de façon identique, elles sont toujours différentes au niveau des octets. La chaîne php est codée en hexadécimal (c'est-à-dire composée uniquement de caractères 0-9 et a-f) tandis que les données de cacao sont les octets bruts (bien que NSData imprime utilement une chaîne hexadécimale de son contenu lorsque NSLogged). Vous devez toujours ajouter le deuxième paramètre TRUE à la fonction md5() de php pour obtenir la chaîne d'octets brute.

Édition 2: OpenSSL 1.1.0c a modifié l'algorithme de résumé] (Encryption/decryption doesn't work well between two different openssl versions) utilisé dans certains composants internes. Auparavant, MD5 était utilisé et 1.1.0 passait à SHA256. Veillez à ce que la modification ne vous affecte pas à la fois dans EVP_BytesToKey et des commandes comme openssl enc.

+0

Boaz, mes excuses ... Je viens de relire ce commentaire et j'ai remarqué que vous avez souligné la même chose (PHP md5() peut renvoyer des octets bruts) que j'ai commenté ci-dessous. Mes excuses ... c'était un long week-end. – MyztikJenz

1

J'ai trouvé mon problème. La réponse courte: la clé utilisée était de différentes longueurs sous Cocoa et PHP. La réponse longue ...

Mon enquête initiale utilisait Blowfish/CBC qui est un chiffrement de longueur de clé variable de 16 octets à 56. En quittant l'idée de Boaz que la clé était en quelque sorte à blâmer, je suis passé à TripleDES pour le chiffre car cela utilise une longueur de clé fixe de 24 octets. C'est alors que j'ai remarqué un problème: la clé retournée par Cocoa/EVP_BytesToKey() avait une longueur de 24 octets, mais la valeur retournée par md5() en hachant ma clé n'était que de 16 octets.

La solution au problème était que PHP crée une clé de la même manière que EVP_BytesToKey jusqu'à ce que la longueur de sortie soit au moins (cipherKeyLength + cipherIVLength). Ce qui suit PHP exactement ce que fait (sans tenir compte du sel ou le nombre d'itérations)

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

Notez qu'il y aura très probablement PKCS # 5 rembourrage sur la fin de cette sortie. Consultez les commentaires ici http://us3.php.net/manual/en/ref.mcrypt.php pour pkcs5_pad et pkcs5_unpad pour ajouter et enlever ledit rembourrage. Prenez la valeur brut md5 de la clé et si cela n'est pas assez long, ajoutez la clé au résultat md5 et md5 cette chaîne à nouveau. Laver, rincer, répéter. La page de manuel pour EVP_BytesToKey() explique ce qu'il fait réellement et montre où l'on mettrait des valeurs de sel, si nécessaire. Cette méthode de régénération de la clé régénère aussi correctement le vecteur d'initialisation (iv), il n'est donc pas nécessaire de le transmettre.

Mais qu'en est-il de Blowfish?

EVP_BytesToKey() renvoie la plus petite clé possible pour un chiffrement, car il n'accepte pas de contexte dans lequel baser une taille de clé. Donc, la taille par défaut est tout ce que vous obtenez, ce qui pour Blowfish est de 16 octets. Par contre, mcrypt_get_key_size() renvoie la plus grande taille de clé possible. Ainsi, les lignes suivantes dans mon code d'origine:

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

serait toujours retourner une clé de 32 caractères parce que $ KEYSIZE est réglé sur 56. Modification du code ci-dessus:

$cipher = MCRYPT_BLOWFISH; 
$cipherMode = MCRYPT_MODE_CBC; 

$keySize = 16; 

permet Blowfish de décoder correctement, mais à peu près ruine le bénéfice d'une clé de longueur variable. Pour résumer, EVP_BytesToKey() est cassé quand il s'agit de chiffrements de longueur de clé variable. Vous devez créer une clé/iv différemment lorsque vous utilisez un chiffrement à clé variable. Je n'y suis pas allé beaucoup parce que 3DES fonctionnera pour ce dont j'ai besoin.

+0

J'ai perdu une journée entière à tourner en rond sur ce même problème et le substitut PHP pour EVP_BytesToKey a résolu mon problème. Merci! – grahamparks

Questions connexes