Google App Engine (Python) 中的 AES 加密和 iOS (Objective-C) 上的解密
我正在尝试加密来自 python (Google App Engine) 的一些数据,然后在 iOS 上解密。
由于 AES 加密有如此多的选项以及 Python 和 Objective-C 中可用的不同格式,因此存在几个与此相关的问题。
由于 Google App Engine 上 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
问题是,我似乎无法获取数据即使它在服务器(在Python中)上解密得很好,也可以在客户端(iOS)上正确解密。
在解密过程中, 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() 函数
- 是否有更好的方法在 Google App Engine 和 iOS 之间进行 AES 加密?
编辑:我还应该注意,除了使用相同的初始化向量的答案之外,我发现了一个差异在 python 和 iOS 代码之间进行填充时,加密/解密对于少量数据工作正常,但后来我遇到了无法解密较大数据的情况,我通过将 iOS 端更改为不使用 PKCS7Padding 并将 0 放在那里来解决这个问题。 。
I'm trying to encrypt some data from python (Google App Engine) and then decrypt it on iOS.
There are several issues surrounding this based on the fact that there are so many options with AES Encryption and the different formats available in Python and Objective-C.
Because of the limited availability of the PyCrypto libraries on Google App Engine and the AES code on the iOS/Objective-C side requiring PKCS7Padding, I decided to use slowAES on the python side.
I'm also using a 16-bit key, CBC Mode, and PKCS7Padding.
Given that, this is my encrypt function and helper variables/function:
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
Note that on the Python side, I am encrypting, packing, and then base64 encoding the data. Before returning this encrypted data, I'm also doing a test run at decrypting it just to show that in the logs and it does indeed work.
On the iOS side, I'm using the following AES NSData addition:
- (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;
}
And I'm making use of this when I pull down the base64/packed/encrypted data like so:
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
The problem is, I can't seem to get the data to decrypt correctly on the client (iOS) side even though it decrypts just fine on the server (in python).
During the decryption, the cryptStatus always ends up : kCCAlignmentError. Which I don't quite understand.
I've also messed with AES 256 but I need a 32bit key I think and that doesn't seem to be an option for slowAES in CBC mode (at least according to the examples?).
Logging of the Server (Notice the actual unencrypted data is merely an empty set [] . That's a JSON representation of such to return to the client.
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: []
Logging of the client (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:
So my questions are:
- What does it mean by an "Alignment Error"? Or what am I doing wrong that it doesn't want to decrypt on the client?
- Do I need to worry about unpacking the data at all considering it looks like it matches up just fine? And if so, how would I go about an unpack() function in C or Objective-C?
- Is there just plain a better way to do AES encryption between Google App Engine and iOS?
EDIT: I should also note that besides the answer of using the same Initialization Vector, I found a discrepancy between the python and iOS code for the padding. The encrypt/decrypt worked fine for small amounts of data but then I ran into failure to decrypt with larger ones. I fixed this by changing the iOS side to NOT use PKCS7Padding and put 0 there instead.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
IV 需要两端匹配。
IV(初始化向量)是通过加密器/解密器发送的一串字节,以便在发送“真实”数据之前将其“内存”置于伪随机状态。由于加密结果取决于之前经历的情况,因此这种初始化使得恶意第三方无法(在不知道 IV 的情况下)知道给定的明文和密钥是否可以生成给定的密文。
理想情况下,IV 本身是可变的,可能基于序列号或与密文一起发送的其他文本,或者基于在两端之间同步的计数器。
IV 的存在(即使是半可预测的)显着增加了使用“已知明文”攻击的难度。这对于相对较短、频繁的消息尤其重要。
The IV needs to match on both ends.
The IV (initialization vector) is a string of bytes that's sent through the encryptor/decryptor to place its "memory" in a pseudo-random state before the "real" data is sent through. Since the encryption results depend on what's gone through before, this initialization makes it impossible (without knowing the IV) for a malicious 3rd party o know whether a given cleartext and key could have produced a given cypertext.
Ideally the IV is itself somehow variable, based, perhaps, on a serial number or some other text that's sent along with the cyphertext, or based on a counter that's synchronized between ends.
The presence of the IV (even if semi-predictable) significantly increases the difficulty of using a "known cleartext" attack. This is especially important for relatively short, frequent messages.