Google App Engine(Python)中的AES加密与iOS(Objective-C)中的解密
我正在尝试在 Python(谷歌应用引擎)中加密一些数据,然后在 iOS 上解密这些数据。
这个过程有很多问题,因为 AES 加密有很多选择,而且 Python 和 Objective-C 的格式也不同。
由于谷歌应用引擎上 PyCrypto 库的可用性有限,而 iOS/Objective-C 端的 AES 代码需要 PKCS7Padding,所以我决定在 Python 端使用 slowAES。
我还使用了 16 位的密钥、CBC 模式和 PKCS7Padding。
基于这些,我写了我的加密函数和一些辅助变量/函数:
def str2nums(s):
return map(ord, s)
key = "hjt4mndfy234n5fs"
moo = aes.AESModeOfOperation()
iv = [12, 34, 96, 15] * 4
CBC_mode = moo.modeOfOperation['CBC']
nkey = str2nums(key)
def encrypt(plaintext):
funcName = inspect.stack()[0][3]
logging.debug("Enter " + funcName)
logging.debug("Input string: " + plaintext)
m, s, encData = moo.encrypt(plaintext, CBC_mode, nkey, len(nkey), iv)
fmt = len(encData)*'B'
dataAsStr = ""
for j in encData:
dataAsStr = dataAsStr + str(j) + ","
logging.debug("Output encrypted data:[" + dataAsStr + "]")
encoded = base64.b64encode(struct.pack(fmt, *encData))
logging.debug("Output encrypted string: " + encoded)
decoded = struct.unpack(fmt, base64.b64decode(encoded))
decrypted = moo.decrypt(decoded, s, CBC_mode, nkey, len(nkey), iv)
logging.debug("Output decrypted back: " + decrypted)
return encoded
请注意,在 Python 端,我是先加密、打包,然后进行 base64 编码数据。在返回这个加密数据之前,我还做了一次解密测试,以便在日志中显示,并且确实可以正常工作。
在 iOS 端,我使用了以下 AES NSData 扩展:
- (NSData *)AES128DecryptWithKey:(NSString *)key {
// 'key' should be 16 bytes for AES128, will be null-padded otherwise
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
NSUInteger dataLength = [self 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 bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
当我拉取 base64/打包/加密的数据时,我是这样使用的:
NSMutableString * result = [[NSMutableString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
if (LOG) { NSLog(@"Base64 encoded / Encrypted string: %@", result); }
NSData* encryptedData = [NSData decodeBase64ForString:result]; // base64 decode
if (LOG) { NSLog(@"Encrypted string: %@", encryptedData); }
NSData* decryptedData = [encryptedData AES128DecryptWithKey:@"hjt4mndfy234n5fs"]; // AES Decrypt
问题是,我在客户端(iOS)上似乎无法正确解密数据,尽管在服务器(Python)上解密是没问题的。
在解密过程中,cryptStatus 总是显示为:kCCAlignmentError。 这个我不是很理解。
我也尝试过 AES 256,但我想我需要一个 32 位的密钥,而在 CBC 模式下 slowAES 似乎不支持这个(至少根据示例来看是这样?)。
服务器的日志(注意,实际未加密的数据只是一个空集合 []。这是一个 JSON 表示,用于返回给客户端。)
2012-01-04 08:48:13.962
Enter encrypt
D 2012-01-04 08:48:13.962
Input string: []
D 2012-01-04 08:48:13.967
Output encrypted data:[4,254,226,26,101,240,22,113,44,54,209,203,233,64,208,255,]
D 2012-01-04 08:48:13.967
Output encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
D 2012-01-04 08:48:13.971
Output decrypted back: []
客户端(iOS)的日志:
2012-01-04 12:45:13.891 Base64 encoded / Encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
2012-01-04 12:45:13.892 Encrypted string: <04fee21a 65f01671 2c36d1cb e940d0ff>
2012-01-04 12:45:29.126 Decrypted string:
所以我的问题是:
- 什么是“对齐错误”?或者我做错了什么,以至于客户端不想解密?
- 考虑到数据看起来匹配得很好,我需要担心解包数据吗?如果需要,我该如何在 C 或 Objective-C 中实现 unpack() 函数?
- 在谷歌应用引擎和 iOS 之间,是否有更好的 AES 加密方式?
补充说明:我还应该提到,除了使用相同的初始化向量之外,我发现 Python 和 iOS 代码在填充方面存在差异。对于小数据量,加密/解密都正常,但对于大数据量就解密失败了。我通过在 iOS 端不使用 PKCS7Padding,而是用 0 替代来解决这个问题。
1 个回答
IV需要在两端保持一致。
IV(初始化向量)是一串字节,它会通过加密器和解密器,帮助把“记忆”放在一个伪随机的状态,然后再发送“真实”的数据。因为加密的结果会受到之前数据的影响,所以这个初始化的过程让恶意的第三方在不知道IV的情况下,无法判断某个明文和密钥是否能产生特定的密文。
理想情况下,IV应该是可变的,可能是基于某个序列号,或者是与密文一起发送的其他文本,或者是基于在两端同步的计数器。
即使IV是半可预测的,它的存在也大大增加了使用“已知明文”攻击的难度。这对于相对较短、频繁发送的消息尤其重要。