python PBEWithMD5AndDES 实现 , 在网上不好找,使用大模型生成的代码基本上都是错的, 浪费时间验证, 如下是实测有效的, 跟java程序的PBEWithMD5AndDES 实现进行比较, 输出的值是一样的, 设置了VIP可见, 如果你不是VIP, 可以参考源代码自行修改 pbe-with-md5-and-triple-des-python
因为源码有些陈旧会有些问题会报错, 下面我发布的是验证可用的, 使用中遇到啥问题, 欢迎留言.
安装依赖, 实测于版本 python 3.11
pip install pycryptodome
使用方法, 这里面,我将原函数做了修改, 输出是16进制数值, 方便存储
from pbe_with_md5_and_triple_des import PBEWithMD5AndDES, PBEWithMD5AndTripleDESif __name__ == '__main__':password = '123456'plain_text = 'admin'salt = 'RCGTeGiH'.encode("utf-8")cipher = PBEWithMD5AndDES()encrypted_text = cipher.encrypt(plain_text, password, salt)print(encrypted_text)print(cipher.decrypt(encrypted_text, password, salt))
代码如下
"""pbe_with_md5_and_triple_des~~~~~~~~~~~~This module provides ciphers that implement 'PBE With MD5 And Triple DES' and 'PBE With MD5 And DES' algorithms:copyright: (c) 2017 by Anton Koba (anton.koba@gmail.com):license: MIT"""from abc import ABC, abstractmethod
import base64
import hashlib
import array
from Crypto.Cipher import DES, DES3BLOCK_LENGTH_BYTES = 8 # pad incoming message to whole length of blockDERIVED_KEY_ITERATIONS = 1000 # cycles to hash over to produce dk and ivclass AbstractPBEWithMD5AndDES(ABC):""" Defines basic algorithm for PBE With MD5 And DES / Triple DES (DESede)DES and Triple DES versions differ in the way how the derived key (dk) andinitialization vector (iv) are generated"""# use DES3 (triple DES a.k.a. DESede) or plain DEStriple_des = Truedef __init__(self, iterations=1000):super().__init__()self.iterations = iterationsdef encrypt(self, plain_text, password, salt):"""Encrypts plain text with given password:param plain_text: plain text to decrypt:param password: password to decrypt with:return: base64-encoded encrypted text"""# pad message up to a whole block sizepadded_text = self._pad_plain_text(plain_text)# get dk and iv using proper algorithm (either for DES ot DES3), password as bytes(dk, iv) = self._get_derived_key_and_iv(password.encode('utf-8'), salt)# get proper class (DES/DES3) to instantiate and use for encodingdes_class = self._get_des_encoder_class()des = des_class.new(dk, DES.MODE_CBC, iv)# do the encryptionencrypted_text = des.encrypt(padded_text.encode('utf-8'))print(padded_text)print(encrypted_text.hex())# return encrypted text prepended with salt, all base64-encodedreturn base64.b64encode(salt + encrypted_text)def decrypt(self, encoded_text, password):"""Decrypts encoded_text with given password:param encoded_text: encoded string:param password: password to decrypt with:return: decrypted plain text as string (bytes)"""decoded_encrypted_text = base64.b64decode(encoded_text)# get first 8 bytes as saltsalt = decoded_encrypted_text[:8]# get rest of data (starting from 8th byte as messageencrypted_text_message = decoded_encrypted_text[8:]# get dk and iv using proper algorithm (either for DES ot DES3)(dk, iv) = self._get_derived_key_and_iv(password.encode('utf-8'), salt)# get proper class (DES/DES3) to instantiate and use for decodingdes_class = self._get_des_encoder_class()des = des_class.new(dk, DES.MODE_CBC, iv)# do the decryptiondecrypted_text = des.decrypt(encrypted_text_message)# return decrypted text with possible padding removed, converted from bytes string to stringreturn str(self._unpad_decrypted_message(decrypted_text), 'utf-8')def _pad_plain_text(self, plain_text):"""Pads plain text up to the whole length of block (8 bytes).We are adding chars which are equal to the number of padded bytes.i.e. 'hello' -> 'hello/x03/x03/x03':param plain_text: plain text to be padded (bytes):return: padded bytes"""pad_number = BLOCK_LENGTH_BYTES - (len(plain_text) % BLOCK_LENGTH_BYTES)result = plain_textfor i in range(pad_number):result += chr(pad_number)return resultdef _unpad_decrypted_message(self, decrypted_message):""" Decrypted message could be padded on the end, last character means number of:param decrypted_message: with PKCS7 padding:return: unpadded text"""message_length = len(decrypted_message)pad_value = decrypted_message[-1]if pad_value > 8:# no padding usedreturn decrypted_messageelse:# where real data endsposition = message_length - pad_value# padding element, repeated `pad_value` number of times, as byte stringpadding_elements = array.array('B', [pad_value] * pad_value).tostring()# check if correctly paddedif pad_value == 0 or decrypted_message[-pad_value:] != padding_elements:raise ValueError('Incorrect padding')return decrypted_message[:position]def _get_des_encoder_class(self):return DES3 if self.triple_des else DES@abstractmethoddef _get_derived_key_and_iv(self, password, salt, cycles=DERIVED_KEY_ITERATIONS):return Noneclass PBEWithMD5AndDES(AbstractPBEWithMD5AndDES):triple_des = Falsedef _get_derived_key_and_iv(self, password, salt, cycles=DERIVED_KEY_ITERATIONS):"""Returns tuple of dk(8 bytes) and iv(8 bytes) for DESLogic: concatenate password + salt and hash them given number of iterations(result of hash function is given to it an an input on following iteration):param password: password used for encryption/decryption:param salt: salt:param cycles: number of hashing iterations:return: (8 bytes dk, 8 bytes iv)"""key = password + saltfor i in range(cycles):m = hashlib.md5(key)key = m.digest()return key[:8], key[8:]class PBEWithMD5AndTripleDES(AbstractPBEWithMD5AndDES):def _get_derived_key_and_iv(self, password, salt, cycles=DERIVED_KEY_ITERATIONS):"""Returns tuple of dk(24 bytes) and iv(8 bytes) for DES3 (Triple DES, DESede)Logic:Salt will be split in two halves and processed separately.1. If 2 halves of salt are same, reverse first part2. For each half of salt:- Start hashing loop with half of salt + password (not password + salt as in DES keys)concatenate output of hash with password on each iteration- iterate for each half of salt given number of times3. Join two parts of hashes (16 + 16 bytes)4. First 24 bytes will be used as key for DES3, latest 8 bytes - iv for DES3:param password: password used for encryption/decryption:param salt: salt:param cycles: number of hashing iterations (see description):return: (24 bytes dk, 8 bytes iv)"""# reverse first half of salt if two halves are the sameif salt[:4] == salt[4:]:salt = salt[-5::-1] + salt[4:]# do part 1part1_to_hash = salt[:4]for i in range(cycles):m = hashlib.md5(part1_to_hash + password)part1_to_hash = m.digest()# do part 2part2_to_hash = salt[4:]for i in range(cycles):m = hashlib.md5(part2_to_hash + password)part2_to_hash = m.digest()result = part1_to_hash + part2_to_hash# key, ivreturn result[:24], result[24:]if __name__ == '__main__':password = '123456'plain_text = 'admin'salt = 'RCGTeGiH'.encode("utf-8")cipher = PBEWithMD5AndDES()encrypted_text = cipher.encrypt(plain_text, password, salt)print(encrypted_text)dd = cipher.decrypt(encrypted_text,password)