AES 128 解密失败


AES 128 decryption fails

我有一个使用AES 128 CBC加密数据的iOS应用程序。我可以在 objective-c 中解密应用程序中的数据,以便至少告诉我加密在该上下文中正常工作(通用加密)。问题是我需要将这些加密数据发送到服务器并通过 PHP 解密。这就是我失败的地方。

我使用 openssl 在命令行上手动进行了加密。使用该输出并将其反馈回此 php 函数,我可以正确解密,因此我知道 php 函数正常工作,至少相对于 openssl 而言。因此,问题是如何让Objective-C以与openssl相同的方式输出密文(或者如何让php解密Common Crypto的密文)。换句话说,这些函数中的每一个都在自己的上下文中"工作"。

<?php
function jsonEncode ($result, $message) {
    $arr = array('result' => $result, 'message' => $message);
    echo json_encode($arr);
}

//$ciphertext = base64_decode($_POST['ciphertext']);
//$iv         = $_POST['iv'];
// overriding the http post values with values copied in from console output for testing
$ciphertext = base64_decode('dwb7MWCQUUiVuJLzL2EzYm0NcodwORP47qPhLzaolAk=');
// openssl on the command line yields ciphertext of
//  'gRiOXseXTjhpPCiiHgnaQQoal3a9E87Gx3FVpZPtR1I=' which decrypts successfully
$iv = 'hkPDfznq1t1UpKrW';
$key = 'T8ZvJba0HHsmiVSD';
//$plaintext = openssl_decrypt($ciphertext, 'aes-128-cbc', $key, false, $iv);
//jsonEncode('success', $plaintext);
// this is the line that fails
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv);
//$padding = ord($plaintext[strlen($plaintext) - 1]);
//jsonEncode('success', substr($plaintext, 0, -$padding));
jsonEncode('success', $plaintext);
?>

编辑:基于下面@Zaph非常有用的评论,我在Objective-C中重写了我的加密函数以手动进行填充。我认为这是正确的。但是,当 php 函数返回时,mcrypt_decrypt函数的计算结果为 false。下面是新的目标 C 函数:

- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
    char keyPtr[kCCKeySizeAES128+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    char ivPtr[kCCBlockSizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));
    if (iv) {
        [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    }
    NSUInteger dataLength = [self length];
    int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
    int newSize = 0;
    if(diff > 0) {
        newSize = (int)(dataLength + diff);
    }
    // manually add padding to the end of the data array
    char dataPtr[newSize];
    memcpy(dataPtr, [self bytes], [self length]);
    for(int i = 0; i < diff; i++) {
        dataPtr[i + dataLength] = diff;
    }
    dataPtr[newSize] = ''0';
    size_t bufferSize = newSize + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    // print out the padded array for verification
    NSLog(@"diff: %d  new size: %d", diff, newSize);
    for (int i=0; i<newSize; i++)
        printf("0x%x ", dataPtr[i]);
    printf("'n");

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128,
                                          0x0000, //No padding
                                          keyPtr,
                                          kCCKeySizeAES128,
                                          ivPtr,
                                          dataPtr,
                                          sizeof(dataPtr),
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    if(cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    return nil;
}

目标 C 控制台输出:

diff: 12  new size: 32
0x65 0x6e 0x63 0x72 0x79 0x70 0x74 0x69 0x6f 0x6e 0x20 0x69 0x73 0x20 0x74 0x72 0x69 0x63 0x6b 0x79 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 
encrypted text: dwb7MWCQUUiVuJLzL2EzYm0NcodwORP47qPhLzaolAk=

纯文本是"加密很棘手"。

php不支持kCCOptionPKCS7Padding,它有自己的填充应该如何完成的想法。因此,您必须在CCCrypt之前提供填充。或者在 php 代码中执行 PKCS#7 填充。(顺便说一句:PKCS#5 和 PKCS#7 填充本质上是相同的。

您应该看到第一个块在 php 中解密正常,如果没有,则存在另一个问题。

这是我的空 (php) 填充方法:

+ (NSData *)phpPadData:(NSData *)data {
    NSMutableData *newData = [data mutableCopy];
    NSUInteger paddLength = kCCBlockSizeAES128 - (data.length % kCCBlockSizeAES128);
    [newData increaseLengthBy:paddLength];
    return [newData copy];
}

当我使用它来编码"加密很棘手"时,我得到:

密文数据: 81188e5e c7974e38 693c28a2 1e09da41 a5d64983 e124b73c 36da520a 8198d3e7ciphertextBase64: gRiOXseXTjhpPCiiHgnaQaXWSYPhJLc8NtpSCoGY0+c=

如果我使用在线网站,我得到的完全相同:http://aes.online-domain-tools.com(可能使用 php mcrypt)。这与问题的OpenSSL不同。

这是我的测试代码,我将字符串与数据编码与加密分开。我把这些类方法放在类名Test中,任何类都可以。

+ (NSString *)encryptCleartextString:(NSString *)cleartextString keyString:(NSString *)keyString ivString:(NSString *)ivString {
    NSData *cleartext = [cleartextString dataUsingEncoding:NSUTF8StringEncoding];
    NSData *key       = [keyString       dataUsingEncoding:NSUTF8StringEncoding];
    NSData *iv        = [ivString        dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ciphertext = [Test phpEncryptCleartext:cleartext key:key iv:iv];
    NSLog(@"ciphertext: %@", ciphertext);
    NSString *ciphertextString = [ciphertext base64EncodedStringWithOptions:0];
    NSLog(@"ciphertextString: %@", ciphertextString);
    return ciphertextString;
}
+ (NSData *)phpEncryptCleartext:(NSData *)cleartext key:(NSData *)key iv:(NSData *)iv {
    NSLog(@"phpEncryptCleartext");
    NSLog(@"cleartext: %@", cleartext);
    NSLog(@"key:       %@", key);
    NSLog(@"iv:        %@", iv);
    NSData *cleartextPadded = [self phpPadData:cleartext];
    NSLog(@"cleartextPadded: %@", cleartextPadded);
    CCCryptorStatus ccStatus   = kCCSuccess;
    size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
    NSMutableData  *ciphertext    = [NSMutableData dataWithLength:cleartextPadded.length];
    ccStatus = CCCrypt(kCCEncrypt,
                       kCCAlgorithmAES128,
                       0,
                       key.bytes,
                       kCCKeySizeAES128,
                       iv.bytes,
                       cleartextPadded.bytes,
                       cleartextPadded.length,
                       ciphertext.mutableBytes,
                       ciphertext.length,
                       &cryptBytes);
    if (ccStatus == kCCSuccess) {
        ciphertext.length = cryptBytes;
    }
    else {
        NSLog(@"kEncryptionError code: %d", ccStatus); // Add error handling
        ciphertext = nil;
    }
    NSLog(@"ciphertext:               %@", ciphertext);
    return ciphertext;
}
// Test code
NSString *cleartextString = @"encryption is tricky";
NSString *ivString  = @"hkPDfznq1t1UpKrW";
NSString *keyString = @"T8ZvJba0HHsmiVSD";
NSString *encryptedTextBase64 = [Test encryptCleartextString:cleartextString keyString:keyString ivString:ivString];
NSLog(@"encryptedTextBase64:     %@", encryptedTextBase64);