Python 中的双鱼加密
Twofish encryption in Python
我有一个测试工具,它使用Twofish作为加密算法,在将数据发送到服务器之前对数据进行加密。代码是用 C++ 编写的,并使用 Bruce Schneier 的优化 C 实现 (https://www.schneier.com/code/twofish-optimized-c.zip). I need to port this tool to Python and I am using twofish module (https://pypi.python.org/pypi/twofish/0.3.0)。我可以加密和解密 16 个字符长度的字符串,但对于其他字符串长度,它会给出错误 'ValueError: invalid block length'。
如何使用 Python 的 Twofish 模块加密和解密大数据?
>>> from twofish import Twofish
>>> key = binascii.unhexlify('8CACBE276491F6FF4B1EC0E9CFD52E76')
>>> t = Twofish(key)
>>> cipher_text = T.encrypt('deadbeaf12345678')
>>> plain_text = t.decrypt(cipher_text)
>>> plain_text
'deadbeaf12345678'
>>> cipher_text = t.encrypt('deadbeaf12345678hello world 1234')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/twofish.py", line 69, in encrypt
raise ValueError('invalid block length')
ValueError: invalid block length
更新:
我正在尝试针对此问题的另一种解决方案。
我从 Bruce Schneier 的优化 C 实现 (https://www.schneier.com/code/twofish-optimized-c.zip) 创建了一个 Windows DLL,twofish.dll。
此外,我还使用 __declspec(dllexport).
导出了用于编码和解码成员函数的包装函数
我正在使用 ctype.CDLL 函数在 Python 中加载此 DLL。
encode函数的原型是:
__declspec(dllexport) int encode(unsigned char *key, unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int outbuflen, unsigned int& outlen)
我应该如何在 Python 脚本中定义参数类型?
import ctypes
import binascii
import requests
twofish_dll = ctypes.CDLL("twofish.dll")
encode = twofish_dll.encode
f = open('test.xml', 'r')
plain_text = f.read()
f.close()
cipher_text = ctypes.create_string_buffer(8192)
cipher_text_lenght = (ctypes.c_uint)()
KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)
encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
执行上述代码时抛出以下错误:
Traceback (most recent call last):
File "C:\Data\sepm_test.py", line 21, in <module>
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
TypeError: bytes or integer address expected instead of str instance
Twofish 是一种分组密码,一次只能加密 16 个八位字节。郭思 the documentation:
Create a twofish.Twofish instance with a key of length ]0, 32] and then use the encrypt and decrypt methods on 16 bytes blocks.
All values must be binary strings (str on Python 2, bytes on Python 3)
[WARNING] this should be used in a sensible cipher mode, like CTR or CBC. If you don't know what this means, you should probably use a higher level library.
一定要注意这个警告,CBC or CTR is not rocket science,但是如果你天真地使用 Twofish,它的安全性就会受到严重威胁。
我最终通过将 Bruce Schneier 为 Twofish (https://www.schneier.com/code/twofish-optimized-c.zip) 优化的 C 实现编译成一个 DLL 并使用 ctypes 模块加载该 DLL 来解决这个问题。
import ctypes
import binascii
import requests
twofish_dll = ctypes.CDLL("twofish.dll")
encode = twofish_dll.encode
f = open('test.xml', 'r')
plain_text = f.read()
f.close()
plain_text_buffer = ctypes.create_string_buffer(str.encode(plain_text))
plain_text_buffer_length = (ctypes.c_uint)(len(plain_text_buffer))
cipher_text_buffer = ctypes.create_string_buffer(8192)
cipher_text_buffer_length = (ctypes.c_uint)(len(cipher_text_buffer))
cipher_text_length = ctypes.c_uint(0)
KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)
encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode.restype = ctypes.c_int
encode(ctypes.c_char_p(key), plain_text_buffer, plain_text_buffer_length, cipher_text_buffer, cipher_text_buffer_length, ctypes.pointer(cipher_text_length))
下面的答案是一种带有模块化代码的完整解决方案。我正在使用它,因为它在生产中几乎没有配置更改。
函数 - “argumentParse”:在解析参数时获取参数。该程序同时进行加密和解密。以下是如何调用代码进行加密和解密的示例。
python TwofishEncryptDecrypt.py --encrypt --filename --keypath
python TwofishEncryptDecrypt.py --decrypt --filename --keypath
现在 - Twofish 是如何工作的? Twofish 是一种加密方法,其中对大小为 16 的块进行加密,并不断连接结果,直到不再存在块为止。现在遇到文件不能被16等分的情况,那么在末尾添加一些填充符可以是space并使最后一个块为16并加密。
paddingBytesLength = self.BLOCK_SIZE - (len(fileContent) % self.BLOCK_SIZE)
对于解密,它只是相反,取块大小为 16 解密并继续连接,直到没有更多的加密块剩下。现在你可以 trim space 从头开始,因为在加密过程中可能使用了一些填充。
编码愉快。
'''
The encryption algo is "Twofish Encryption"
'''
import sys
from twofish import Twofish
import os
def usages():
print('-------Usgaes of TwoFish encrypt/decrypt script------------')
print('')
print(' --help or -h to get help of the script')
print(' --encrypt or -e to encrypt a file')
print(' --decrypt or -d to decrypt a file')
print(' --filename or -f filename to be encrypted or decrypted')
print(' --keypath or -k filename to pass keyfile name')
print('')
print('python TwofishEncryptDecrypt.py --encrypt --filename <FileName> --keypath <KeyFilename>')
print('python TwofishEncryptDecrypt.py --decrypt --filename <FileName> --keypath <KeyFilename>')
print('')
print('-----------------------------End---------------------------')
def argumentParse(argsList):
if '--help' in argsList or '-h' in argsList:
usages()
sys.exit(0)
elif ('-e' in argsList and '-d' in argsList) or ('--encrypt' in argsList and '--decrypt' in argsList):
print('Both action not allowed together')
usages()
sys.exit(0)
i = 0
for item in argsList:
if item == '-e' or item == '--encrypt':
arguments.update({'action': 'encryption'})
elif item == '-d' or item == '--decrypt':
arguments.update({'action': 'decryption'})
elif item == '-f' or item == '--filename':
if os.path.exists(argsList[i+1]) and os.path.getsize(argsList[i+1])>0:
arguments.update({'filename': argsList[i+1]})
else:
print("[No such file or Directory] or [File Might Be empty] :",argsList[i+1])
pass
elif item == '-k' or item == '--keypath':
arguments.update({'keypath': argsList[i+1]})
else:
pass
i+=1
return arguments
class encryptDecrypt():
BLOCK_SIZE = 16
twofish_passphrase = ''
def encryptTwofish(self, filename):
fileContent = open(filename,'r').read()
paddingBytesLength = self.BLOCK_SIZE - (len(fileContent) % self.BLOCK_SIZE)
paddingBytes = ''
for i in range(paddingBytesLength):
#paddingBytes += '00'
paddingBytes += ' '
#fileContent = fileContent + bytearray.fromhex(paddingBytes).decode("utf-8")
fileContent = fileContent.decode('utf-8') + paddingBytes
iteration_count = int(len(fileContent) / self.BLOCK_SIZE)
encryptedFileContent = ''.encode()
for i in range(iteration_count):
encryptedFileContent += self.T.encrypt(fileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE].encode())
return encryptedFileContent
def decryptTwofish(self, filename):
decryptedFileContent = ''
encryptedFileContent = open(filename,'rb').read()
iteration_count = int(len(encryptedFileContent) / self.BLOCK_SIZE)
for i in range(iteration_count):
decryptedFileContent += self.T.decrypt(encryptedFileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE]).decode()
return decryptedFileContent.strip()
def readKey(self, path):
keyValue = open(path,'r')
for line in keyValue:
self.twofish_passphrase=line.rstrip('\n')
self.T = Twofish(self.twofish_passphrase)
# entry point
def writeFile(self, filename, fileContent):
resultFile = open(filename,'wb')
resultFile.write(fileContent)
resultFile.close()
if __name__ == "__main__":
arguments = {}
argsList = sys.argv
arguments = argumentParse(argsList)
if not 'keypath' in arguments or not 'action' in arguments or not 'filename' in arguments:
usages()
sys.exit()
twofish_encrypt = encryptDecrypt()
twofish_encrypt.readKey(arguments['keypath'])
if (arguments['action'] == 'encryption'):
twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.encryptTwofish(arguments['filename']))
elif (arguments['action'] == 'decryption'):
twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.decryptTwofish(arguments['filename']))
# Remove ending NULL character from decrypted string
我有一个测试工具,它使用Twofish作为加密算法,在将数据发送到服务器之前对数据进行加密。代码是用 C++ 编写的,并使用 Bruce Schneier 的优化 C 实现 (https://www.schneier.com/code/twofish-optimized-c.zip). I need to port this tool to Python and I am using twofish module (https://pypi.python.org/pypi/twofish/0.3.0)。我可以加密和解密 16 个字符长度的字符串,但对于其他字符串长度,它会给出错误 'ValueError: invalid block length'。
如何使用 Python 的 Twofish 模块加密和解密大数据?
>>> from twofish import Twofish
>>> key = binascii.unhexlify('8CACBE276491F6FF4B1EC0E9CFD52E76')
>>> t = Twofish(key)
>>> cipher_text = T.encrypt('deadbeaf12345678')
>>> plain_text = t.decrypt(cipher_text)
>>> plain_text
'deadbeaf12345678'
>>> cipher_text = t.encrypt('deadbeaf12345678hello world 1234')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/twofish.py", line 69, in encrypt
raise ValueError('invalid block length')
ValueError: invalid block length
更新: 我正在尝试针对此问题的另一种解决方案。 我从 Bruce Schneier 的优化 C 实现 (https://www.schneier.com/code/twofish-optimized-c.zip) 创建了一个 Windows DLL,twofish.dll。 此外,我还使用 __declspec(dllexport).
导出了用于编码和解码成员函数的包装函数我正在使用 ctype.CDLL 函数在 Python 中加载此 DLL。 encode函数的原型是:
__declspec(dllexport) int encode(unsigned char *key, unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int outbuflen, unsigned int& outlen)
我应该如何在 Python 脚本中定义参数类型?
import ctypes
import binascii
import requests
twofish_dll = ctypes.CDLL("twofish.dll")
encode = twofish_dll.encode
f = open('test.xml', 'r')
plain_text = f.read()
f.close()
cipher_text = ctypes.create_string_buffer(8192)
cipher_text_lenght = (ctypes.c_uint)()
KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)
encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
执行上述代码时抛出以下错误:
Traceback (most recent call last):
File "C:\Data\sepm_test.py", line 21, in <module>
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
TypeError: bytes or integer address expected instead of str instance
Twofish 是一种分组密码,一次只能加密 16 个八位字节。郭思 the documentation:
Create a twofish.Twofish instance with a key of length ]0, 32] and then use the encrypt and decrypt methods on 16 bytes blocks.
All values must be binary strings (str on Python 2, bytes on Python 3)
[WARNING] this should be used in a sensible cipher mode, like CTR or CBC. If you don't know what this means, you should probably use a higher level library.
一定要注意这个警告,CBC or CTR is not rocket science,但是如果你天真地使用 Twofish,它的安全性就会受到严重威胁。
我最终通过将 Bruce Schneier 为 Twofish (https://www.schneier.com/code/twofish-optimized-c.zip) 优化的 C 实现编译成一个 DLL 并使用 ctypes 模块加载该 DLL 来解决这个问题。
import ctypes
import binascii
import requests
twofish_dll = ctypes.CDLL("twofish.dll")
encode = twofish_dll.encode
f = open('test.xml', 'r')
plain_text = f.read()
f.close()
plain_text_buffer = ctypes.create_string_buffer(str.encode(plain_text))
plain_text_buffer_length = (ctypes.c_uint)(len(plain_text_buffer))
cipher_text_buffer = ctypes.create_string_buffer(8192)
cipher_text_buffer_length = (ctypes.c_uint)(len(cipher_text_buffer))
cipher_text_length = ctypes.c_uint(0)
KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)
encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode.restype = ctypes.c_int
encode(ctypes.c_char_p(key), plain_text_buffer, plain_text_buffer_length, cipher_text_buffer, cipher_text_buffer_length, ctypes.pointer(cipher_text_length))
下面的答案是一种带有模块化代码的完整解决方案。我正在使用它,因为它在生产中几乎没有配置更改。
函数 - “argumentParse”:在解析参数时获取参数。该程序同时进行加密和解密。以下是如何调用代码进行加密和解密的示例。
python TwofishEncryptDecrypt.py --encrypt --filename --keypath python TwofishEncryptDecrypt.py --decrypt --filename --keypath
现在 - Twofish 是如何工作的? Twofish 是一种加密方法,其中对大小为 16 的块进行加密,并不断连接结果,直到不再存在块为止。现在遇到文件不能被16等分的情况,那么在末尾添加一些填充符可以是space并使最后一个块为16并加密。
paddingBytesLength = self.BLOCK_SIZE - (len(fileContent) % self.BLOCK_SIZE)
对于解密,它只是相反,取块大小为 16 解密并继续连接,直到没有更多的加密块剩下。现在你可以 trim space 从头开始,因为在加密过程中可能使用了一些填充。
编码愉快。
'''
The encryption algo is "Twofish Encryption"
'''
import sys
from twofish import Twofish
import os
def usages():
print('-------Usgaes of TwoFish encrypt/decrypt script------------')
print('')
print(' --help or -h to get help of the script')
print(' --encrypt or -e to encrypt a file')
print(' --decrypt or -d to decrypt a file')
print(' --filename or -f filename to be encrypted or decrypted')
print(' --keypath or -k filename to pass keyfile name')
print('')
print('python TwofishEncryptDecrypt.py --encrypt --filename <FileName> --keypath <KeyFilename>')
print('python TwofishEncryptDecrypt.py --decrypt --filename <FileName> --keypath <KeyFilename>')
print('')
print('-----------------------------End---------------------------')
def argumentParse(argsList):
if '--help' in argsList or '-h' in argsList:
usages()
sys.exit(0)
elif ('-e' in argsList and '-d' in argsList) or ('--encrypt' in argsList and '--decrypt' in argsList):
print('Both action not allowed together')
usages()
sys.exit(0)
i = 0
for item in argsList:
if item == '-e' or item == '--encrypt':
arguments.update({'action': 'encryption'})
elif item == '-d' or item == '--decrypt':
arguments.update({'action': 'decryption'})
elif item == '-f' or item == '--filename':
if os.path.exists(argsList[i+1]) and os.path.getsize(argsList[i+1])>0:
arguments.update({'filename': argsList[i+1]})
else:
print("[No such file or Directory] or [File Might Be empty] :",argsList[i+1])
pass
elif item == '-k' or item == '--keypath':
arguments.update({'keypath': argsList[i+1]})
else:
pass
i+=1
return arguments
class encryptDecrypt():
BLOCK_SIZE = 16
twofish_passphrase = ''
def encryptTwofish(self, filename):
fileContent = open(filename,'r').read()
paddingBytesLength = self.BLOCK_SIZE - (len(fileContent) % self.BLOCK_SIZE)
paddingBytes = ''
for i in range(paddingBytesLength):
#paddingBytes += '00'
paddingBytes += ' '
#fileContent = fileContent + bytearray.fromhex(paddingBytes).decode("utf-8")
fileContent = fileContent.decode('utf-8') + paddingBytes
iteration_count = int(len(fileContent) / self.BLOCK_SIZE)
encryptedFileContent = ''.encode()
for i in range(iteration_count):
encryptedFileContent += self.T.encrypt(fileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE].encode())
return encryptedFileContent
def decryptTwofish(self, filename):
decryptedFileContent = ''
encryptedFileContent = open(filename,'rb').read()
iteration_count = int(len(encryptedFileContent) / self.BLOCK_SIZE)
for i in range(iteration_count):
decryptedFileContent += self.T.decrypt(encryptedFileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE]).decode()
return decryptedFileContent.strip()
def readKey(self, path):
keyValue = open(path,'r')
for line in keyValue:
self.twofish_passphrase=line.rstrip('\n')
self.T = Twofish(self.twofish_passphrase)
# entry point
def writeFile(self, filename, fileContent):
resultFile = open(filename,'wb')
resultFile.write(fileContent)
resultFile.close()
if __name__ == "__main__":
arguments = {}
argsList = sys.argv
arguments = argumentParse(argsList)
if not 'keypath' in arguments or not 'action' in arguments or not 'filename' in arguments:
usages()
sys.exit()
twofish_encrypt = encryptDecrypt()
twofish_encrypt.readKey(arguments['keypath'])
if (arguments['action'] == 'encryption'):
twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.encryptTwofish(arguments['filename']))
elif (arguments['action'] == 'decryption'):
twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.decryptTwofish(arguments['filename']))
# Remove ending NULL character from decrypted string