Skip to main content
 首页 » 编程设计

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

2025年05月04日20xiaohuochai

我正在尝试从 python (Google App Engine) 加密一些数据,然后在 iOS 上解密。

基于 AES 加密有很多选项以及 Python 和 Objective-C 中可用的不同格式这一事实,围绕此存在几个问题。

由于 PyCrypto 库在 Google App Engine 上的可用性有限,并且 iOS/Objective-C 端的 AES 代码需要 PKCS7Padding,我决定在 python 端使用 slowAES。

我还使用了 16 位 key 、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 位 key ,这似乎不是 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. 是否有更好的方法在 Google App Engine 和 iOS 之间进行 AES 加密?

编辑:我还应该注意到,除了使用相同初始化向量的答案外,我发现 python 和 iOS 代码之间的填充差异。加密/解密对于少量数据工作正常,但后来我遇到了无法用较大数据解密的情况。我通过将 iOS 端更改为不使用 PKCS7Padding 并将 0 放在那里来解决此问题。

请您参考如下方法:

IV 需要在两端匹配。

IV(初始化向量)是一串字节,通过加密器/解密器发送,以便在发送“真实”数据之前将其“内存”置于伪随机状态。由于加密结果取决于之前经过的过程,因此这种初始化使得恶意的第 3 方无法(不知道 IV)知道给定的明文和 key 是否可以生成给定的密文。

理想情况下,IV 本身在某种程度上是可变的,可能基于序列号或与密文一起发送的某些其他文本,或者基于在端点之间同步的计数器。

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