【问题标题】:Golang AES ECB EncryptionGolang AES ECB 加密
【发布时间】:2014-06-05 23:48:36
【问题描述】:

尝试在 Go 中模拟一种基本上是 AES ECB 模式加密的算法。

这是我目前所拥有的

func Decrypt(data []byte) []byte {
    cipher, err := aes.NewCipher([]byte(KEY))
    if err == nil {
        cipher.Decrypt(data, PKCS5Pad(data))
        return data
    }
    return nil
}

我还有一个 PKCS5Padding 算法,它已经过测试并且可以工作,它首先填充数据。我在 Go AES 包中找不到任何关于如何切换加密模式的信息(绝对不在 the docs 中)。

我有这个代码是另一种语言的,这就是我知道这个算法不能正常工作的原因。

编辑:这是我在问题页面上解释的方法

func AESECB(ciphertext []byte) []byte {
    cipher, _ := aes.NewCipher([]byte(KEY))
    fmt.Println("AESing the data")
    bs := 16
    if len(ciphertext)%bs != 0     {
        panic("Need a multiple of the blocksize")
    }

    plaintext := make([]byte, len(ciphertext))
    for len(plaintext) > 0 {
        cipher.Decrypt(plaintext, ciphertext)
        plaintext = plaintext[bs:]
        ciphertext = ciphertext[bs:]
    }
    return plaintext
}

这实际上并没有返回任何数据,也许我在将其从加密更改为解密时搞砸了

【问题讨论】:

  • 错误说明了什么?你有/你能提供一个示例游乐场吗?
  • 在加密之前填充明文,之后取消填充。而且你只对最后一个块这样做,而不是中间的任何地方。
  • @owlstead 我看到的实现实际上在解密之前填充了加密数据......这可能是正确的吗? github.com/martinp/pysnap/blob/master/pysnap/utils.py#L40
  • 你这里特别需要ECB模式加密吗?您要加密什么/是否需要与 API 进行必要的互操作?
  • @Jameo 不,这不正确。这意味着您添加了一个完整的填充块(因为密文始终是块大小的 x 倍)。因此,明文将包含所有内容包括末尾的填充和一个随机垃圾块。所以它会解密,是的,但结果不正确。

标签: encryption go aes ecb


【解决方案1】:

Electronic codebook ("ECB") 是一种非常简单的操作模式。待加密的数据被分成字节块,所有字节块大小相同。对于每个块,应用一个密码,在本例中为AES,生成加密块。

下面的代码sn-p解密ECB中的AES-128数据(注意块大小为16字节):

package main

import (
    "crypto/aes"
)

func DecryptAes128Ecb(data, key []byte) []byte {
    cipher, _ := aes.NewCipher([]byte(key))
    decrypted := make([]byte, len(data))
    size := 16

    for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
        cipher.Decrypt(decrypted[bs:be], data[bs:be])
    }

    return decrypted
}

正如@OneOfOne 所提到的,ECB 是不安全的并且很容易被检测到,因为重复的块总是会加密到相同的加密块。这个Crypto SE answer 很好地解释了原因。

【讨论】:

  • 谢谢你,很有帮助。值得注意的是,这不仅限于 128 位加密 - 通过传入 32 字节密钥而不是 16 字节密钥,它也适用于 256 位。
【解决方案2】:

为什么?我们故意将欧洲央行排除在外:它不安全,如果需要的话 实现起来很简单。

https://github.com/golang/go/issues/5597

【讨论】:

  • 好点,如果你点击链接,你会在 Go 中找到简单的 ECB 模式实现
  • 但是,当然,您不会将这种“简单的 ECB 模式实现”放入任何软件中(永远),因为它存在重大缺陷,对吧? :)
  • @elithrar 绝对不适用于新软件...这是我尝试与之交互的现有 API 的要求
【解决方案3】:

我使用了你的代码,所以我觉得有必要向你展示我是如何修复它的。

我正在为 Go 中的这个问题做 cryptopals challenges。

我将引导您完成错误,因为代码大部分是正确的。

for len(plaintext) > 0 {
    cipher.Decrypt(plaintext, ciphertext)
    plaintext = plaintext[bs:]
    ciphertext = ciphertext[bs:]
}

循环确实会解密数据,但不会将其放在任何地方。它只是简单地移动两个数组,不产生任何输出。

i := 0
plaintext := make([]byte, len(ciphertext))
finalplaintext := make([]byte, len(ciphertext))
for len(ciphertext) > 0 {
    cipher.Decrypt(plaintext, ciphertext)
    ciphertext = ciphertext[bs:]
    decryptedBlock := plaintext[:bs]
    for index, element := range decryptedBlock {
        finalplaintext[(i*bs)+index] = element
    }
    i++
    plaintext = plaintext[bs:]
} 
return finalplaintext[:len(finalplaintext)-5]

这项新改进的作用是将解密的数据存储到一个名为 finalplaintext 的新 [] 字节中。如果你返回,你会得到数据。

这样做很重要,因为 Decrypt 函数一次只能处理一个块大小。

我返回一个切片,因为我怀疑它被填充了。我是密码学和 Go 的新手,所以任何人都可以随时更正/修改。

【讨论】:

    【解决方案4】:

    理想情况下,您希望实现crypto/cipher#BlockMode 接口。由于官方不存在,我以crypto/cipher#NewCBCEncrypter为起点:

    package ecb
    import "crypto/cipher"
    
    type ecbEncrypter struct { cipher.Block }
    
    func newECBEncrypter(b cipher.Block) cipher.BlockMode {
       return ecbEncrypter{b}
    }
    
    func (x ecbEncrypter) BlockSize() int {
       return x.Block.BlockSize()
    }
    
    func (x ecbEncrypter) CryptBlocks(dst, src []byte) {
       size := x.BlockSize()
       if len(src) % size != 0 {
          panic("crypto/cipher: input not full blocks")
       }
       if len(dst) < len(src) {
          panic("crypto/cipher: output smaller than input")
       }
       for len(src) > 0 {
          x.Encrypt(dst, src)
          src, dst = src[size:], dst[size:]
       }
    }
    

    【讨论】:

      【解决方案5】:

      我被几件事弄糊涂了。

      首先我需要上述算法的 aes-256 版本,但是当给定密钥的长度为 32 时,显然 aes.Blocksize(即 16)不会改变。所以给出长度为 32 的密​​钥就足够了使算法aes-256

      其次,解密后的值仍然包含填充,并且填充值根据加密字符串的长度而变化。例如。当有 5 个填充字符时,填充字符本身将为 5。

      这是我的函数,它返回一个字符串:

      func DecryptAes256Ecb(hexString string, key string) string {
        data, _ := hex.DecodeString(hexString)
      
        cipher, _ := aes.NewCipher([]byte(key))
      
        decrypted := make([]byte, len(data))
        size := 16
      
        for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
          cipher.Decrypt(decrypted[bs:be], data[bs:be])
        }
      
        // remove the padding. The last character in the byte array is the number of padding chars
        paddingSize := int(decrypted[len(decrypted)-1])
        return string(decrypted[0 : len(decrypted)-paddingSize])
      }
      

      【讨论】:

      • 1. AES 有一个 16 字节的块大小,它与密钥大小无关。 AES 具有 128、192 和 256 位三种密钥大小。 2. 这里看到的填充是PKCS#7,如果要加密的数据并不总是块大小的倍数,则填充是必要的。 3. 大多数 AES 实现(aes Go 实现不会)将处理超过一个块的输入数据,并自动处理块调用和填充。 4. 请参阅crypto/cipher 处理阻塞和填充。
      猜你喜欢
      • 1970-01-01
      • 2016-06-25
      • 1970-01-01
      • 2019-04-18
      • 2018-07-08
      • 1970-01-01
      • 2016-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多