将Python的AES加密算法移植到Golang中

huangapple go评论98阅读模式
英文:

Porting Python AES encryption routies to Golang

问题

我正在尝试将以下Python AES文件加密例程移植到Go语言:

def derive_key_and_iv(password, salt, key_length, iv_length):
    d = d_i = ''
    while len(d) < key_length + iv_length:
        d_i = md5(d_i + password + salt).digest()
        d += d_i
    return d[:key_length], d[key_length:key_length+iv_length]


def encrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    salt = Random.new().read(bs - len('Salted__'))
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    out_file.write('Salted__' + salt)
    finished = False
    while not finished:
        chunk = in_file.read(1024 * bs)
        if len(chunk) == 0 or len(chunk) % bs != 0:
            padding_length = (bs - len(chunk) % bs) or bs
            chunk += padding_length * chr(padding_length)
            finished = True
        out_file.write(cipher.encrypt(chunk))


def decrypt(in_file, out_file, password, key_length=32):
    bs = AES.block_size
    print(bs)
    salt = in_file.read(bs)[len('Salted__'):]
    key, iv = derive_key_and_iv(password, salt, key_length, bs)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    next_chunk = ''
    finished = False
    while not finished:
        chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
        if len(next_chunk) == 0:
            padding_length = ord(chunk[-1])
            chunk = chunk[:-padding_length]
            finished = True
        out_file.write(chunk)

我已经编写了以下Go语言例程,但是我无法使其正常工作。我正在尝试让Go语言的加密例程与调用Python和C的解密例程配合使用,所以我只关心如何使我的Go语言加密例程正常工作,但为了清晰起见,我包含了所有Python代码。

我的当前Go语言例程如下:

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"log"
	"crypto/md5"
	"os"
)

func pyEncrypt(password []byte, pathToInFile string, pathToOutFile string) {
	bs := int(aes.BlockSize)
	salt := make([]byte, aes.BlockSize-len("Salted__"))
	_, err := rand.Read(salt)
	if err != nil {
		panic(err)
	}

	key, iv := deriveKeyAndIV(password, salt, bs)
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	cfb := cipher.NewCFBEncrypter(block, iv)

	fin, err := os.Open(pathToInFile)
	if err != nil {
		panic(err)
	}
	defer fin.Close()

	fout, err := os.Create(pathToOutFile)
	if err != nil {
		panic(err)
	}
	defer fout.Close()

	_, err = fout.Write([]byte("Salted__"))
	if err != nil {
		panic(err)
	}
	_, err = fout.Write(salt)
	if err != nil {
		panic(err)
	}

	for true {
		ciphertext := make([]byte, 1024*bs)
		chunk := make([]byte, 1024*bs)
		_, err := fin.Read(chunk)
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		cfb.XORKeyStream(ciphertext, chunk)
		fout.Write(ciphertext)
	}
}

func deriveKeyAndIV(password []byte, salt []byte, bs int) ([]byte, []byte) {
	var digest []byte
	hash := md5.New()
	out := make([]byte, 32+bs) //right now I'm just matching the default key size (32) from the python script so 32 represents the default from python
	for i := 0; i < 32+bs; i += len(digest) {
		hash.Reset()
		hash.Write(digest)
		hash.Write(password)
		hash.Write(salt)
		digest = hash.Sum(digest[:0])
		copy(out[i:], digest)
	}
	return out[:32], out[32:32+bs] //matching the default key size from Python as that is what the application uses
}

func main() {
	pwd := []byte("test123")

	pyEncrypt(pwd, "/home/chris/pt.txt", "/home/chris/genc.txt")
}

目前它可以运行,生成一个看起来正确的加密文件,Python可以“解密”而不报错,但它生成的是无意义的内容,实际上并没有产生明文。Python例程可以独立工作,但我无法使我的Go语言加密例程生成Python可以解密的输出。我必须与Python的加密例程保持一致,以便与调用者兼容。你能看出我在Go语言加密例程中做错了什么吗?非常感谢你的帮助。

英文:

I am trying to port the following Python AES file encryption routines over to Go:

def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = &#39;&#39;
while len(d) &lt; key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len(&#39;Salted__&#39;))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write(&#39;Salted__&#39; + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
print(bs)
salt = in_file.read(bs)[len(&#39;Salted__&#39;):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = &#39;&#39;
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)

I have the following Go routines coded up but I'm not quite able to get it working. I am trying to get the encryption routines working in Go for callers that call the decrypt in Python and C so I'm really only interested in figuring out how to get my Golang encryption routine working but have included all the Python bits for clarity.

My current Go routines look like this:

package main
import (
&quot;crypto/aes&quot;
&quot;crypto/cipher&quot;
&quot;crypto/rand&quot;
&quot;encoding/base64&quot;
&quot;errors&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;     
&quot;crypto/md5&quot;  
&quot;os&quot;
)
func pyEncrypt(password []byte, pathToInFile string, pathToOutFile string){
bs := int(aes.BlockSize)
salt := make([]byte, aes.BlockSize - len(&quot;Salted__&quot;)) 
_, err := rand.Read(salt)
if err != nil {panic(err)}
key, iv := deriveKeyAndIV(password, salt, bs)	
block, err := aes.NewCipher(key)
if err != nil {panic(err)}
cfb := cipher.NewCFBEncrypter(block, iv)
fin, err := os.Open(pathToInFile)
if err != nil {panic(err)}
defer fin.Close()
fout, err := os.Create(pathToOutFile)
if err != nil {panic(err)}
defer fout.Close()
_, err = fout.Write([]byte(&quot;Salted__&quot;)) 
if err != nil {panic(err)}
_, err = fout.Write(salt)
if err != nil {panic(err)}
for true{
ciphertext := make([]byte, 1024 *bs)
chunk := make([]byte, 1024 * bs)
_, err := fin.Read(chunk)
if err == io.EOF{
break
}else if err != nil{
panic(err)
}
cfb.XORKeyStream(ciphertext, chunk)		
fout.Write(ciphertext)
}
}
func deriveKeyAndIV(password []byte, salt []byte, bs int) ([]byte, []byte) {
var digest []byte
hash := md5.New()
out := make([]byte, 32 + bs) //right now I&#39;m just matching the default key size (32) from the python script so 32 represents the default from python
for i := 0; i &lt; 32 + bs ; i += len(digest) {
hash.Reset()
hash.Write(digest)
hash.Write(password)
hash.Write(salt)
digest = hash.Sum(digest[:0])
copy(out[i:], digest)
}
return out[:32], out[32:32+bs] //matching the default key size from Python as that is what the application uses
}
func main() {
pwd := []byte(&quot;test123&quot;)
pyEncrypt(pwd, &quot;/home/chris/pt.txt&quot;, &quot;/home/chris/genc.txt&quot;)	
}

Right now it runs, lol, generates an encrypted file that looks right and the Python "decrypts" without error but it generates gibberish and doesn't actually produce the clear text. The Python routines work by stand-alone but I can't get my Golang encrypt producing output that the Python decrypt can decrypt. I have to match the Python encryption routine for compatibility with the callers. Do you see what I'm doing wrong in my Golang encryption routine? Thank you so much for your assistance.

答案1

得分: 0

以下是工作中的加密例程:

func pyEncrypt(password string, pathToInFile string, pathToOutFile string){
    bs := int(aes.BlockSize)
    salt := make([]byte, aes.BlockSize - len("Salted__")) 
    _, err := rand.Read(salt)
    if err != nil {panic(err)}

    key, iv := deriveKeyAndIV([]byte(password), salt, bs)    
    block, err := aes.NewCipher(key)
    if err != nil {panic(err)}
    
    cbc := cipher.NewCBCEncrypter(block, iv)

    fin, err := os.Open(pathToInFile)
    if err != nil {panic(err)}
    defer fin.Close()
    
    fout, err := os.Create(pathToOutFile)
    if err != nil {panic(err)}
    defer fout.Close()
    
    _,err = fout.Write([]byte("Salted__"))
    if err != nil {panic(err)}
    _,err = fout.Write(salt)
    if err != nil {panic(err)}
    
    for true{
        chunk := make([]byte, 1024 * bs)
                
        n,err := fin.Read(chunk)
        if err == io.EOF{
        break
        }else if err != nil{
            panic(err)
        }
        
        if n == 0 || n % bs != 0 {//need to pad up to block size :bs
            paddingLength := (bs - n % bs) 
            paddingChr := []byte(string(rune(paddingLength)))
            paddingBytes := make([]byte, paddingLength)
            
            for i := 0; i < paddingLength; i++{
                paddingBytes[i] = paddingChr[0]
            }
            
            chunk = append(chunk[0:n], []byte(paddingBytes)...)
        }else{
            chunk = chunk[0:n]//no padding needed            
        }
        
        ciphertext := make([]byte, len(chunk))
        cbc.CryptBlocks(ciphertext,chunk)
        fout.Write(ciphertext)
    }
}

希望对你有帮助!

英文:

Here is the working encryption routine:

func pyEncrypt(password string, pathToInFile string, pathToOutFile string){
bs := int(aes.BlockSize)
salt := make([]byte, aes.BlockSize - len(&quot;Salted__&quot;)) 
_, err := rand.Read(salt)
if err != nil {panic(err)}
key, iv := deriveKeyAndIV([]byte(password), salt, bs)	
block, err := aes.NewCipher(key)
if err != nil {panic(err)}
cbc := cipher.NewCBCEncrypter(block, iv)
fin, err := os.Open(pathToInFile)
if err != nil {panic(err)}
defer fin.Close()
fout, err := os.Create(pathToOutFile)
if err != nil {panic(err)}
defer fout.Close()
_,err = fout.Write([]byte(&quot;Salted__&quot;))
if err != nil {panic(err)}
_,err = fout.Write(salt)
if err != nil {panic(err)}
for true{
chunk := make([]byte, 1024 * bs)
n,err := fin.Read(chunk)
if err == io.EOF{
break
}else if err != nil{
panic(err)
}
if n == 0 || n % bs != 0 {//need to pad up to block size :bs
paddingLength := (bs - n % bs) 
paddingChr := []byte(string(rune(paddingLength)))
paddingBytes := make([]byte, paddingLength)
for i := 0; i &lt; paddingLength; i++{
paddingBytes[i] = paddingChr[0]
}
chunk = append(chunk[0:n], []byte(paddingBytes)...)
}else{
chunk = chunk[0:n]//no padding needed			
}
ciphertext := make([]byte, len(chunk))
cbc.CryptBlocks(ciphertext,chunk)
fout.Write(ciphertext)
}
}

huangapple
  • 本文由 发表于 2015年12月6日 01:00:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/34108194.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定