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

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

Porting Python AES encryption routies to Golang

问题

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

  1. def derive_key_and_iv(password, salt, key_length, iv_length):
  2. d = d_i = ''
  3. while len(d) < key_length + iv_length:
  4. d_i = md5(d_i + password + salt).digest()
  5. d += d_i
  6. return d[:key_length], d[key_length:key_length+iv_length]
  7. def encrypt(in_file, out_file, password, key_length=32):
  8. bs = AES.block_size
  9. salt = Random.new().read(bs - len('Salted__'))
  10. key, iv = derive_key_and_iv(password, salt, key_length, bs)
  11. cipher = AES.new(key, AES.MODE_CBC, iv)
  12. out_file.write('Salted__' + salt)
  13. finished = False
  14. while not finished:
  15. chunk = in_file.read(1024 * bs)
  16. if len(chunk) == 0 or len(chunk) % bs != 0:
  17. padding_length = (bs - len(chunk) % bs) or bs
  18. chunk += padding_length * chr(padding_length)
  19. finished = True
  20. out_file.write(cipher.encrypt(chunk))
  21. def decrypt(in_file, out_file, password, key_length=32):
  22. bs = AES.block_size
  23. print(bs)
  24. salt = in_file.read(bs)[len('Salted__'):]
  25. key, iv = derive_key_and_iv(password, salt, key_length, bs)
  26. cipher = AES.new(key, AES.MODE_CBC, iv)
  27. next_chunk = ''
  28. finished = False
  29. while not finished:
  30. chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
  31. if len(next_chunk) == 0:
  32. padding_length = ord(chunk[-1])
  33. chunk = chunk[:-padding_length]
  34. finished = True
  35. out_file.write(chunk)

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

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

  1. package main
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/rand"
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "log"
  11. "crypto/md5"
  12. "os"
  13. )
  14. func pyEncrypt(password []byte, pathToInFile string, pathToOutFile string) {
  15. bs := int(aes.BlockSize)
  16. salt := make([]byte, aes.BlockSize-len("Salted__"))
  17. _, err := rand.Read(salt)
  18. if err != nil {
  19. panic(err)
  20. }
  21. key, iv := deriveKeyAndIV(password, salt, bs)
  22. block, err := aes.NewCipher(key)
  23. if err != nil {
  24. panic(err)
  25. }
  26. cfb := cipher.NewCFBEncrypter(block, iv)
  27. fin, err := os.Open(pathToInFile)
  28. if err != nil {
  29. panic(err)
  30. }
  31. defer fin.Close()
  32. fout, err := os.Create(pathToOutFile)
  33. if err != nil {
  34. panic(err)
  35. }
  36. defer fout.Close()
  37. _, err = fout.Write([]byte("Salted__"))
  38. if err != nil {
  39. panic(err)
  40. }
  41. _, err = fout.Write(salt)
  42. if err != nil {
  43. panic(err)
  44. }
  45. for true {
  46. ciphertext := make([]byte, 1024*bs)
  47. chunk := make([]byte, 1024*bs)
  48. _, err := fin.Read(chunk)
  49. if err == io.EOF {
  50. break
  51. } else if err != nil {
  52. panic(err)
  53. }
  54. cfb.XORKeyStream(ciphertext, chunk)
  55. fout.Write(ciphertext)
  56. }
  57. }
  58. func deriveKeyAndIV(password []byte, salt []byte, bs int) ([]byte, []byte) {
  59. var digest []byte
  60. hash := md5.New()
  61. 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
  62. for i := 0; i < 32+bs; i += len(digest) {
  63. hash.Reset()
  64. hash.Write(digest)
  65. hash.Write(password)
  66. hash.Write(salt)
  67. digest = hash.Sum(digest[:0])
  68. copy(out[i:], digest)
  69. }
  70. return out[:32], out[32:32+bs] //matching the default key size from Python as that is what the application uses
  71. }
  72. func main() {
  73. pwd := []byte("test123")
  74. pyEncrypt(pwd, "/home/chris/pt.txt", "/home/chris/genc.txt")
  75. }

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

英文:

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

  1. def derive_key_and_iv(password, salt, key_length, iv_length):
  2. d = d_i = &#39;&#39;
  3. while len(d) &lt; key_length + iv_length:
  4. d_i = md5(d_i + password + salt).digest()
  5. d += d_i
  6. return d[:key_length], d[key_length:key_length+iv_length]
  7. def encrypt(in_file, out_file, password, key_length=32):
  8. bs = AES.block_size
  9. salt = Random.new().read(bs - len(&#39;Salted__&#39;))
  10. key, iv = derive_key_and_iv(password, salt, key_length, bs)
  11. cipher = AES.new(key, AES.MODE_CBC, iv)
  12. out_file.write(&#39;Salted__&#39; + salt)
  13. finished = False
  14. while not finished:
  15. chunk = in_file.read(1024 * bs)
  16. if len(chunk) == 0 or len(chunk) % bs != 0:
  17. padding_length = (bs - len(chunk) % bs) or bs
  18. chunk += padding_length * chr(padding_length)
  19. finished = True
  20. out_file.write(cipher.encrypt(chunk))
  21. def decrypt(in_file, out_file, password, key_length=32):
  22. bs = AES.block_size
  23. print(bs)
  24. salt = in_file.read(bs)[len(&#39;Salted__&#39;):]
  25. key, iv = derive_key_and_iv(password, salt, key_length, bs)
  26. cipher = AES.new(key, AES.MODE_CBC, iv)
  27. next_chunk = &#39;&#39;
  28. finished = False
  29. while not finished:
  30. chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
  31. if len(next_chunk) == 0:
  32. padding_length = ord(chunk[-1])
  33. chunk = chunk[:-padding_length]
  34. finished = True
  35. 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:

  1. package main
  2. import (
  3. &quot;crypto/aes&quot;
  4. &quot;crypto/cipher&quot;
  5. &quot;crypto/rand&quot;
  6. &quot;encoding/base64&quot;
  7. &quot;errors&quot;
  8. &quot;fmt&quot;
  9. &quot;io&quot;
  10. &quot;log&quot;
  11. &quot;crypto/md5&quot;
  12. &quot;os&quot;
  13. )
  14. func pyEncrypt(password []byte, pathToInFile string, pathToOutFile string){
  15. bs := int(aes.BlockSize)
  16. salt := make([]byte, aes.BlockSize - len(&quot;Salted__&quot;))
  17. _, err := rand.Read(salt)
  18. if err != nil {panic(err)}
  19. key, iv := deriveKeyAndIV(password, salt, bs)
  20. block, err := aes.NewCipher(key)
  21. if err != nil {panic(err)}
  22. cfb := cipher.NewCFBEncrypter(block, iv)
  23. fin, err := os.Open(pathToInFile)
  24. if err != nil {panic(err)}
  25. defer fin.Close()
  26. fout, err := os.Create(pathToOutFile)
  27. if err != nil {panic(err)}
  28. defer fout.Close()
  29. _, err = fout.Write([]byte(&quot;Salted__&quot;))
  30. if err != nil {panic(err)}
  31. _, err = fout.Write(salt)
  32. if err != nil {panic(err)}
  33. for true{
  34. ciphertext := make([]byte, 1024 *bs)
  35. chunk := make([]byte, 1024 * bs)
  36. _, err := fin.Read(chunk)
  37. if err == io.EOF{
  38. break
  39. }else if err != nil{
  40. panic(err)
  41. }
  42. cfb.XORKeyStream(ciphertext, chunk)
  43. fout.Write(ciphertext)
  44. }
  45. }
  46. func deriveKeyAndIV(password []byte, salt []byte, bs int) ([]byte, []byte) {
  47. var digest []byte
  48. hash := md5.New()
  49. 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
  50. for i := 0; i &lt; 32 + bs ; i += len(digest) {
  51. hash.Reset()
  52. hash.Write(digest)
  53. hash.Write(password)
  54. hash.Write(salt)
  55. digest = hash.Sum(digest[:0])
  56. copy(out[i:], digest)
  57. }
  58. return out[:32], out[32:32+bs] //matching the default key size from Python as that is what the application uses
  59. }
  60. func main() {
  61. pwd := []byte(&quot;test123&quot;)
  62. pyEncrypt(pwd, &quot;/home/chris/pt.txt&quot;, &quot;/home/chris/genc.txt&quot;)
  63. }

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

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

  1. func pyEncrypt(password string, pathToInFile string, pathToOutFile string){
  2. bs := int(aes.BlockSize)
  3. salt := make([]byte, aes.BlockSize - len("Salted__"))
  4. _, err := rand.Read(salt)
  5. if err != nil {panic(err)}
  6. key, iv := deriveKeyAndIV([]byte(password), salt, bs)
  7. block, err := aes.NewCipher(key)
  8. if err != nil {panic(err)}
  9. cbc := cipher.NewCBCEncrypter(block, iv)
  10. fin, err := os.Open(pathToInFile)
  11. if err != nil {panic(err)}
  12. defer fin.Close()
  13. fout, err := os.Create(pathToOutFile)
  14. if err != nil {panic(err)}
  15. defer fout.Close()
  16. _,err = fout.Write([]byte("Salted__"))
  17. if err != nil {panic(err)}
  18. _,err = fout.Write(salt)
  19. if err != nil {panic(err)}
  20. for true{
  21. chunk := make([]byte, 1024 * bs)
  22. n,err := fin.Read(chunk)
  23. if err == io.EOF{
  24. break
  25. }else if err != nil{
  26. panic(err)
  27. }
  28. if n == 0 || n % bs != 0 {//need to pad up to block size :bs
  29. paddingLength := (bs - n % bs)
  30. paddingChr := []byte(string(rune(paddingLength)))
  31. paddingBytes := make([]byte, paddingLength)
  32. for i := 0; i < paddingLength; i++{
  33. paddingBytes[i] = paddingChr[0]
  34. }
  35. chunk = append(chunk[0:n], []byte(paddingBytes)...)
  36. }else{
  37. chunk = chunk[0:n]//no padding needed
  38. }
  39. ciphertext := make([]byte, len(chunk))
  40. cbc.CryptBlocks(ciphertext,chunk)
  41. fout.Write(ciphertext)
  42. }
  43. }

希望对你有帮助!

英文:

Here is the working encryption routine:

  1. func pyEncrypt(password string, pathToInFile string, pathToOutFile string){
  2. bs := int(aes.BlockSize)
  3. salt := make([]byte, aes.BlockSize - len(&quot;Salted__&quot;))
  4. _, err := rand.Read(salt)
  5. if err != nil {panic(err)}
  6. key, iv := deriveKeyAndIV([]byte(password), salt, bs)
  7. block, err := aes.NewCipher(key)
  8. if err != nil {panic(err)}
  9. cbc := cipher.NewCBCEncrypter(block, iv)
  10. fin, err := os.Open(pathToInFile)
  11. if err != nil {panic(err)}
  12. defer fin.Close()
  13. fout, err := os.Create(pathToOutFile)
  14. if err != nil {panic(err)}
  15. defer fout.Close()
  16. _,err = fout.Write([]byte(&quot;Salted__&quot;))
  17. if err != nil {panic(err)}
  18. _,err = fout.Write(salt)
  19. if err != nil {panic(err)}
  20. for true{
  21. chunk := make([]byte, 1024 * bs)
  22. n,err := fin.Read(chunk)
  23. if err == io.EOF{
  24. break
  25. }else if err != nil{
  26. panic(err)
  27. }
  28. if n == 0 || n % bs != 0 {//need to pad up to block size :bs
  29. paddingLength := (bs - n % bs)
  30. paddingChr := []byte(string(rune(paddingLength)))
  31. paddingBytes := make([]byte, paddingLength)
  32. for i := 0; i &lt; paddingLength; i++{
  33. paddingBytes[i] = paddingChr[0]
  34. }
  35. chunk = append(chunk[0:n], []byte(paddingBytes)...)
  36. }else{
  37. chunk = chunk[0:n]//no padding needed
  38. }
  39. ciphertext := make([]byte, len(chunk))
  40. cbc.CryptBlocks(ciphertext,chunk)
  41. fout.Write(ciphertext)
  42. }
  43. }

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:

确定