iOS中的AES解密:PKCS5填充和CBC

时间:2011-02-09 22:49:48

标签: ios aes padding

我正在为iOS实现一些解密代码,用于源自我无法控制的服务器上的消息。另一个平台上的先前实现记录了解密要求AES256,指定了密钥和初始化向量,并且还说:

 * Cipher Mode: CBC
 * Padding: PKCS5Padding

创建CCCryptor对象的选项仅包括kCCOptionPKCS7Padding和kCCOptionECBMode,注意CBC是默认值。根据我对加密填充的理解,我不明白如何使用这两者;我以为它们是相互排斥的。在创建用于解密的CCCryptor时,我尝试使用0作为选项和kCCOptionPKCS7Padding,但是在解密之后都给了我乱码。

我已将此解密的转储与另一平台上已解码的字节缓冲区的转储进行了比较,并确认它们确实不同。所以我在这个实现中做的事情有很大不同,我只是不知道是什么......并且没有关于如何处理它的线索。平台不同,很难从之前的实现中推断出很多,因为它基于一个非常不同的平台。当然,之前实施的作者已经离开了。

任何猜测还有什么不兼容或如何解决这个问题?

4 个答案:

答案 0 :(得分:6)

PKCS#5 padding and PKCS#7 padding are practically the same(将字节01,或0202或0303等添加到算法块大小的长度,在这种情况下为16字节)。正式的PKCS#5填充应仅用于8字节块,但在许多运行时,两者可以互换而没有问题。填充总是出现在密文的末尾,所以如果你只是乱码,那就不是填充。 ECB是一种块操作模式(不应该用于加密可以与随机数区分开的数据):它需要填充,因此两者不是互斥的。

最后,如果你只是执行解密(不是MAC或其他形式的完整性控制),并且你将unpadding的结果返回给服务器(解密失败),那么由于填充oracle,你的纯文本数据是不安全的攻击。

答案 1 :(得分:4)

首先,您可以稍后担心填充。像你所做的那样提供0意味着AES CBC没有填充,并且使用该配置你应该看到你的消息很好。 Albiet可能在末尾有一些填充字节。离开:

  1. 您没有正确加载密钥。
  2. 您没有正确加载IV。
  3. 您没有正确加载数据。
  4. 服务器正在做一些你不期望的事情。
  5. 要调试此操作,您需要隔离系统。您可以通过实施环回测试来实现此目的,您可以在其中加密然后解密数据以确保正确加载所有内容。但这可能会产生误导。即使你做错了(例如,向后加载密钥),你仍然可以解密你加密的内容,因为你在双方都采用了完全相同的错误方式。

    所以你需要对Known Answer Tests(KATs)进行测试。您可以在AES wikipedia entry上查找官方KAT。但事实恰恰相反,我已将another answer发布在我们可以使用的SO上。

    鉴于此输入:

    KEY: 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
    IV:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    PLAIN TEXT:   encrypt me
    CIPHER TEXT:  338d2a9e28208cad84c457eb9bd91c81
    

    使用第三方程序验证您是否可以解密密文并获取纯文本。

    $ echo -n "encrypt me" > to_encrypt
    $ openssl enc -in to_encrypt -out encrypted -e -aes-256-cbc \
    > -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
    > -iv 0000000000000000
    $ hexdump -C encrypted
    00000000  33 8d 2a 9e 28 20 8c ad  84 c4 57 eb 9b d9 1c 81  |3.*.( ....W.....|
    00000010
    $ openssl enc -in encrypted -out plain_text -d -aes-256-cbc \
    > -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
    > -iv 0000000000000000
    $ hexdump -C plain_text 
    00000000  65 6e 63 72 79 70 74 20  6d 65                    |encrypt me|
    0000000a
    

    所以现在尝试在你的程序中解密这个已知答案测试。一定要启用PKCS7填充,因为这是我在这个例子中使用的。作为练习,解密它没有填充,并看到结果是相同的,除了你在“加密我”文本后有填充字节。

    实施KAT是一大步。它表示您的实现正确,但您对服务器行为的假设是错误的。然后是时候开始质疑这些假设......

    (和P.S.,你提到的那些选项并不相互排斥.ECB意味着没有IV,而CBC意味着你有IV。与填充无关。)

    好的,我知道我说这是一个练习,但我想证明即使你用填充加密 并且在没有填充的情况下解密,你不会得到垃圾。因此,考虑到使用PKCS7填充的KAT,我们使用 no padding 选项对其进行解密,并获得一条可读消息,后跟06用作填充字节。

    $ openssl enc -in encrypted -out plain_text -d -aes-256-cbc \
    -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
    -iv 0000000000000000 -nopad
    $ hexdump -C plain_text
    00000000  65 6e 63 72 79 70 74 20  6d 65 06 06 06 06 06 06  |encrypt me......|
    00000010
    $ 
    

答案 2 :(得分:3)

保罗,
需要PKCS#5填充来识别解密数据中的填充。对于CBC,输入缓冲区必须是密码块大小的倍数(AES为16)。因此,要加密的缓冲区扩展了额外的字节。请注意,加密后,数据的原始大小将丢失。 PKCS#5填充允许检索该大小。这是通过用重复的字节填充扩展数据缓冲区来完成的,其值等于填充大小。例如,如果您的明文缓冲区是12个字节,要使其为16的倍数,则需要再添加4个字节。 (如果数据为16,则会再添加16个以使其成为32)。然后用'0x4'填充这4个字节以符合PKCS#5填充。解密时,只需查找解密数据中的最后一个字节,然后从解密缓冲区的长度中减去该数字。

你正在做的是填充'0'。虽然您似乎很高兴看到结果,但当您的原始数据以“0”之一结束时,您会感到惊讶。

答案 3 :(得分:0)

事实证明,对我所遇到的内容的解释非常简单:我误解了我在之前的实现中读到的内容,意味着它使用的是256位密钥,但实际上它使用的是128位密钥键。做出这样的改变,突然之间那些晦涩难懂的东西变成了明文。 : - )

对于调用CBC的options参数,

0实际上是正确的。在前面的实现中对PKCS5填充的引用仍然是神秘的,但这并不重要,因为因为我现在所拥有的功能。

感谢拍摄,个人。