【问题标题】:Different Results in Go and Pycrypto when using AES-CFB使用 AES-CFB 时 Go 和 Pycrypto 的不同结果
【发布时间】:2014-05-27 19:54:35
【问题描述】:

我正在向现有的 python 代码库添加一个 go 应用程序。我在处理语言之间的加密时遇到了麻烦。这是使用 go 1.2.1 和 Python 2.7.x / PyCrypto 2.7a1。

这是 Python 示例:

import Crypto.Cipher
import Crypto.Hash.HMAC
import Crypto.Hash.SHA256
import Crypto.PublicKey.RSA
from binascii import hexlify, unhexlify

#encrypt
payload =  unhexlify("abababababababababababababababababababababababababababababababab")
password = unhexlify("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
iv = unhexlify("00000000000000000000000000000000")

print "IV: ", hexlify(iv), "len: ", len(iv)
print "Password length: ", len(password)


cipher = Crypto.Cipher.AES.new(
            key=password, 
            mode=Crypto.Cipher.AES.MODE_CFB, 
            IV=iv)

payload = cipher.encrypt(payload)

print hexlify(payload) #dbf6b1877ba903330cb9cf0c4f530d40bf77fe2bf505820e993741c7f698ad6b

这是 Go 示例:

package main

import (
    "fmt"
    "crypto/cipher"
    "crypto/aes"
    "encoding/hex"
)

// encrypt
func main() {
    payload, err1 := hex.DecodeString("abababababababababababababababababababababababababababababababab")
    password, err2 := hex.DecodeString("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
    iv, err3 := hex.DecodeString("00000000000000000000000000000000")

    if err1 != nil {
        fmt.Printf("error 1: %v", err1)
        return
    }

    if err2 != nil {
        fmt.Printf("error 2: %v", err2)
        return
    }

    if err3 != nil {
        fmt.Printf("error 3: %v", err3)
        return
    }

    aesBlock, err4 := aes.NewCipher(password)

    fmt.Printf("IV length:%v\n", len(iv))
    fmt.Printf("password length:%v\n", len(password))

    if err4 != nil {
        fmt.Printf("error 4: %v", err4)
        return
    }

    cfbDecrypter := cipher.NewCFBEncrypter(aesBlock, iv)
    cfbDecrypter.XORKeyStream(payload, payload) 

    fmt.Printf("%v\n", hex.EncodeToString(payload)) // db70cd9e6904359cb848410bfa38d7d0a47b594f7eff72d547d3772c9d4f5dbe
}

这里是golang link,我找不到安装了 PyCrypto 的 Python pastebin。

正如标题和来源所暗示的,两个 sn-ps 产生不同的密文:
蟒蛇:dbf6b1877ba903330cb9cf0c4f530d40bf77fe2bf505820e993741c7f698ad6b
Golang:db70cd9e6904359cb848410bfa38d7d0a47b594f7eff72d547d3772c9d4f5dbe

两种语言都可以解密它们的“本机”密文,但都不能解密其他语言。因为 python 实现已经存在,所以我正在寻找一种解决方案,允许 Go 解密使用示例 PyCrypto AES 设置和密钥大小加密的密文。

【问题讨论】:

    标签: python encryption go pycrypto


    【解决方案1】:

    对当前系统的研究表明,我们的 python 系统使用的是 CFB8(8 位段)。 Go 不支持开箱即用,但当前 CFBDecrypter / CFBEncrypter 中使用的源代码看起来可以相当容易地进行调整。

    【讨论】:

    • 不知道我是否应该为你的陈述投赞成票,还是为我生命中失去的 30 分钟试图弄清楚这一点投反对票:)
    • 为了弄清楚这一点,我失去了 30 多分钟的生命,这让我感到安慰:D
    • CFB# Go 使用的任何指针?从 Go 到 C# 的“翻译”遇到了类似的问题。
    • 我最终能够解决这个问题。 Go 似乎使用了 128 的反馈,但没有填充,C# 抱怨解密时的大小。解决方案:自己将加密文本填充到最近的 128B(跟踪您附加了多少字节,简单的 0 字节有效),然后在解密后删除该填充。瞧!
    【解决方案2】:

    如果我们将AES 对象的segment_size 从默认的8 更改为AES.block_size*8(即128),似乎可以使密码与Go 的crypto/cipher 兼容,如下所示:

    Crypto.Cipher.AES.new(
                key=password, 
                mode=Crypto.Cipher.AES.MODE_CFB, 
                IV=iv,
                segment_size=AES.block_size*8
    )
    

    【讨论】:

    • 我收到一个错误:ValueError: Input strings must be a multiple of the segment size 16 in length
    • 你需要记住填充你的输入
    • 无论反馈大小如何,技术上都不需要为 CFB 模式填充输入。但如果 pycrypto 坚持这一点,您可能只需用任意字节填充输入,然后在加密/解密后从输出末尾删除那么多字节。
    【解决方案3】:

    如果有人正在寻找分段大小 = 8 的 CFB 模式的 Go 实现,您可以使用:

    import "crypto/cipher"
    
    // CFB stream with 8 bit segment size
    // See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    type cfb8 struct {
        b         cipher.Block
        blockSize int
        in        []byte
        out       []byte
    
        decrypt bool
    }
    
    func (x *cfb8) XORKeyStream(dst, src []byte) {
        for i := range src {
            x.b.Encrypt(x.out, x.in)
            copy(x.in[:x.blockSize-1], x.in[1:])
            if x.decrypt {
                x.in[x.blockSize-1] = src[i]
            }
            dst[i] = src[i] ^ x.out[0]
            if !x.decrypt {
                x.in[x.blockSize-1] = dst[i]
            }
        }
    }
    
    // NewCFB8Encrypter returns a Stream which encrypts with cipher feedback mode
    // (segment size = 8), using the given Block. The iv must be the same length as
    // the Block's block size.
    func newCFB8Encrypter(block cipher.Block, iv []byte) cipher.Stream {
        return newCFB8(block, iv, false)
    }
    
    // NewCFB8Decrypter returns a Stream which decrypts with cipher feedback mode
    // (segment size = 8), using the given Block. The iv must be the same length as
    // the Block's block size.
    func newCFB8Decrypter(block cipher.Block, iv []byte) cipher.Stream {
        return newCFB8(block, iv, true)
    }
    
    func newCFB8(block cipher.Block, iv []byte, decrypt bool) cipher.Stream {
        blockSize := block.BlockSize()
        if len(iv) != blockSize {
            // stack trace will indicate whether it was de or encryption
            panic("cipher.newCFB: IV length must equal block size")
        }
        x := &cfb8{
            b:         block,
            blockSize: blockSize,
            out:       make([]byte, blockSize),
            in:        make([]byte, blockSize),
            decrypt:   decrypt,
        }
        copy(x.in, iv)
    
        return x
    }
    

    【讨论】:

    • 我相信这会通过减少循环长度来削弱安全属性。它还引入了低效率。您可能应该使用具有完整反馈大小的 Go 实现并修复 Python 或 Mcrypt。另请参阅安全堆栈交换上的Cipher Feedback Mode
    • @jww:对于新系统,或者如果您可以随意更改现有系统的加密方案,我同意。 (事实上​​,我会更进一步,建议您可能根本不应该使用 CFB 模式;您应该使用 CTR 模式,或者更好的是 authenticated encryption 模式,例如 SIV。)但是,有时您会这样做必须使用无法更改的密码模式与旧系统进行互操作。
    • 附言。虽然testing your code 代表another question,但我注意到由于XORKeyStream 中的拼写错误(= src[i] 而不是= dst[i]),您的代码在加密模式下被破坏。我已经编辑了你的答案来修复它,它现在似乎可以工作了(只要它现在正确地解密了自己的密文,并匹配其他语言的实现)。
    【解决方案4】:

    我发现从 Python 端处理这个问题的最简单方法是使用 M2Crypto 库。

    最终代码如下:

    import M2Crypto.EVP
    
    iv = ciphertext[:16]
    ciphertext = ciphertext[16:]
    
    cipher = M2Crypto.EVP.Cipher('aes_256_cfb', t, iv, 0)
    text = cipher.update(ciphertext)
    print text
    

    无需在 Go 中进行更改即可完美运行。

    【讨论】:

      【解决方案5】:

      我通过适应这样的python代码来解决(golang编码和python解码):

      # golang encode
      padNum := len(data) % 16
      if padNum != 0 {
          for i := 0; i < 16-padNum; i++ {
              data = append(data, ',')
          }
      }
      
      # python decode
      cipher = AES.new(key=self.key, mode=AES.MODE_CFB, IV=iv,segment_size=128)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-27
        • 1970-01-01
        • 2015-03-29
        • 2020-11-09
        • 1970-01-01
        • 2017-04-12
        • 1970-01-01
        相关资源
        最近更新 更多