我试图解决的情况:在我的可可应用程序,我需要一个字符串对称密码加密,张贴到PHP,并让该脚本解码数据。该过程需要在反向工作用于返回应答(PHP编码,可可解码)。

我失去了一些东西,因为即使我可以同时获得密钥和初始化向量(IV)是在PHP和可可相同,当一个应用程序发送它的编码数据提供给其他解码永远不会奏效。这两个工作得很好,编码/解码自己的数据(验证,以确保有没有手头有些PEBKAC问题)。我有一个怀疑有一个填充问题的某个地方,我只是没有看到它。

我的可可应用编码使用SSCrypto(这是周围OpenSSL的功能只是一个方便,花花公子包装器)。该密码是河豚,模式是CBC。 (原谅内存泄漏,代码已被剥离至裸要领)

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和iv然后贴到PHP试图对其进行解码:

<?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总是乱码。

我试过在倒车过程中,从PHP编码输出发送到可可但EVP_DecryptFinal()失败,这是什么使我相信有一个NULL填充问题的地方。我已阅读并重新读取PHP和OpenSSL文档,但现在一切都模糊了起来,我的想法去尝试。

有帮助吗?

解决方案

我觉得你的问题是,获得从钥匙串的原始加密密钥的方法是双方不同。 PHP的MD5()函数返回一个十六进制字符串,即“a476c3 ......”你所砍伐的关键尺寸,同时EVP_BytesToKey()是一个相当复杂的哈希例程返回原始字节串。它可能与提供的参数简化到原始MD5哈希,但我真的不能告诉。无论哪种方式,这将是从PHP哈希不同。

如果你改变了PHP来的MD5( “ThisIsMyKey”,TRUE),这将使您原始MD5哈希值。对事物的可可侧,SSCrypto的+ getMD5ForData:方法应该产生相同的字符串相同的一个(文字编码问题放在一边)

修改1:如果PHP字符串和可可数据打印出相同,他们仍然在字节级不同。 PHP的字符串是十六进制编码(即只包含字符0-9和A-F),而可可数据是原始字节(尽管NSData的有益打印出其内容的十六进制编码的串时NSLogged)。您还需要第二个TRUE参数添加到PHP的MD5()函数来获取原始字节串。

编辑2:的OpenSSL 1.1.0c改变了摘要算法的某些内部组件使用。以前,MD5被使用,并且1.1.0切换到SHA256。小心的变化不影响你在这两个EVP_BytesToKey和像openssl enc命令。

其他提示

我想通了,我的问题。答案很简单:关键使用了可可和PHP根据不同长度的。长的答案......

我的原始查询使用的Blowfish / CBC其是从16个字节的可变密钥长度密码〜56变成为关状态波阿斯的想法,即键在某种程度上怪的,我切换到TripleDes的用于加密作为使用固定密钥的24个字节长度。就在这时,我注意到一个问题:由可可/ EVP_BytesToKey()返回的密钥的长度为24个字节,但通过MD5()返回的散列我的键的值仅为16

该问题的解决方案是具有PHP创建密钥相同的方式EVP_BytesToKey确实直到输出长度为至少(cipherKeyLength + cipherIVLength)。以下PHP不只是(忽略任何盐或计数迭代)

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

请注意,有将最有可能在该输出结束PKCS#5填充。退房的意见在这里 http://us3.php.net/manual/en /ref.mcrypt.php 获取pkcs5_padpkcs5_unpad用于添加和删除所述填充。

基本上,取密钥的原始MD5值,并且如果这还不够长,再次追加键的MD5结果和MD5该字符串。洗涤,漂洗,重复。该手册页EVP_BytesToKey()解释它实际上做什么,并表示其中一个将放入盐值,如果需要的话。再生键的这种方法也正确地重新生成初始化向量(IV),所以它没有必要把它传递。

但对于河豚?

EVP_BytesToKey()返回最小的键可能,因为它不接受的上下文由从基部的密钥大小的加密。所以默认大小是你所得到的,这对河豚是16个字节。 mcrypt_get_key_size(),在另一方面,返回最大可能的密钥大小。因此,在我的原始代码下面的行:

$keySize = mcrypt_enc_get_key_size( $td );
$key = substr( md5( "ThisIsMyKey" ), 0, $keySize );
因为$密钥大小被设置为56更改上述代码

将总是返回一个32个字符的键:

$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = 16;

允许河豚解码正确,但相当多的废墟的可变长度密钥的益处。综上所述,EVP_BytesToKey()坏了,当涉及到可变密钥长度的密码。您需要创建一个键/四不同的使用可变密钥密码时。我没去成它更因为3DES会为我所需要的工作。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top