合作方给的java版本的AES/CBC/NoPadding,再没调试的情况下,还以为是真的没NoPadding,调试了之后才发现,他们补位的时候的不是使用空字符去补位,所以感觉不是严格的NoPadding。
java代码:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
/**
* AES加解密方法类
*/
public class AesUtils {
// 偏移量
public static final String VIPARA = "5928772605893626";
// 编码方式
public static final String CODE_TYPE = "UTF-8";
// 填充类型
public static final String AES_TYPE = "AES/CBC/NoPadding";
// 填充字符集
private static final String[] consult = new String[]{"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
/**
* AES加密
* @param text 需加密字符串
* @return
* @throws Exception
*/
public static String encrypt(String text, String aesKey) throws Exception{
// 偏移量
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
// 密钥填充至16位
if(aesKey.length()<16) aesKey = completionCodeFor16Bytes(aesKey);
SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance(AES_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
byte[] encryptedData = cipher.doFinal(completionCodeFor16Bytes(text).getBytes(CODE_TYPE));
return toHex(encryptedData);
}
/**
* AES解密
* @param encryptText -需解密字符串
* @return
* @throws Exception
*/
public static String decrypt(String encryptText, String aesKey) throws Exception{
byte[] bytes = hexToBytes(encryptText);
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
// 密钥填充至16位
if(aesKey.length()<16) aesKey = completionCodeFor16Bytes(aesKey);
SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance(AES_TYPE);
cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
byte[] decryptedData = cipher.doFinal(bytes);
return restoreData(new String(decryptedData, CODE_TYPE));
}
/**
* 字符填充(加密前处理)
* @param str
* @return
* @throws UnsupportedEncodingException
*/
public static String completionCodeFor16Bytes(String str) throws UnsupportedEncodingException {
int len = str.getBytes(CODE_TYPE).length;
int index = len%16;
int coverCnt = 16 - index; // 需填充字符数量
String coverVal = consult[coverCnt - 1]; // 填充值
StringBuffer sb = new StringBuffer(str);
// 补位填充
for(int i=0; i<coverCnt; i++) sb.append(coverVal);
return sb.toString();
}
/**
* 还原字符串(去填充)
* @param str
* @return
*/
public static String restoreData(String str) {
// 获取最后的字符串值
int num = 0;
String markStr = str.substring(str.length()-1);
// 获取需截取字符长度
for(int i=0;i<consult.length; i++) {
if(consult[i].equals(markStr)) {
num = i +1;
break;
}
}
// 还原字符
str = str.substring(0,str.length()-num);
return str;
}
/**
* byte数组转16进制值字符串
* @param buf
* @return
*/
public static String toHex(byte[] buf) {
if(buf != null && buf.length != 0) {
StringBuilder out = new StringBuilder();
for(int i = 0; i < buf.length; ++i) {
out.append(consult[buf[i] >> 4 & 15]).append(consult[buf[i] & 15]);
}
return out.toString();
} else {
return "";
}
}
/**
* 字符串转byte数组
* @param str
* @return
*/
public static byte[] hexToBytes(String str) {
if(str == null) {
return null;
} else {
char[] hex = str.toCharArray();
int length = hex.length / 2;
byte[] raw = new byte[length];
for(int i = 0; i < length; ++i) {
int high = Character.digit(hex[i * 2], 16);
int low = Character.digit(hex[i * 2 + 1], 16);
int value = high << 4 | low;
if(value > 127) {
value -= 256;
}
raw[i] = (byte)value;
}
return raw;
}
}
public static void main(String[] args) throws Exception{
String text = "xxxxxxxxxxx_1548042768541";
String s = encrypt(text, "xxxxx*o#xxxxxxxx");
System.out.println("加密串: " + s);
System.out.println("解密值:" + decrypt(s, "xxxxx*o#xxxxxxxx"));
}
}
对于的Python版本的代码:
再处理过程中再window上出现的错误信息有:
TypeError: Object type <class 'str'> cannot be passed to C code
原因是windos环境下的解密库的Crypto.Cipher问题,尝试安装一下pycryptodome
from Crypto.Cipher import AES
# 密钥(key), 密斯偏移量(iv) CBC模式加密
def aes_encrypt(key=None, data=None, vi=None, nopadding=False):
# vi = '5928772605893626'
# 长度不是16的倍数则填充(填充方式:PKCS5Padding)
# bs = 16
# pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
# data = pad(data)
# data = data+'6666666'
length = 16 # 这里只是用于下面取余前面别以为是配置
count = len(data.encode('utf-8')) # 这是我上传的主要目的,字符长度不同所以不能直接用,需要先编码转成字节
if (count % length != 0):
add = length - (count % length)
else:
add = 0 # 看看你们对接是满16的时候加上16还是0.这里注意
# 其它语言nopadding时,python还是需要‘\0’或'\x00'这里注意与其它语言对接注
# 需要使用上面的字符集去填充,如果是真的是 nopadding 的话 这个加的是\0
if nopadding:
text1 = data + '' + ('\0' * add)
else:
# 字符串补位--最后一位就是位数 需要补充多少位的数据
consult = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]
text1 = data + '' + (consult[add - 1] * add)
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
encryptedbytes = cipher.encrypt(text1.encode('utf8'))
# 加密后得到的是bytes类型的数据
# encodestrs = base64.b64encode(encryptedbytes)
# 使用Base64进行编码,返回byte字符串
# enctext = encodestrs.decode('utf8')
# 对byte字符串按utf-8进行解码
jiami22 = encryptedbytes.hex()
return encryptedbytes.hex().upper()
def aes_decrypt(key=None, data=None, vi=None, nopadding=False):
# vi = '5928772605893626'
# encodebytes = base64.decodebytes(data)
# 将加密数据转换位bytes类型数据
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
# # 将加密数据转换位bytes类型数据fromhex
text_decrypted = cipher.decrypt(bytes.fromhex(data.lower()))
# 解密-bytesToString 字节转字符串
str_text_decrypted = bytes.decode(text_decrypted, encoding='utf8')
if nopadding:
return str_text_decrypted.rstrip('\0')
else:
consult = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]
# 截取最后一位标记- # 字符串补位--最后一位就是位数 需要补充多少位的数据
num = 0
# 获取最后一个位数的
mark_str_index = str_text_decrypted[-1:]
num = consult.index(mark_str_index)+1
# for i, val in enumerate(consult):
# # 计算出现的位置索引号
# if val == mark_str_index:
# num = int(i) + 1;
# break
return str_text_decrypted[:-num]
if __name__ == '__main__':
// 偏移量 就是IV
# public static final String VIPARA = "5928772605893626";
kkk = aes_decrypt(key='xxxxx*o#xxxxxxxx', data=aes_encrypt(key='xxxxx*o#xxxxxxxx', data='xxxxxxxxxxx_1575595738129', vi='5928772605893626'), vi='5928772605893626')
# print(bytesToString(kkk))
print(kkk)