2

我从我的 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 中(在base64EncodedStringWithOptions:生成的 NSData 上运行之后)我得到1FGpZpVm2p4z3BBY6KW2fw==.

我需要在 RNCryptor 设置中进一步调整以使其正常工作吗?

更新

我直接玩过原生 iOS CommonCrypto 框架,完全没有使用第三方 RNCryptor lib。不过,我一直得到与 RNCryptor 相同的结果。我什至尝试在我的 Objective-C 和 PHP 代码片段中实现 AES128,但即便如此,这两个环境的结果也从未匹配......</p>

更新 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

4 回答 4

4

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

于 2013-11-05T22:57:01.617 回答
4

我意识到我在这里聚会迟到了,你可能已经继续前进了。但我想指出,现在有一个完整的 RNCryptor PHP 端口,它与 Objective-C 实现的字节流兼容。我自己去年夏天贡献了它,我目前正在维护它。:-)

所以现在用 RNCryptor PHP 加密和用 RNCryptor Objective-C 解密应该很容易,反之亦然。查看主 RNCryptor 项目以获取详细信息和子项目列表。

于 2014-01-24T03:39:38.543 回答
1

md5在脚本语言以外的情况下,将 an 作为 hex-ascii返回是不常见的,甚至php提供了二进制输出选项。

关于 php 有一些标准的东西mcrypt_encrypt

  1. 如果密钥小于所需的密钥大小,则使用 \0 填充密钥。
  2. 数据将用 \0 字符填充到块大小的倍数,通常使用诸如填充pkcs7
  3. 未指定如何处理不是块大小的 iv,可以猜测它也用尾随 \0 字符填充。

由于该方法的 hex-ascii 输出,其中 iv 将是正确的长度md5。但是留下了非标准的数据长度和填充。

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

从底层的 CommonCrypto “CommonCryptor.h”:

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

密钥长度不正确。

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

如需进一步帮助,请提供您正在使用的示例数据以及mcrypt_encryptbase64 编码之前的 hex-ascii 输出。

参考见:
mcrypt- encrypt.php和md5.php

于 2013-11-05T21:18:24.110 回答
0

Zaph的笔记,那个

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

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

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

解决方案是让 PHP 在运行之前执行 PKCS7 数据填充mcrypt示例解决方案)或让 Objective-C 执行 PHP 样式的\0填充并确保删除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;
}
于 2013-11-06T03:41:25.600 回答