Python3.6 AES加密依賴包Crypto的安裝,pyinstaller打包exe報錯Cannot load native module ‘Crypto.Cipher._raw_ecb的解決方法

uiop_uiop_uiop發表於2020-12-29

所有方法都來自網際網路,我做了個全套總結

pycrypto,pycrytodome和crypto是一個東西,在很久以前,crypto在python上面的名字是pycrypto它是一個第三方庫,但是已經停止更新三年了,所以不建議安裝這個庫;但有一個延伸版pycryptodome,用法和pycrypto一模一樣的,正常用pip安裝就OK:
pip install pycryptodome

安裝完之後,去C:\Python36\Lib\site-packages資料夾(python安裝目錄的第三方庫)下找到crypto資料夾,改成Crypto,‘C’改成大寫,然後用測試程式碼驗證:我用的CFB模式,不限制明文的長度,比較簡單

 
  1. #AES-dome

  2. # -*- coding: utf-8 -*-

  3. import base64

  4. import os

  5. from Crypto.Cipher import AES

  6. '''

  7. 採用AES CFB模式 對稱加密演算法

  8. CFB模式,對待加密原文長度沒有要求

  9. '''

  10. class AES_encrypt_decrypt(object):

  11. def __init__(self, key):

  12. self.key = key.encode('utf-8')

  13. self.mode = AES.MODE_CFB

  14.  
  15. # 加密函式,採用CFB方式,text明文沒有長度限制

  16. def encrypt(self, text):

  17. text = text.encode('utf-8')

  18. #注意,這裡Python3.6下用AES傳遞key時必須要轉換成2進位制,key前面要加'b'

  19. crypto = AES.new(self.key, self.mode, b'woshigongyao6666')

  20. # 這裡金鑰key 長度必須為16(AES-128),

  21. # 24(AES-192),或者32 (AES-256)Bytes 長度

  22. # 目前AES-128 足夠目前使用

  23. self.ciphertext = crypto.encrypt(text)

  24. # 因為AES加密時候得到的字串不一定是ascii字符集的,輸出到終端或者儲存時候可能存在問題

  25. # 所以這裡統一用base64轉化為字串

  26. return str(base64.encodebytes(self.ciphertext), encoding='utf-8')

  27.  
  28. # 解密

  29. def decrypt(self, text):

  30. crypto = AES.new(self.key, self.mode, b'woshigongyao6666')

  31. plain_text = crypto.decrypt(base64.decodebytes(text.encode(encoding='utf-8')))

  32. return bytes.decode(plain_text)

  33.  
  34. if __name__ == '__main__':

  35. # 初始化金鑰

  36. sql = AES_encrypt_decrypt('woshisiyao666888')

  37.   text = 'my AES encrypt program!'

  38. e = pc.encrypt(text) # 加密

  39. d = pc.decrypt(e) # 解密

  40. print("原文:"+'\n'+text)

  41. print("加密結果:"+'\n'+ e)

  42. print("解密結果:"+'\n'+d)

 

執行上面的程式,應該可以跑出來這樣的結果:

 
  1. 原文:

  2. my AES encrypt program!

  3. 加密結果:

  4. DyMSIDSVV6zp8r2XtcPEHaWlqcNuEOA=

  5.  
  6. 解密結果:

  7. my AES encrypt program!

好,下面開始用Pyinstaller打包,但無奈報錯,經過網上一頓找,發現原因大致如下,延續開發的人漏了hook,需要手動加一個hook檔案到C:\python36\Lib\site-packages\PyInstaller\hooks,檔名是hook-Crypto.py,內容如下:

 
  1. #-----------------------------------------------------------------------------

  2. # Copyright (c) 2005-2018, PyInstaller Development Team.

  3. #

  4. # Distributed under the terms of the GNU General Public License with exception

  5. # for distributing bootloader.

  6. #

  7. # The full license is in the file COPYING.txt, distributed with this software.

  8. #-----------------------------------------------------------------------------

  9.  
  10. """

  11. Hook for PyCryptodome library: https://pypi.python.org/pypi/pycryptodome

  12.  
  13. PyCryptodome is an almost drop-in replacement for the now unmaintained

  14. PyCrypto library. The two are mutually exclusive as they live under

  15. the same package ("Crypto").

  16.  
  17. PyCryptodome distributes dynamic libraries and builds them as if they were

  18. Python C extensions (even though they are not extensions - as they can't be

  19. imported by Python). It might sound a bit weird, but this decision is rooted

  20. in PyPy and its partial and slow support for C extensions. However, this also

  21. invalidates several of the existing methods used by PyInstaller to decide the

  22. right files to pull in.

  23.  
  24. Even though this hook is meant to help with PyCryptodome only, it will be

  25. triggered also when PyCrypto is installed, so it must be tested with both.

  26.  
  27. Tested with PyCryptodome 3.5.1, PyCrypto 2.6.1, Python 2.7 & 3.6, Fedora & Windows

  28. """

  29.  
  30. import os

  31. import glob

  32.  
  33. from PyInstaller.compat import EXTENSION_SUFFIXES

  34. from PyInstaller.utils.hooks import get_module_file_attribute

  35.  
  36. # Include the modules as binaries in a subfolder named like the package.

  37. # Cryptodome's loader expects to find them inside the package directory for

  38. # the main module. We cannot use hiddenimports because that would add the

  39. # modules outside the package.

  40.  
  41. binaries = []

  42. binary_module_names = [

  43. 'Crypto.Math', # First in the list

  44. 'Crypto.Cipher',

  45. 'Crypto.Util',

  46. 'Crypto.Hash',

  47. 'Crypto.Protocol',

  48. ]

  49.  
  50. try:

  51. for module_name in binary_module_names:

  52. m_dir = os.path.dirname(get_module_file_attribute(module_name))

  53. for ext in EXTENSION_SUFFIXES:

  54. module_bin = glob.glob(os.path.join(m_dir, '_*%s' % ext))

  55. for f in module_bin:

  56. binaries.append((f, module_name.replace('.', os.sep)))

  57. except ImportError:

  58. # Do nothing for PyCrypto (Crypto.Math does not exist there)

  59. pass

儲存之後,拷貝到那個目錄,重新用Pyinstaller打包,完美生成EXE檔案!

相關文章