Screaming Loud

日々是精進

PythonでCFBを使って暗号化したものがGoで復号できない

Goには標準パッケージにcipherがあり、それを用いると暗号化復号化を行うことができる

例えば暗号化、復号化は以下のように定義することで実行できます。

import (
    "crypto/cipher"
    "crypto/des"
) 

func TripleDesEncrypt(data, key, iv []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    encrypted := make([]byte, len(data))
    stream.XORKeyStream(encrypted, data)
    return encrypted, nil
}

func TripleDesDecrypt(data, key, iv []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }

    stream := cipher.NewCFBDecrypter(block, iv)
    decrypted := make([]byte, len(data))
    stream.XORKeyStream(decrypted, data)
    return decrypted, nil
}

The Go Playground で動かせるようにしておきました。

ただユースケースとして、他言語で暗号化しGoで復号化させるという場合もよくあると思います。

Pythonで暗号化する際にpycryptoを使うと以下で簡単に暗号化できます。 暗号化後はbyte列になってしまうので、base64エンコードしておきます。

from Crypto.Cipher import DES3
from Crypto import Random
import base64

key = "123456789123456789123456"
iv = "12345678"
cipher_encrypt = DES3.new(key, DES3.MODE_CFB, iv)
plaintext = "Hello World"
encrypted_text = cipher_encrypt.encrypt(plaintext)
b64_text = base64.b64encode(encrypted_text)
print("encrypt:",encrypted_text.hex()," base64 encoded: ",b64_text)

ここで暗号化した文字列を先程のGoの実装に食わせると復号化されません。 これはPythonが8bit segmentを利用しているからなようで、Goはそこに対応していません。

そこで、Goで復号化するためには以下のようにDecryptのメソッドを変更する必要があります。

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]
        }
    }
}

func newCFB8Decrypter(block cipher.Block, iv []byte) 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:   true,
    }
    copy(x.in, iv)

    return x
}

これで復号化ができます。 動くコードはこちらに置いておきます。

The Go Playground

参考URL

stackoverflow.com