RNCryptor AES256匹配PHP MCRYPT_RIJNDAEL_256

时间:2013-11-04 21:20:13

标签: php objective-c encryption aes rncryptor

我在iOS应用程序中调用的PHP API要求以某种自定义方式加密有效负载。我在使用RNCryptor在Objective-C中复制这种方法时遇到了麻烦。

以下是用于加密字符串的PHP代码:

function encrypt($string) {
    $key = 'some-random-key';
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key))));
}

这就是我试图在Objective-C中实现相同的加密结果:

+ (NSData*)encryptData:(NSData*)sourceData {

    NSString *keyString = @"some-random-key";
    NSData *key = [[keyString MD5String] dataUsingEncoding:NSUTF8StringEncoding];
    NSData *iv = [[[keyString MD5String] MD5String] dataUsingEncoding:NSUTF8StringEncoding];

    NSMutableData *encryptedData = [NSMutableData data];

    RNCryptorEngine *cryptor = [[RNCryptorEngine alloc] initWithOperation:kCCEncrypt settings:kRNCryptorAES256Settings key:key IV:iv error:nil];

    [encryptedData appendData:[cryptor addData:sourceData error:nil]];
    [encryptedData appendData:[cryptor finishWithError:nil]];

    return encryptedData;

}

但两个函数的结果永远不匹配。例如,对于相同的单字串,PHP代码返回J39gRcuBEaqMIPP1VlizdA8tRjmyAB6za4zG5wcOB/8=,而在Objective-C中(在生成的NSData上运行base64EncodedStringWithOptions:之后),我得到1FGpZpVm2p4z3BBY6KW2fw==

我是否需要在RNCryptor设置中进一步调整才能使其正常工作?

更新

我直接使用原生iOS CommonCrypto框架,而不使用第三方RNCryptor lib。我匆匆得到与RNCryptor相同的结果。我甚至尝试在Objective-C和PHP片段中实现AES128,但即便如此,也从未使两个环境的结果匹配......

更新2

我正在使用的MD5String方法是NSString上的一个类别,定义如下:

- (NSString *)MD5String {
    const char *cstr = [self UTF8String];
    unsigned char result[16];
    CC_MD5(cstr, strlen(cstr), result);

    return [[NSString stringWithFormat:
            @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ] lowercaseString];
}

4 个答案:

答案 0 :(得分:4)

虽然大多数答案都集中在MD5哈希上,但最有可能导致问题的是MCRYPT_RIJNDAEL_256 不是 AES。它没有指定密钥大小为256但块大小为256,而AES的块大小始终为128位。至于其他参数,请在双方加密例程之前以十六进制打印出值,以找出它们的值。

答案 1 :(得分:4)

我意识到我在这里参加派对已经迟到了,你可能已经开始了。但我想指出,现在有一个full PHP port的RNCryptor与Objective-C实现的字节流兼容。我自己去年夏天做了贡献,我现在正在维护它。 : - )

所以现在使用RNCryptor PHP进行加密并使用RNCryptor Objective-C进行解密应该非常容易,反之亦然。查看main RNCryptor project了解详细信息和子项目列表。

答案 2 :(得分:1)

除了脚本语言之外,将md5作为hex-ascii返回是很常见的,即使php提供了二进制输出选项。

关于php mcrypt_encrypt

,有一些关于标准的事情
  1. 如果密钥小于所需的密钥大小,则使用\ 0填充密钥。
  2. 数据将以\ 0个字符填充到块的倍数 大小,通常使用填充,如pkcs7
  3. 未指定是如何处理非块大小的iv,可以猜测是 也用尾随\ 0字符填充。
  4. 其中iv是正确的长度,因为md5方法的hex-ascii输出。但是这会留下数据长度和填充,这是非标准的。

    密钥将是32个字节,因此将在php中填充32 \ 0个字符。这引出了使用带有128位密钥的AES256的问题。

    从底层的CommonCrypto“CommonCryptor.h”:

      

    keyLength:密钥材料的长度。必须适合   选择的操作和算法。

    密钥长度不正确。

    要做的事:
     1.处理数据长度/填充
     2.处理密钥长度

    如需进一步的帮助,请在base64编码之前提供您正在使用的样本数据和mcrypt_encrypt的hex-ascii输出。

    供参考,参见:
    mcrypt-encrypt.phpmd5.php

答案 3 :(得分:0)

Zaph的注释,

  

数据将用\ 0字符填充到块大小的倍数

让我走上正轨,帮助我找出部分问题。

基本上,PHP的mcrypt仅对数据使用\0填充,而Apple的CommonCryptor允许您在PKCS7填充(kCCOptionPKCS7Padding)之间进行选择或不使用任何填充。这是我永远无法使数据匹配的原因之一:在它即将加密之前,它总是以不同方式填充。

解决方案是让PHP在运行mcryptexample solution)之前执行数据的PKCS7填充,或者让Objective-C执行PHP样式的\0填充和make一定要删除kCCOptionPKCS7Padding(将NULL传递给CCCrypt选项):

NSMutableData *dataToEncrypt = [sourceData mutableCopy];
NSUInteger dataLength = [dataToEncrypt length];

// See how much padding is required
NSUInteger padding = kCCBlockSizeAES128 - (dataLength % kCCBlockSizeAES128);

// Add that many \0’s (there could be a more efficient way to do this)
for (int i=0; i<padding; i++)
    [dataToEncrypt appendData:[@"\0" dataUsingEncoding:NSASCIIStringEncoding]];

// Recalculate the data length
dataLength = dataToEncrypt.length;

我最终放弃了RNCryptor并直接使用原生CommonCryptor API,因此最终结果看起来像这样(encryptedBase64String方法在我的应用程序中是NSString的类别,因为我只需要以这种方式加密字符串;还要注意表示自由形式键字符串的kHSEncryptionKey常量):

- (NSString*)encryptedBase64String {

    // Prepare the data

    NSMutableData *sourceData = [[self dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];

    // Process the key

    NSString *key = [[kHSEncryptionKey MD5String] substringWithRange:NSMakeRange(0, 16)];

    char keyPtr[kCCKeySizeAES128 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    // Process the iv

    NSString *iv = [[[kHSEncryptionKey MD5String] MD5String] substringWithRange:NSMakeRange(0, 16)];

    char ivPtr[kCCKeySizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];

    // Pad the data, PHP style

    NSUInteger dataLength = [sourceData length];
    NSUInteger padding = kCCBlockSizeAES128 - (dataLength % kCCBlockSizeAES128);

    for (int i=0; i<padding; i++)
        [sourceData appendData:[@"\0" dataUsingEncoding:NSASCIIStringEncoding]];

    dataLength = sourceData.length;

    // Buffer for the resulting data

    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void* buffer = malloc(bufferSize);

    // Run the encryption

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, NULL,
                                          keyPtr, kCCKeySizeAES128,
                                          ivPtr,
                                          sourceData.bytes, dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);

    if (cryptStatus == kCCSuccess) {
        NSData *encyptedData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        return [encyptedData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    }

    free(buffer);
    return nil;
}