在项目开发中遇到AES加密解密的问题,因为一个参数问题卡了比较久,做个记录。并给出AES加密解密分别用Java、Python和C++的实现代码。
一、AES简介
AES加密算法即密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
AES加密算法涉及4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。数据块分组长度必须为128比特,密钥长度可以是128比特、192比特、256比特中的任意一个(如果数据块及密钥长度不足时,会补齐)。
解密算法的每一步分别对应加密算法的逆操作,加解密所有操作的顺序正好是相反的。
二、AES加密解密-Java实现
/** * PAD模式 * NoPadding * PKCS7Padding * PKCS5Padding * PKCS1Padding */ private final String AES_PATTERN = "AES/CBC/NoPadding"; /** * AES加密KEY参数值,16个字符 */ private final String AES_KEY = "aaaaaaaaaaaaaaaa"; /** * AES加密向量参数值,16个字符 */ private final String AES_IV = "bbbbbbbbbbbbbbbb"; /** * 字符编码 */ private final String CHARSET_ISO ="ISO8859-1"; /** * 解密 * * @param content 要解密的文本内容,也可以直接替换成byte[]数组 * @param pattern 模式参数 * @param key 解密KEY值 * @param iv 解密向量值 * @param charSet 字符编码 * @return */ public String decrypt(byte[] content, String pattern, String key, String iv, String charSet) { try { Cipher cipher = Cipher.getInstance(pattern); Key sKeySpec = new SecretKeySpec(key.getBytes(charSet), "AES"); cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(iv.getBytes(charSet))); byte[] result = cipher.doFinal(content); return new String(result, charSet); } catch (Exception e) { throw new RuntimeException(e); } } /** * 加密 * * @param content 要加密的文本内容,也可以直接替换成byte[]数组 * @param pattern 模式参数 * @param key 解密KEY值 * @param iv 解密向量值 * @param charSet 字符编码 * @return */ public byte[] encrypt(String content, String pattern, String key, String iv, String charSet) { try { Cipher cipher = Cipher.getInstance(pattern); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(charSet), "AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv.getBytes(charSet))); byte[] encrypted = cipher.doFinal(content.getBytes(charSet)); return encrypted; } catch (Exception e) { throw new RuntimeException(e); } } /** * 初始化向量参数 * * @param iv * @return * @throws Exception */ public static AlgorithmParameters generateIV(byte[] iv) throws Exception { AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); params.init(new IvParameterSpec(iv)); return params; }
1)关于为何用”ISO8859-1″的编码方式,见Android AES加密报错处理:
javax.crypto.IllegalBlockSizeException: error:1e00007b: Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH
2)Padding参数要特别注意下
如果项目中配置文件用Python脚本加密了,解密要使用Java代码来实现,需要注意下Python脚本使用的Padding模式,就是因为在Python脚本的实现中,没有设置Padding参数,Java代码中设置了PKCS5Padding,使得解密一直出异常。如果是输出下面的异常,查下Padding参数是否设置正确。
java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e000065: Cipher functions:OPENSSL_internal:BAD_DECRYPT java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e000065: Cipher functions:OPENSSL_internal:BAD_DECRYPT at java.lang.Thread.run(Thread.java:776) Caused by: javax.crypto.BadPaddingException: error:1e000065: Cipher functions:OPENSSL_internal:BAD_DECRYPT at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method) at com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER.doFinalInternal(OpenSSLCipher.java:568) at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:350) at javax.crypto.Cipher.doFinal(Cipher.java:2056) ... 4 more
3)如果是加密解密字符串,Padding参数用PKCS5Padding,AES_PATTERN的值调整为”AES/CBC/PKCS5Padding”,报如下的异常可以尝试修改Padding参数。
Caused by: javax.crypto.IllegalBlockSizeException: error:1e00006a: Cipher functions:OPENSSL_internal:DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method) at com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER.doFinalInternal(OpenSSLCipher.java:568) at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:350) at javax.crypto.Cipher.doFinal(Cipher.java:2056)
三、AES加密解密-Python实现
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from binascii import b2a_hex, a2b_hex
from Crypto.Cipher import AES
import struct
import os
MODE = AES.MODE_CBC
KEY = ‘aaaaaaaaaaaaaaaa’
IV = ‘bbbbbbbbbbbbbbbb’
def aes_encrypt(content):
cryptor = AES.new(KEY, MODE, IV)
# 密钥key,长度:16(AES-128)、24(AES-192)、32(AES-256)
length = 16
count = len(content)
if count < length:
add = (length – count)
content = content + (‘\0’ * add)
elif count > length:
add = (length – (count % length))
content = content + (‘\0’ * add)
encrypted = cryptor.encrypt(content)
return encrypted
def aes_decrypt(content):
cryptor = AES.new(KEY, MODE, IV)
decrypt = cryptor.decrypt(content)
return decrypt
def encrypt_file(input, output):
if not os.path.exists(input):
print(‘encrypt_file: input file %s not exits.’ % input)
return
print(“encrypt_file file %s to %s” % (input, output))
with open(input, ‘r’) as origin:
data = origin.read()
encrypt_content = aes_encrypt(data)
with open(output, ‘w’) as encrypt:
encrypt.write(encrypt_content)
def decrypt_file(input, output):
if not os.path.exists(input):
print(‘decrypt_file: input file %s not exits.’ % input)
return
print(“decrypt_file file %s to %s” % (input, output))
with open(input, ‘r’) as origin:
data = origin.read()
decrypt_content = aes_decrypt(data)
with open(output, ‘w’) as decrypt:
decrypt.write(decrypt_content)
四、AES加密解密-C++实现
/** * 加密 */ gbool ConfigEncrypt(char *pData, int nLen, char **ppOutData, int *pnOutLen, char *KEY, char *VEC) { if (pData == NULL) { return gfalse; } char ivec[16] ={0} ; AES_KEY aes_ks1; int nRet = 0; char *pInData = NULL; int nBlock = 0; char *pOutData = NULL; int nOutLen = 0; if (pData <= 0) { return (gfalse); } int nTmpLen = 16 - nLen % 16; pInData = (char*)malloc(nLen + 64); memset(pInData, 0, nLen + 64); memcpy(pInData, pData, nLen); pOutData = (char*)malloc(nLen + 64); memset(pOutData, 0, nLen + 64); memcpy(ivec, VEC, 16u); nLen += nTmpLen; nRet = AES_set_encrypt_key((const unsigned char *)KEY, 128, &aes_ks1); AES_cbc_encrypt((const unsigned char *)pInData, (unsigned char *)pOutData, nLen + 16, &aes_ks1, (unsigned char *)ivec, AES_ENCRYPT); free(pInData); nOutLen = nLen + 16; *ppOutData = pOutData; *pnOutLen = nOutLen; return (gtrue); } /** * 解密 */ gbool ConfigDecrypt(char *pData, int nLen, char **ppOutData, int *pnOutLen, char *KEY, char *VEC) { if (pData == NULL || nLen == 0) { return gfalse; } gbool bRet = gfalse; char ivec[16] ={0} ; AES_KEY aes_ks1 = {0}; int nRet = 0; char *pTemp = NULL; if (pData <= 0) { return gfalse; } memcpy(ivec, VEC, sizeof(ivec)); nRet = AES_set_decrypt_key((const unsigned char *)KEY, 128, &aes_ks1); pData = pData + 16; char *pDstData = (char*)calloc(nLen + 16, 1); if (pDstData) { AES_cbc_encrypt((const unsigned char *)pData, (unsigned char *)pDstData, nLen - 16, &aes_ks1, (unsigned char *)ivec, AES_DECRYPT); memcpy(pnOutLen, pDstData, 4); char *pOutData = (char *)calloc((*pnOutLen) + 1, 1); if (pOutData && *pnOutLen > 0) { memcpy(pOutData, pDstData + 4, *pnOutLen); *ppOutData = pOutData; bRet = gtrue; } free(pDstData); pDstData = NULL; } return bRet; }
五、参考资料
Cipher functions:EVP_DecryptFinal_ex:DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
扫码或搜索:文呓
微信公众号 扫一扫关注