Question

La situation que je tente de résoudre: dans mon application Cocoa, je dois chiffrer une chaîne avec un chiffrement symétrique, l'envoyer à PHP et laisser ce script décoder les données. Le processus doit fonctionner en sens inverse pour renvoyer une réponse (PHP encode, Cocoa décode).

Il me manque quelque chose parce que même si je peux obtenir que la clé et le vecteur d'initialisation (iv) soient identiques en 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 encoder / décoder leurs propres données (vérifiées pour s’assurer qu’il n’ya pas de problème PEBKAC sous la main). Je soupçonne qu'il y a un problème de rembourrage quelque part, je ne le vois tout simplement pas.

Mon application cacao utilise SSCrypto (qui est un encapsuleur pratique pour les fonctions OpenSSL). Le chiffre est Blowfish, le mode est CBC. (pardonnez les fuites de mémoire, le code a été réduit au strict minimum)

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 alors envoyés à PHP qui 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 du charabia.

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

Était-ce utile?

La solution

Je pense que votre problème est que la méthode de calcul de la clé de chiffrement brute à partir 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églez à la taille de la clé, tandis qu'EVP_BytesToKey () est une routine de hachage assez complexe qui renvoie une chaîne d'octets brute. Cela pourrait, avec les paramètres fournis, être simplifié jusqu’à un hachage MD5 brut, mais je ne peux pas vraiment le dire. De toute façon, ça va être différent du hash php.

Si vous modifiez le php en md5 ("ThisIsMyKey", TRUE), cela vous donnera un hachage brut md5. Pour ce qui est du cacao, la méthode + getMD5ForData: de SSCrypto devrait générer le même pour la même chaîne (les problèmes d’encodage de texte mis à part).

Éditer 1: Si la chaîne php et les données Cocoa s’impriment de manière identique, elles sont toujours différentes au niveau octet. La chaîne php est codée en hexadécimal (c'est-à-dire ne contient que les 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 codée en hexadécimal de son contenu lorsque NSLogged). Vous devez toujours ajouter le second paramètre TRUE à la fonction phd md5 () pour obtenir la chaîne d'octets bruts.

Éditer 2: OpenSSL 1.1.0c a modifié l'algorithme de résumé utilisé dans certains composants internes. Auparavant, MD5 était utilisé et 1.1.0 est passé à SHA256. Veillez à ce que la modification ne vous affecte pas à la fois dans EVP_BytesToKey et dans les commandes telles que openssl enc .

Autres conseils

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

Ma demande initiale consistait à utiliser Blowfish / CBC, un chiffrement à longueur de clé variable allant de 16 octets à 56. Abandonnant l'idée de Boaz selon laquelle la clé était en quelque sorte à blâmer, je suis passé à TripleDES pour le chiffrement car celui-ci utilise une clé fixe longueur de 24 octets. C’est alors que j’ai remarqué un problème: la clé renvoyée par Cocoa / EVP_BytesToKey () avait une longueur de 24 octets, mais la valeur renvoyée par md5 (), hachant ma clé n’était que de 16.

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 égale à (cipherKeyLength + cipherIVLength). Le PHP suivant fait exactement cela (en ignorant les itérations sel ou count)

$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 que PKCS # 5 sera probablement complété à 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 l’ajout et la suppression de ce remplissage.

En gros, prenez la valeur md5 brute de la clé et si ce n’est pas assez long, ajoutez la clé au résultat md5 et remettez-y cette chaîne. Laver, rincer, répéter. La page de manuel de EVP_BytesToKey () explique ce qu’il fait réellement et indique où il serait judicieux de placer des valeurs de sel, si nécessaire. Cette méthode de régénération de la clé régénère également 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 permettant de baser une taille de clé. Donc, la taille par défaut est tout ce que vous obtenez, soit 16 octets pour Blowfish. mcrypt_get_key_size () , en revanche, renvoie la plus grande taille de clé possible. Donc, les lignes suivantes dans mon code d'origine:

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

renverrait toujours une clé de 32 caractères car $ keySize a la valeur 56. Changer le code ci-dessus en:

$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = 16;

permet à blowfish de décoder correctement mais détruit sensiblement l’avantage d’une clé de longueur variable. Pour résumer, EVP_BytesToKey () est cassé en ce qui concerne les 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 ai pas beaucoup parlé, car 3DES fonctionnera pour ce dont j'ai besoin.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top