Pergunta

A situação que eu estou tentando resolver:na minha Cacau app, o que eu preciso para encriptar uma string com uma cifra simétrica, o POST é para PHP, e tenho esse script decodificar os dados.O processo deve funcionar no sentido inverso para retornar uma resposta (PHP codifica, Cacau decodifica).

Eu estou faltando alguma coisa, porque mesmo que eu possa obter a chave e vetor de inicialização (iv) para ser o mesmo em ambos PHP e o Cacau, a decodificação nunca funciona quando um aplicativo envia seus dados codificados para os outros.Ambos funcionam muito bem codificação/decodificação seus próprios dados (verificado para se certificar de que não havia algum PEBKAC problema na mão).Eu tenho uma suspeita que há um problema de preenchimento de algum lugar, e eu não vejo isso.

Minha cacau app codifica usando SSCrypto (que é apenas acessível-dândi wrapper em torno de funções OpenSSL).A cifra é Blowfish, modo CBC.(perdoe-se a vazamentos de memória, o código foi removido para o essencial)

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 são Lançados, em seguida, PHP, que tenta decodificá-la:

<?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 bobagem.

Eu tentei reverter o processo, enviando codificados saída do PHP para o Cacau, mas EVP_DecryptFinal() falhar, o que é o que me leva a acreditar que existe um NULO problema de preenchimento de algum lugar.Eu li e re-ler o PHP e o OpenSSL docs, mas é tudo que se precipitam agora e eu estou sem ideias para experimentar.

Foi útil?

Solução

Eu acho que o seu problema é que o método de derivar a chave de criptografia crua da sequência de teclas é diferente nos dois lados. A função php md5 () retorna uma string hexadecimal, ou seja, 'a476c3 ...', que você está cortando para o tamanho da chave, enquanto o EVP_BYTESTOKEY () é uma rotina de hash bastante complicada que retorna uma corda de byte cru. Pode, com os parâmetros fornecidos simplificar até um hash MD5 bruto, mas não sei dizer. De qualquer maneira, será diferente do hash php.

Se você alterar o PHP para MD5 ("ThisismyKey", verdadeiro), isso lhe dará um hash bruto de MD5. No lado do cacau, o método de sscrypto +getMd5fordata: o método deve gerar o mesmo para a mesma string (problemas de codificação de texto à parte).

EDIT 1: Se os dados de string php e cacau imprimirem de forma idêntica, eles ainda serão diferentes no nível de bytes. A string php é codificada por hexadecipal (ou seja, consiste apenas dos caracteres 0-9 e AF), enquanto os dados do cacau são os bytes brutos (embora o NSDATA imprima uma corda codificada por hexadecipal de seu conteúdo quando NSLogged). Você ainda precisa adicionar o segundo parâmetro verdadeiro à função md5 () do PHP para obter a string de byte cru.

Editar 2: OpenSSL 1.1.0c mudou o algoritmo Digest usado em alguns componentes internos. Anteriormente, o MD5 foi usado e 1.1.0 mudou para SHA256. Tenha cuidado para que a mudança não esteja afetando você em ambos EVP_BytesToKey e comandos como openssl enc.

Outras dicas

Eu descobri o meu problema.A resposta curta:a chave a ser utilizada foi de comprimentos diferentes, sob o Cacau e o PHP.A resposta longa...

O meu original inquérito estava usando Blowfish/CBC, que é uma variável comprimento da chave de codificação a partir de 16 bytes para 56.Sair de Boaz a idéia de que a chave foi, de alguma forma, a culpa, eu mudei para TripleDES para a codificação que usa um fixo com comprimento de chave 24 bytes.Foi então que eu notei um problema:a chave devolvidos por Cacau/EVP_BytesToKey() foi de 24 bytes de comprimento, mas o valor retornado pela md5() hash minha chave era de apenas 16.

A solução para o problema foi que o PHP criar uma chave da mesma forma EVP_BytesToKey faz até a saída comprimento era de pelo menos (cipherKeyLength + cipherIVLength).O seguinte PHP não é só isso (ignorando qualquer de sal ou a contagem de iterações)

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

Note-se que há provavelmente vai ser PKCS#5 de preenchimento no final da saída.Confira os comentários aqui http://us3.php.net/manual/en/ref.mcrypt.php para pkcs5_pad e pkcs5_unpad para adicionar e remover disse preenchimento.

Basicamente, tomar as matérias-md5 valor da chave e se isso não for suficiente, acrescente a chave para o md5 resultado md5 e que a cadeia novamente.A lavagem, enxágüe, repita.A página man para EVP_BytesToKey() explica o que é, na verdade, fazendo e mostra onde se poderia colocar valores de sal, se necessário.Este método de regeneração de chave corretamente também regenera o vetor de inicialização (iv), de modo que não é necessário passá-lo adiante.

Mas o que sobre o Blowfish?

EVP_BytesToKey() retorna o menor chave possível para uma cifra como não aceitar um contexto através do qual a base de um tamanho de chave de.Assim, o tamanho padrão é tudo o que você conseguir, o que para o Blowfish é de 16 bytes. mcrypt_get_key_size(), por outro lado, retorna o maior tamanho de chave possível.Portanto, as seguintes linhas no meu código original:

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

sempre retornar uma chave de 32 caracteres, porque $algoritmo é definido para 56.Alterar o código acima para:

$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = 16;

permite que o blowfish para decodificar corretamente, mas praticamente em ruínas, em benefício de uma chave de comprimento variável.Para resumir, EVP_BytesToKey() é interrompida quando ele vem para a variável comprimento da chave de cifragem.Você precisa criar uma chave/iv diferente quando utilizar uma variável chave de codificação.Eu não entrar muito, porque, 3DES vai funcionar para o que eu preciso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top