Google App Engine(Python)中的AES加密与iOS(Objective-C)中的解密

2 投票
1 回答
1957 浏览
提问于 2025-04-17 09:33

我正在尝试在 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: 

所以我的问题是:

  1. 什么是“对齐错误”?或者我做错了什么,以至于客户端不想解密?
  2. 考虑到数据看起来匹配得很好,我需要担心解包数据吗?如果需要,我该如何在 C 或 Objective-C 中实现 unpack() 函数?
  3. 在谷歌应用引擎和 iOS 之间,是否有更好的 AES 加密方式?

补充说明:我还应该提到,除了使用相同的初始化向量之外,我发现 Python 和 iOS 代码在填充方面存在差异。对于小数据量,加密/解密都正常,但对于大数据量就解密失败了。我通过在 iOS 端不使用 PKCS7Padding,而是用 0 替代来解决这个问题。

1 个回答

0

IV需要在两端保持一致。

IV(初始化向量)是一串字节,它会通过加密器和解密器,帮助把“记忆”放在一个伪随机的状态,然后再发送“真实”的数据。因为加密的结果会受到之前数据的影响,所以这个初始化的过程让恶意的第三方在不知道IV的情况下,无法判断某个明文和密钥是否能产生特定的密文。

理想情况下,IV应该是可变的,可能是基于某个序列号,或者是与密文一起发送的其他文本,或者是基于在两端同步的计数器。

即使IV是半可预测的,它的存在也大大增加了使用“已知明文”攻击的难度。这对于相对较短、频繁发送的消息尤其重要。

撰写回答