1

我有以下代码

func encrypt(key, data string) (string, error) {
    byteKey := []byte(key)
    plaintext := []byte(data)

    block, err := aes.NewCipher(byteKey)
    if err != nil {
        return "", err
    }
    ciphertext := make([]byte, len(plaintext))
    stream := cipher.NewCTR(block, byteKey[aes.BlockSize:])
    stream.XORKeyStream(ciphertext, plaintext)
    return base64.StdEncoding.EncodeToString(ciphertext), nil
}

哪里byteKey = []byte("4e8f1670f502a3d40717709e5f80d67c") (不确定这是否是正确的语法,但这是十六进制的关键。)

我的任务是用任何语言编写解密程序,这就是我目前所做的:

import base64
from Crypto.Cipher import AES

def decrypt(ct):
        key = '4e8f1670f502a3d40717709e5f80d67c'.decode('hex')
        nonce = 0
        ct1 = base64.b64decode(ct)
        cipher = AES.new(key, mode=AES.MODE_CTR, counter=lambda: nonce)
        print str(cipher.decrypt(ct1))

我做错了什么我只是不知道是什么。请问高手可以帮忙吗?提前致谢。

4

1 回答 1

3

提示:想想计数器模式是如何工作的。您的代码中的初始计数器值是什么,后续值是什么?它们应该是什么?


counter=lambda: nonce

这是一个始终返回相同值的常量函数,在您的程序中为 0。但这不是您需要传递给的counter:您需要传递一个函数,该函数在每次调用时返回当前计数器值。例如,如果初始计数器值为 0,则此函数第一次调用时必须返回 0,第二次调用时返回 1,第三次调用时返回 2,等等。更准确地说,它必须返回'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', then'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'等。

Python 接口在这里设计得很糟糕。它过于灵活,但这种灵活性在实践中从来没有用过,并且很难正确使用 API。此外,文档根本不清楚:

counter(可调用)-(仅MODE_CTR)。返回下一个计数器块的有状态函数,它是字节的字节串block_size。为了获得更好的性能,请使用Crypto.Util.Counter.

实际上,您非常需要使用Crypto.Util.Counter,不仅仅是为了“性能”,而是为了使计算正确。不要难过:你不是第一个被这个绊倒的人。

面对一个您无法弄清楚的 API,您接下来可能会转向 Stack Overflow……这个问题已经在PyCrypto 问题中使用 AES+CTR得到了回答,但请注意,7 年来这个问题没有正确答案,尽管有一个被赞成和接受的。我的回答显示了如何使用MODE_CTRand counter

第二个问题可能是初始计数器值。为 CTR 模式选择初始计数器值有两种主要策略:

  • 对于一次性密钥,从 0 开始,并且不要将 ICV 与消息一起传输,因为它是众所周知的常数。
  • 对于多用密钥,每次生成一个随机值,在密文开头发送ICV。

我不熟悉您发布的加密代码中的 API。但由于它返回与明文长度相同的密文,它可能使用一个常量 ICV(这对于一次性密钥来说很好,但如果密钥被重用则灾难性的),很可能是 0。不过,请检查该文档API。

(如果您需要进一步的编码帮助,请在Stack Overflow而不是Cryptography上提问,因为编码问题在 Cryptography.SE 上是题外话。并且一定要发布完整的代码来重现问题,包括输入和输出。)

于 2018-11-12T22:03:07.373 回答