PBEWithMD5AndDES明文加密解密算法
前言
各项目、平台的功能往往涉及到数据库存储、读取等操作,连接数据库时需要用户名、密码等敏感信息。出于信息保护的角度,需要对这些敏感信息进行加密。
在JAVA中,通常使用加密算法PBEWithMD5AndDES,使用方式简单:实例化对象、设置固定口令,即可生成加密密文。
目前不少功能、算法开发均基于python编程,在python、JAVA联合开发的项目中,需要保证加密解密方式一致。然而,python并未直接提供调用包,因此本文对PBEWithMD5AndDES算法原理进行分析,在python中实现同样操作,保证两种编程环境下的结果统一。
一、JAVA中使用PBEWithMD5AndDES算法
在JAVA中调用PBEWithMD5AndDES算法的方式如下:
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentPBEConfig;
// 加密
public void get_Encrypt() {
// 实例化
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
// 配置加密算法和固定口令
EnvironmentPBEConfig config = new EnvironmentPBEConfig();
config.setAlgorithm("PBEWithMD5AndDES"); // 设置成PBEWithMD5AndDES算法
config.setPassword("forTest"); // 用于加密的口令,解密时也需要
encryptor.setConfig(config);
String myPwd = "123456test"; // 需要加密的明文密码
String encryptedPwd = encryptor.encrypt(myPwd); // 加密
System.out.println("加密结果为:" + encryptedPwd);
}
// 解密
public static void testPwdDecrypt() {
// 实例化
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
// 配置加密算法和固定口令
EnvironmentPBEConfig config = new EnvironmentPBEConfig();
config.setAlgorithm("PBEWithMD5AndDES"); // 设置成PBEWithMD5AndDES算法
config.setPassword("forTest"); // 用于加密的口令,同加密
String encryptedPwd = "EVjR4mo1JvBbhzIMVEuPMlgVJn43PB1R"; // 需要解密的加密密码
String myPwd = encryptor.decrypt(encryptedPwd); // 解密
System.out.println("解密结果为:" + myPwd);
}
调用上述功能的执行结果为:
加密结果为:EVjR4mo1JvBbhzIMVEuPMlgVJn43PB1R
解密结果为:123456test
二、PBEWithMD5AndDES算法分析
1. 加密过程
数据流程
PBEWithMD5AndDES算法的加密过程整体流程如下:使用PBKDF1算法得到密钥和初始化向量、通过DES加密算法对明文密码进行加密、得到的加密信息联合盐一起进行Base64编码,最终输出密文。
各算法介绍
(1) PBKDF1算法
输入:口令、系统随机生成的8字节盐、迭代次数(默认为1000次)
输出:16位输出(前8位为密钥,后8位为初始化向量IV)
过程:口令与盐的组合作为输入,使用MD5(哈希)对输入进行加密,按照指定迭代次数进行多次迭代,每一次迭代的输入为上一次MD5的结果。最终输出32位字符串,取中间的第9位到第24位的部分,得到16位长度的结果。
- MD5的算法原理可参考:MD5算法实现原理
(2) DES加密算法
输入:密钥、初始化向量IV、明文
输出:加密信息
过程:将待加密数据以64bit为单位拆分成若干数据块,然后再进行外层、内层两重迭代。外层迭代为数据块之间的迭代,使用密码分组链接模式(Cipher Block Chaining,CBC)对各数据块进行加密;内层迭代在各数据块的内部,通过Feistel网络实现。
- CBC模式加密:初始化向量与第一个明文数据块进行异或运算,结果与第二个明文数据块再进行异或,依次往后,得到密文。
- Feistel网络加密:将输入划分为左、右两侧数据,“子密钥”指的是本轮加密所使用的密钥。在Feistel网络中,每一轮都需要使用一个不同的子密钥。轮函数(典型的包括置换、代换、置换与代换的组合)的作用是根据“右侧”和子密钥生成对“左侧”加密的比特序列。每一轮加密左、右需调换一次数据。
(3) Base64编码
输入:随机生成的8字节盐+加密信息
输出:密文
过程:将原始数据按 3 字节分组(如果数据长度不是 3 的倍数,则用 0 字节填充,在最后用 = 号填补);24位的数据拆分成 4 组,每组 6 位;每 6 位的二进制值被解释为一个整数(0~63),用这个整数查找 Base64 的字符表,对应输出一个字符。Base64字符表如下:
2. 解密过程
数据流程
PBEWithMD5AndDES算法的解密过程就是将加密流程倒着走一遍:对密文进行Base64解码,从解码结果得到加密信息和盐;再利用盐、口令、迭代次数根据PBKDF1算法得到密钥和初始化向量;最后使用DES解密算法得到明文。
各算法介绍
(1) Base64解码
输入:密文
输出:解码结果,前8位为盐,剩余字节为加密的信息
过程:
- 识别出 Base64 编码的字符串中的有效字符(即大写字母 A-Z、小写字母 a-z、数字 0-9、“+”和“/”);
- 将每个有效字符转换为对应的6位二进制值(例如,“A”对应 000000,“B”对应 000001等);
- 将6位二进制值按顺序拼接起来,每8位一组,转换为对应的 ASCII 码值,从而得到原始的二进制数据;
- 如果编码字符串末尾有“=”填充字符,则在解码时忽略这些填充字符,并根据填充字符的数量确定需要丢弃的二进制位(如果末尾有一个“=”,则丢弃最后 4 个二进制位;如果末尾有两个“=”,则丢弃最后 8 个二进制位)
(2) PBKDF1算法
流程同加密
(3) DES解密算法
输入:密钥、初始化向量IV、密文
输出:明文
过程:将密文数据以64bit为单位拆分成若干数据块,然后再进行外层、内层两重迭代。外层迭代为数据块之间的迭代,使用密码分组链接模式(Cipher Block Chaining,CBC)对各数据块进行解密;内层迭代在各数据块的内部,通过Feistel网络实现。
-
CBC解密:初始化向量与第一个密文数据块进行异或运算,结果与第二个密文数据块再进行异或,依次往后,得到明文。
-
Feistel网络加密:将输入划分为左、右两侧数据,“子密钥”指的是本轮加密所使用的密钥。在Feistel网络中,每一轮都需要使用一个不同的子密钥。轮函数(典型的包括置换、代换、置换与代换的组合)的作用是根据“右侧”和子密钥生成对“左侧”加密的比特序列。每一轮加密左、右需调换一次数据。
三、Python中PBEWithMD5AndDES算法实现
1. Python加密代码
import base64
from Crypto.Cipher import DES
from Crypto.Hash import MD5
from Crypto.Protocol.KDF import PBKDF1
from Crypto.Util.Padding import pad
import os
def encrypt_pbe_with_md5_and_des(password, plaintext):
# 生成随机的8字节盐
salt = os.urandom(8)
# 使用PBKDF1生成密钥和IV
key_iv = PBKDF1(password.encode('utf-8'), salt, 16, 1000, MD5)
key = key_iv[:8]
iv = key_iv[8:16]
# 使用DES加密
cipher = DES.new(key, DES.MODE_CBC, iv=iv)
padded_data = pad(plaintext.encode('utf-8'), 8) # PKCS7填充
encrypted_data = cipher.encrypt(padded_data)
# 组合盐和加密数据,并进行Base64编码
combined = salt + encrypted_data
encrypted_b64 = base64.b64encode(combined).decode('utf-8')
return encrypted_b64
if __name__ == "__main__":
# 配置口令和明文
setPassword = "forTest" # 口令
myPwd = "123456test"
# 加密
encrypted_pwd = encrypt_pbe_with_md5_and_des(setPassword, myPwd)
print("++++++++++++++++++++++++++++++")
print(f"+ 原密码为:{myPwd}")
print(f"+ 加密后的密码为:{encrypted_pwd}")
print("++++++++++++++++++++++++++++++")
注意:由于每次执行加密时,会随机生成一个8字节盐,因此每次加密的结果都是不一样的。
2. Python解密代码
import base64
from Crypto.Cipher import DES
from Crypto.Hash import MD5
from Crypto.Protocol.KDF import PBKDF1
def dencrypt_pbe_with_md5_and_des(password, encrypted_b64):
encrypted_data = base64.b64decode(encrypted_b64)
salt = encrypted_data[:8]
ciphertext = encrypted_data[8:]
# 生成密钥和IV
key_iv = PBKDF1(password.encode('utf-8'), salt, 16, 1000, MD5)
key = key_iv[:8]
iv = key_iv[8:16]
# 解密
cipher = DES.new(key, DES.MODE_CBC, iv=iv)
decrypted = cipher.decrypt(ciphertext)
# 去除PKCS7填充
pad_length = decrypted[-1]
decrypted = decrypted[:-pad_length]
return decrypted.decode('utf-8')
if __name__ == "__main__":
setPassword = "forTest"
pending_pwd = "EVjR4mo1JvBbhzIMVEuPMlgVJn43PB1R"
my_pwd = dencrypt_pbe_with_md5_and_des(setPassword, pending_pwd)
print("++++++++++++++++++++++++++++++")
print(f"+ 加密密码为:{pending_pwd}")
print(f"+ 解密后的密码为:{my_pwd}")
print("++++++++++++++++++++++++++++++")
使用JAVA的加密结果进行检验,得到结果为123456test,与加密前的明文一致:
总结
本文对JAVA中的PBEWithMD5AndDES加密、解密算法原理进行了分析,在Python中实现了同样的算法逻辑,支持JAVA、Python作为编程语言的项目开发。