AES 128 解密失败
AES 128 decryption fails
我有一个 iOS 应用程序使用 AES 128 CBC 加密数据。我可以在 objective-c 中的应用程序中解密该数据,这样至少可以告诉我加密在该上下文中正常工作(Common Crypto)。问题是我需要将此加密数据发送到服务器并通过 PHP 对其进行解密。这就是我失败的地方。
我使用 openssl 在命令行上手动进行了加密。使用该输出并将其反馈给此 php 函数,我可以正确解密,因此我知道 php 函数工作正常,至少相对于 openssl。所以,问题是如何让 Objective-C 以与 openssl 相同的方式输出密文(或者如何让 php 从 Common Crypto 解密密文)。换句话说,这些功能中的每一个 'works' 在其自己的上下文中。
<?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 函数 returns 时,mcrypt_decrypt
函数的计算结果为 false。这是新的 Objective 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] = '[=11=]';
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;
}
Objective 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=
明文为'encryption is tricky'.
php 不支持 kCCOptionPKCS7Padding
,它有自己的填充方式。因此,您必须在 CCCrypt
之前提供填充。或者在 php 的代码中执行 PKCS#7 填充。 (顺便说一句:PKCS#5 和 PKCS#7 填充基本相同。)
你应该看到第一个块之前的块在 php 中解密 OK,如果不是还有另一个问题。
这是我的空(php)填充方法:
+ (NSData *)phpPadData:(NSData *)data {
NSMutableData *newData = [data mutableCopy];
NSUInteger paddLength = kCCBlockSizeAES128 - (data.length % kCCBlockSizeAES128);
[newData increaseLengthBy:paddLength];
return [newData copy];
}
当我用它来编码时 "encryption is tricky" 我得到:
ciphertextData: 81188e5e c7974e38 693c28a2 1e09da41 a5d64983 e124b73c 36da520a 8198d3e7
ciphertextBase64: gRiOXseXTjhpPCiiHgnaQaXWSYPhJLc8NtpSCoGY0+c=
这与我使用在线站点得到的完全相同:http://aes.online-domain-tools.com(可能使用 php mcrypt)。这与问题中的 OpenSSL 不同。
这里是我的测试代码,我把字符串to/from数据编码和加密分开了。我将这些 class 方法放在 class 名称测试中,任何 class 都可以。
+ (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);
我有一个 iOS 应用程序使用 AES 128 CBC 加密数据。我可以在 objective-c 中的应用程序中解密该数据,这样至少可以告诉我加密在该上下文中正常工作(Common Crypto)。问题是我需要将此加密数据发送到服务器并通过 PHP 对其进行解密。这就是我失败的地方。
我使用 openssl 在命令行上手动进行了加密。使用该输出并将其反馈给此 php 函数,我可以正确解密,因此我知道 php 函数工作正常,至少相对于 openssl。所以,问题是如何让 Objective-C 以与 openssl 相同的方式输出密文(或者如何让 php 从 Common Crypto 解密密文)。换句话说,这些功能中的每一个 'works' 在其自己的上下文中。
<?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 函数 returns 时,mcrypt_decrypt
函数的计算结果为 false。这是新的 Objective 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] = '[=11=]';
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;
}
Objective 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=
明文为'encryption is tricky'.
php 不支持 kCCOptionPKCS7Padding
,它有自己的填充方式。因此,您必须在 CCCrypt
之前提供填充。或者在 php 的代码中执行 PKCS#7 填充。 (顺便说一句:PKCS#5 和 PKCS#7 填充基本相同。)
你应该看到第一个块之前的块在 php 中解密 OK,如果不是还有另一个问题。
这是我的空(php)填充方法:
+ (NSData *)phpPadData:(NSData *)data {
NSMutableData *newData = [data mutableCopy];
NSUInteger paddLength = kCCBlockSizeAES128 - (data.length % kCCBlockSizeAES128);
[newData increaseLengthBy:paddLength];
return [newData copy];
}
当我用它来编码时 "encryption is tricky" 我得到:
ciphertextData: 81188e5e c7974e38 693c28a2 1e09da41 a5d64983 e124b73c 36da520a 8198d3e7 ciphertextBase64: gRiOXseXTjhpPCiiHgnaQaXWSYPhJLc8NtpSCoGY0+c=
这与我使用在线站点得到的完全相同:http://aes.online-domain-tools.com(可能使用 php mcrypt)。这与问题中的 OpenSSL 不同。
这里是我的测试代码,我把字符串to/from数据编码和加密分开了。我将这些 class 方法放在 class 名称测试中,任何 class 都可以。
+ (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);