【发布时间】:2021-07-07 21:09:38
【问题描述】:
我有在 iOS (CCCrypt) 和 python (pycryptdome) 上使用 AES (128 和 256) 加密/解密的功能。所有测试用例都在每个平台上工作,但是......当我从 iOS 获取 AES 密钥和加密字符串到 python 时,解密失败。我已经广泛研究并尝试了各种用例,但均无济于事。
我在这里创建了一个简单的测试用例,其中包含 iOS 加密和 python 解密,希望有人能告诉我我在平台上的不同之处。
iOS 代码 测试用例
NSString *test_aes = @"XSmTe1Eyw8JsZkreIFUpNi7BhKEReHTP";
NSString *test_string = @"This is a test string";
NSData *clearPayload = [test_string dataUsingEncoding:NSUTF8StringEncoding];
NSData *encPayload = nil;
char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
// fetch key data
[test_aes getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = clearPayload.length;
size_t bufferSize = dataLength + kCCKeySizeAES256;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[clearPayload bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted );
NSString *encString = @"Error";
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
encPayload = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
encString = [encPayload base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
//free( buffer ); //free the buffer
NSLog(@"Src = %@ AES = %@ String = %@",test_string, test_aes, encString);
encPayload = [[NSData alloc] initWithBase64EncodedString:encString options:NSDataBase64DecodingIgnoreUnknownCharacters];
clearPayload = nil;
char keyPtr2[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero( keyPtr2, sizeof( keyPtr2 ) ); // fill with zeroes (for padding)
// fetch key data
[test_aes getCString:keyPtr2 maxLength:sizeof( keyPtr2 ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength2 = [encPayload length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize2 = dataLength2 + kCCKeySizeAES256;
void *buffer2 = malloc( bufferSize2 );
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus2 = CCCrypt( kCCDecrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[encPayload bytes], dataLength2, /* input */
buffer2, bufferSize2, /* output */
&numBytesDecrypted );
NSString *clearString = @"Error";
if( cryptStatus2 == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
clearPayload = [NSData dataWithBytesNoCopy:buffer2 length:numBytesDecrypted];
clearString = [[NSString alloc] initWithData:clearPayload encoding:NSUTF8StringEncoding];
}
NSLog(@"Res = %@",clearString);
此代码中的加密和解密工作正常,输出为:
Src = This is a test string
AES = XSmTe1Eyw8JsZkreIFUpNi7BhKEReHTP
String = hUbjWyXX4mB01gI0RJhYQRD0iAjQnkGTpsnKcmDpvaQ=
Res = This is a test string
当我将编码后的字符串和 aes 密钥带到 python 来测试这段代码时:
key = "XSmTe1Eyw8JsZkreIFUpNi7BhKEReHTP"
data = "hUbjWyXX4mB01gI0RJhYQRD0iAjQnkGTpsnKcmDpvaQ="
usekey = key
useData = data
if isinstance(key, str):
usekey = key.encode('utf-8')
cipher = AES.new(usekey, AES.MODE_GCM, nonce=self.nonce)
print("nonce", cipher.nonce)
if isinstance(data, str):
useData = data.encode('utf-8')
useData = b64decode(useData)
puseData = useData # unpad(useData,32)
print("decrypt:In bytes=", puseData)
result = cipher.decrypt(puseData)
print ("decrypt:Out bytes=",result)
解密失败,输出为
nonce b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
decrypt:In bytes= b'\x85F\xe3[%\xd7\xe2`t\xd6\x024D\x98XA\x10\xf4\x88\x08\xd0\x9eA\x93\xa6\xc9\xcar`\xe9\xbd\xa4'
decrypt:Out bytes= b'\x08\xc58\x962q\x94\xff#\xfa\xab\xe2\xc8{b\xed\x0b\xedw\x8f\xe3\xec\x0b\x8e\xfb\xcc\x12\x7f\x9e\xb4\x8f\xd6'
上述两个例程都可以毫无问题地处理本地加密数据,我已经破解了这里的示例(包括不释放 malloc 的缓冲区:) 用于调试目的,所以我为有点脏的代码道歉。
注意:当我看到注释 iOS 可能使用它而不是 GCM 时,我尝试将 python 模式更改为 AES.MODE_CBC(并添加了填充代码),这也失败了......现在我保留了 nonce / iv作为 0 的数组,据我所知,iOS 将使用它作为默认值,因为没有提供 CCCrypt,当此示例有效时,我将转换到指定的 iv。
我会很感激任何方向。
编辑: 我继续并在 iOS 端指定了一个空 IV
char iv[16]; // also tried 17
bzero( iv, sizeof( iv ) );
行为完全没有变化...
编辑: 我在两个系统上将 IV 设置为所有 char '1' 并得到相同的结果。 iOS添加代码:
NSString *hardCodeIV = @"1111111111111111";
char iv[17];
bzero( iv, sizeof( iv ) );
[hardCodeIV getCString:iv maxLength:sizeof(iv) encoding:NSUTF8StringEncoding];
生产的
Src = This is a test string
AES = XSmTe1Eyw8JsZkreIFUpNi7BhKEReHTP
String = sFoZ24VRN1hyMzegXT+GFzAn/YGPvaKO8p1eD+xhGaU=
Res = This is a test string
所以在 iOS 上,它使用字节 0 和字符 1 IV 正确加密和解密......
当使用任何一种 IV 在本地加密和解密时,python 代码也能正常工作......但是当在 python 上使用 iOS 加密的输出来解密时,它会失败,如下所示。
将密钥和加密消息移动到python进行解密:
key = "XSmTe1Eyw8JsZkreIFUpNi7BhKEReHTP"
data = "sFoZ24VRN1hyMzegXT+GFzAn/YGPvaKO8p1eD+xhGaU="
usekey = key
useData = data
if isinstance(key, str):
usekey = key.encode('utf-8')
cipher = AES.new(usekey, AES.MODE_GCM, nonce=self.nonce)
print("nonce", cipher.nonce)
if isinstance(data, str):
useData = data.encode('utf-8')
useData = b64decode(useData)
puseData = useData # unpad(useData,32)
print("decrypt:In bytes=", puseData)
result = cipher.decrypt(puseData)
print ("decrypt:Out bytes=",result)
导致:
nonce b'1111111111111111'
decrypt:In bytes= b"\xb0Z\x19\xdb\x85Q7Xr37\xa0]?\x86\x170'\xfd\x81\x8f\xbd\xa2\x8e\xf2\x9d^\x0f\xeca\x19\xa5"
decrypt:Out bytes= b'\xc3\x1e"w\x86:~\x86\xd3\xc9H3\xd3\xd3y)|,|\xe02(\xc6\x17\xa3\x1e\xe2\x0f\x1a#\xbbW'
所以,还是不开心……
看起来很像算法选择是问题所在,但 iOS 上的选项似乎只有 GCM 或 CBC,而 GCM 是默认设置……大多数测试都是在 GCM 上完成的。我尝试在一项测试中使用 CBC(没有 IV,因为它不需要),以防 iOS 实际使用它并且没有告诉我,但如上所示,这也没有成功。
我正在继续测试方法,但确实可以使用完成这项工作的人的一些建议 - 我无法找到有效的示例。 [附带说明,RSA 模型工作正常 - 这就是我移动 AES 密钥的方式 - 解决方案的那部分目前完美无缺,这是我需要投入使用的最后一点)。
【问题讨论】:
标签: python ios cryptography aes