AES 加密 Golang 和 Python

sti*_*ihl 4 python encryption cryptography aes go

我正在为自己做一个有趣的业余项目。一个 golang 服务器和一个 python 客户端。我希望传输的数据被加密,但似乎无法让两种加密方案一起工作。我是加密方面的新手,所以请像对幼儿说话一样解释。

\n\n

这是我的 golang 加密函数:

\n\n
import (\n    "crypto/aes"\n    "crypto/cipher"\n    "crypto/rand"\n    "errors"\n    "io"\n    "fmt"\n)\nfunc Encrypt(key, text []byte) (ciphertext []byte, err error) {\n    var block cipher.Block\n    if block, err = aes.NewCipher(key); err != nil {\n        return nil, err\n    }\n    ciphertext = make([]byte, aes.BlockSize+len(string(text)))\n    iv := ciphertext[:aes.BlockSize]\n    fmt.Println(aes.BlockSize)\n    if _, err = io.ReadFull(rand.Reader, iv); err != nil {\n        return\n    }\n    cfb := cipher.NewCFBEncrypter(block, iv)\n    cfb.XORKeyStream(ciphertext[aes.BlockSize:], text)\n    return\n}\n\nfunc Decrypt(key, ciphertext []byte) (plaintext []byte, err error) {\n    var block cipher.Block\n    if block, err = aes.NewCipher(key); err != nil {\n        return\n    }\n    if len(ciphertext) < aes.BlockSize {\n        err = errors.New("ciphertext too short")\n        return\n    }\n    iv := ciphertext[:aes.BlockSize]\n    ciphertext = ciphertext[aes.BlockSize:]\n    cfb := cipher.NewCFBDecrypter(block, iv)\n    cfb.XORKeyStream(ciphertext, ciphertext)\n    plaintext = ciphertext\n    return\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我的 Python 实现:

\n\n
 class AESCipher:\n    def __init__( self, key ):\n        self.key = key\n        print "INIT KEY" + hexlify(self.key)\n    def encrypt( self, raw ):\n        print "RAW STRING: " + hexlify(raw)\n        iv = Random.new().read( AES.block_size )\n        cipher = AES.new( self.key, AES.MODE_CFB, iv )\n        r = ( iv + cipher.encrypt( raw ) )\n        print "ECRYPTED STRING: " + hexlify(r)\n        return r\n\n    def decrypt( self, enc ):\n        enc = (enc)\n        iv = enc[:16]\n        cipher = AES.new(self.key, AES.MODE_CFB, iv)\n        x=(cipher.decrypt( enc ))\n        print "DECRYPTED STRING: " + hexlify(x)\n        return x\n
Run Code Online (Sandbox Code Playgroud)\n\n

我也不太清楚 python 函数的输出。我的 Go 例程运行良好。但我希望能够在 Go 中加密,在 python 中解密,反之亦然。

\n\n

Python 的输出示例:

\n\n
INIT KEY61736466617364666173646661736466\nRAW STRING: 54657374206d657373616765\nECRYPTED STRING: dfee33dd40c32fbaf9aac73ac4e0a5a9fc7bd2947d29005dd8d8e21a\ndfee33dd40c32fbaf9aac73ac4e0a5a9fc7bd2947d29005dd8d8e21a\nDECRYPTED STRING: 77d899b990d2d3172a3229b1b69c6f2554657374206d657373616765\n77d899b990d2d3172a3229b1b69c6f2554657374206d657373616765\nw\xc3\x98\xe2\x84\xa2\xc2\xb9\xef\xbf\xbd\xc3\x92\xc3\x93*2)\xc2\xb1\xc2\xb6\xc5\x93o%Test message\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,消息已被解密,但最终出现在字符串的末尾?

\n\n

编辑: \n从 GO 解密的示例输出。\n如果我尝试使用 GO 解密以下内容(用 Python 生成)

\n\n
ECRYPTED STRING: (Test Message) 7af474bc4c8ea9579d83a3353f83a0c2844f8efb019c82618ea0b478\n
Run Code Online (Sandbox Code Playgroud)\n\n

我明白了

\n\n
Decrypted Payload: 54 4E 57 9B 90 F8 D6 CD 12 59 0B B1\nDecrypted Payload: TNW\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbdY\xef\xbf\xbd\n
Run Code Online (Sandbox Code Playgroud)\n\n

奇怪的是第一个字符总是正确的

\n\n

这是两个完整的项目:

\n\n

吉图布

\n

Bli*_*ixt 5

Python 使用 8 位段,而 Go 使用 128 位段,因此第一个字符起作用但后面的字符不起作用的原因是因为每个段都依赖于前一个段,因此不同的段大小会破坏链。

我为 Python 制作了这些 URL 安全(非填充的 base64 编码)加密/解密函数,以选择性地以与 Go 相同的方式进行加密(当您指定 时block_segments=True)。

def decrypt(key, value, block_segments=False):
    # The base64 library fails if value is Unicode. Luckily, base64 is ASCII-safe.
    value = str(value)
    # We add back the padding ("=") here so that the decode won't fail.
    value = base64.b64decode(value + '=' * (4 - len(value) % 4), '-_')
    iv, value = value[:AES.block_size], value[AES.block_size:]
    if block_segments:
        # Python uses 8-bit segments by default for legacy reasons. In order to support
        # languages that encrypt using 128-bit segments, without having to use data with
        # a length divisible by 16, we need to pad and truncate the values.
        remainder = len(value) % 16
        padded_value = value + '\0' * (16 - remainder) if remainder else value
        cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
        # Return the decrypted string with the padding removed.
        return cipher.decrypt(padded_value)[:len(value)]
    return AES.new(key, AES.MODE_CFB, iv).decrypt(value)


def encrypt(key, value, block_segments=False):
    iv = Random.new().read(AES.block_size)
    if block_segments:
        # See comment in decrypt for information.
        remainder = len(value) % 16
        padded_value = value + '\0' * (16 - remainder) if remainder else value
        cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
        value = cipher.encrypt(padded_value)[:len(value)]
    else:
        value = AES.new(key, AES.MODE_CFB, iv).encrypt(value)
    # The returned value has its padding stripped to avoid query string issues.
    return base64.b64encode(iv + value, '-_').rstrip('=')
Run Code Online (Sandbox Code Playgroud)

请注意,为了安全消息传递,您需要额外的安全功能,例如防止重放攻击的随机数。

以下是 Go 的等效函数:

func Decrypt(key []byte, encrypted string) ([]byte, error) {
    ciphertext, err := base64.RawURLEncoding.DecodeString(encrypted)
    if err != nil {
        return nil, err
    }
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    if len(ciphertext) < aes.BlockSize {
        return nil, errors.New("ciphertext too short")
    }
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]
    cfb := cipher.NewCFBDecrypter(block, iv)
    cfb.XORKeyStream(ciphertext, ciphertext)
    return ciphertext, nil
}

func Encrypt(key, data []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    ciphertext := make([]byte, aes.BlockSize+len(data))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return "", err
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], data)
    return base64.RawURLEncoding.EncodeToString(ciphertext), nil
}
Run Code Online (Sandbox Code Playgroud)