java AES/ECB/PKCS5padding 加密 转换Python
注意:加密方式128位AES加密模式为ECB进行加密,加密后使用Base64转码。
最近和java项目对接遇到AES加密算法,java代码有SecureRandom.getInstance("SHA1PRNG"); 转换成Python
AES加密
高级加密标准(Advanced Encryption Standard: AES)是美国国家标准与技术研究院(NIST)在2001年建立了电子数据的加密规范。
其是对称加解密算法的最经典算法之一,它是一种分组加密标准,每个加密块大小为128位,允许的密钥长度为128、192和256位。这里只介绍ECB加密模式。
AES加密模式:ECB/CBC/CTR/OFB/CFB
填充:pkcs5padding/pkcs7padding/zeropadding/iso10126/ansix923
数据块:128位/192位/256位
ECB模式
ECB模式作为最简单的工作模式,直接将明文分组,每组分别加密,每个分组独立且前后文无关
Java 默认加密demo
Java默认AES加密模式是"AES/ECB/PKCS5Padding"。
java代码展示
package com.qzd.hit.util;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Demo {
/*** main
* @author qizai
* data: params
* encStr: Here, the length of the key must be a multiple of 16
*/
public static void main(String[] args) throws Exception {
// Encrypt Data
String key = "ROcb6JybnKILLOlO"; // Secret Key length must be (16 24 32)
String data = "[\"123456789\"]"; // Content to be encrypted
String encStr = encrypt(data, key); // Content to be decrypted
System.out.println(encStr); //Encrypted data: w6oijM0xddQrItnH3UybLQ==
// Decrypt Data
String decStr = decrypt(encStr, key);
System.out.println(decStr); //Decrypted data: ["123456789"]
}
public static String encrypt(String content, String password) {
if (StringUtils.isEmpty(content) || StringUtils.isEmpty(password)) {
System.out.println("AES encryption params is null");
return null;
}
try {
// Create cipher
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
byte[] byteContent = content.getBytes("UTF-8");
// Initialize as cryptographic cipher
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));
byte[] encryptByte = cipher.doFinal(byteContent);
return org.apache.commons.codec.binary.Base64.encodeBase64String(encryptByte);
} catch (Exception e) {
System.out.println("AES encryption operation has exception,content:{},password:{}", content, password, e);
}
return null;
}
public static String decrypt(String encryptContent, String password) throws Exception {
if (StringUtils.isEmpty(encryptContent) || StringUtils.isEmpty(password)) {
System.out.println("AES The request parameter is null");
return null;
}
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// Set to decryption mode
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
// Perform decryption operation
byte[] result = cipher.doFinal(org.apache.commons.codec.binary.Base64.decodeBase64(encryptContent));
return new String(result, "UTF-8");
}
private static SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException {
// Generates the generator for the specified algorithm key
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
keyGenerator.init(128, random);
// Grnerate Key
SecretKey secretKey = keyGenerator.generateKey();
// Key converted to AES
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
}
加密注意点:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // Generates the generator for the specified algorithm key
SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); // SHA1PRNG Random number algorithm
random.setSeed(password.getBytes());
keyGenerator.init(128, random);
SecretKey secretKey = keyGenerator.generateKey();
Python 实现AES/ECB/PKCS5padding
安装AES相关库(window下)
pip install pycryptodome
Linux下安装
pip install pycrypto
相关代码实现步骤
import base64
import hashlib
import requests
from Crypto.Cipher import AES as _AES
class AES:
def __init__(self, key: str):
"""Init aes object used by encrypt or decrypt.
AES/ECB/PKCS5Padding same as aes in java default.
"""
self.aes = _AES.new(self.get_sha1prng_key(key), _AES.MODE_ECB)
@staticmethod
def get_sha1prng_key(key: str) -> bytes:
"""encrypt key with SHA1PRNG.
same as java AES crypto key generator SHA1PRNG.
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(decryptKey.getBytes());
keygen.init(128, secureRandom);
:param string key: original key.
:return bytes: encrypt key with SHA1PRNG, 128 bits or 16 long bytes.
"""
signature: bytes = hashlib.sha1(key.encode()).digest()
signature: bytes = hashlib.sha1(signature).digest()
return signature[:16]
@staticmethod
def padding(s: str) -> str:
"""Padding PKCS5"""
pad_num: int = 16 - len(s) % 16
return s + pad_num * chr(pad_num)
@staticmethod
def unpadding(s):
"""Unpadding PKCS5"""
padding_num: int = ord(s[-1])
return s[: -padding_num]
def encrypt_to_bytes(self, content_str):
"""From string encrypt to bytes ciphertext.
"""
content_bytes = self.padding(content_str).encode()
ciphertext_bytes = self.aes.encrypt(content_bytes)
return ciphertext_bytes
def encrypt_to_base64(self, content_str):
"""From string encrypt to base64 ciphertext.
"""
ciphertext_bytes = self.encrypt_to_bytes(content_str)
ciphertext_bs64 = base64.b64encode(ciphertext_bytes).decode()
return ciphertext_bs64
def decrypt_from_bytes(self, ciphertext_bytes):
"""From bytes ciphertext decrypt to string.
"""
content_bytes = self.aes.decrypt(ciphertext_bytes)
content_str = self.unpadding(content_bytes.decode())
return content_str
def decrypt_from_base64(self, ciphertext_bs64):
"""From base64 ciphertext decrypt to string.
"""
ciphertext_bytes = base64.b64decode(ciphertext_bs64)
content_str = self.decrypt_from_bytes(ciphertext_bytes)
return content_str
def encrypt_to_bytes(content_str, encrypt_key: str):
"""From string encrypt to bytes ciphertext.
"""
aes: AES = AES(encrypt_key)
ciphertext_bytes = aes.encrypt_to_bytes(content_str)
return ciphertext_bytes
def encrypt_to_base64(content_str, encrypt_key: str) -> str:
"""From string encrypt to base64 ciphertext.
"""
aes: AES = AES(encrypt_key)
ciphertext_bs64 = aes.encrypt_to_base64(content_str)
return ciphertext_bs64
def decrypt_from_bytes(ciphertext_bytes, decrypt_key: str) -> str:
"""From bytes ciphertext decrypt to string.
"""
aes: AES = AES(decrypt_key)
content_str = aes.decrypt_from_bytes(ciphertext_bytes)
return content_str
def decrypt_from_base64(ciphertext_bs64, decrypt_key: str) -> str:
"""From base64 ciphertext decrypt to string.
"""
aes: AES = AES(decrypt_key)
content_str = aes.decrypt_from_base64(ciphertext_bs64)
return content_str
if __name__ == "__main__":
key = "iC6qfFyiza8aq"
encrypt_str_data = "[\"13811111111\"]"
et = encrypt_to_base64(encrypt_str_data, key)
print("加密后数据: ", et)
ret = decrypt_from_base64(et, key)
print("解密后数据: ", ret)
关键代码
该方法实现了对key的转换,java中关键加密代码的内容,在这里返回为16进制字符串。
@staticmethod
def get_sha1prng_key(key: str) -> bytes:
"""encrypt key with SHA1PRNG.
same as java AES crypto key generator SHA1PRNG.
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
secureRandom.setSeed(decryptKey.getBytes());
keygen.init(128, secureRandom);
:param string key: original key.
:return bytes: encrypt key with SHA1PRNG, 128 bits or 16 long bytes.
"""
signature: bytes = hashlib.sha1(key.encode()).digest()
signature: bytes = hashlib.sha1(signature).digest()
return signature[:16]
简约版
import base64
import hashlib
from Crypto.Cipher import AES
SECRET_KEY = 'iC6qfFyiza8aq' # 密钥
class AesEncrypt(object):
def __init__(self):
self.key = self.getKey(SECRET_KEY)
self.mode = AES.MODE_ECB
def pading(self, text):
"""对加密字符的处理"""
return text + (len(self.key) - len(text) % len(self.key)) * chr(len(self.key) - len(text) % len(self.key))
def unpading(self, text):
"""对解密字符的处理"""
return text[0:-ord(text[-1:])]
def getKey(self, key):
"""对key的处理,key 的长度 16,24,32"""
signature: bytes = hashlib.sha1(key.encode()).digest()
signature: bytes = hashlib.sha1(signature).digest()
return signature[:16]
def encrypt(self, text):
"""加密函数"""
ecb_encrypt = AES.new(self.key, self.mode) # ECB 模式
ecb_text = ecb_encrypt.encrypt(bytes(self.pading(text), encoding="utf8"))
encrypt_string = base64.b64encode(ecb_text).decode()
return encrypt_string
def decrypt(self, text):
"""解密函数"""
decode = base64.b64decode(text)
ecb_decrypt = AES.new(self.key, self.mode) # ECB 模式
plain_text = ecb_decrypt.decrypt(decode)
decrypt_string = self.unpading(plain_text).decode()
return decrypt_string
if __name__ == '__main__':
aes_encrypt = AesEncrypt()
data = "[\"13811111111\"]"
en = aes_encrypt.encrypt(data)
print("加密数据: ", en)
dr = aes_encrypt.decrypt(en)
print("解密数据: ", dr)
1. 对key进行sha1prng加密,就是文中的get_sha1prng_key方法,拿到加密后的密文当做AES的key
2. 确定AES的加密方式ECB,CBC 等 AES加密模式:ECB/CBC/CTR/OFB/CFB
3. 填充,key和原始文本都有可能填充,NoPadding,不填充,0填充,还有pkcs5padding, 不填充就是不对内容填充,直接加密,上面代码实现了\0填充和pkcs5padding 。
4. 总的来说加密内容对不上基本是key处理不一样或者填充不对。