java - Java 256 位 AES 加密
问题描述
我需要为现金流实施 256 位 AES 加密我有 c# 答案,但答案不一样,对于新手,我不确定我的方向是否正确。
这是我的代码
public static void main(String[] args) {
String key = "12345678901234567890123456789012";
String hashIv = "1234567890123456";
String value = "MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest";
String result = encrypt(key, hashIv, value);
System.out.println(result);
System.out.println();
String sha256 = encrySha256("HashKey=" + key + "&" + result + "&HashIV=" + hashIv);
System.out.println(sha256.trim());
}
public static String encrypt(String hashKey, String hashIv, String text) {
try {
SecretKeySpec skeySpec = new SecretKeySpec(hashKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(hashIv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal((text.getBytes("UTF-8")));
String test = bytesToHex(encrypted);
return test.toLowerCase();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static String encrySha256(String value) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(value.getBytes());
byte byteBuffer[] = messageDigest.digest();
StringBuffer strHexString = new StringBuffer();
for (int i = 0; i < byteBuffer.length; i++) {
String hex = Integer.toHexString(0xff & byteBuffer[i]);
if (hex.length() == 1) {
strHexString.append('0');
}
strHexString.append(hex);
}
return strHexString.toString().toUpperCase();
} catch (Exception e) {
}
return null;
}
示例加密答案:
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d
9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9
974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f
984b9d41304ffd879612177c622f75f4214fa
encryptSha256 答案:EA0A6CC37F40C1EA5692E7CBB8AE097653DF3E91365E6A9CD7E91312413C7BB8
这是 C# 代码,这是示例数据
[MerchantID] => 3430112 [RespondType] => JSON [TimeStamp] => 1485232229 [Version] => 1.4 [MerchantOrderNo] => S_1485232229 [Amt] => 40 [ItemDesc] => UnitTest
public string EncryptAES256(string source)//加密
{
string sSecretKey = "12345678901234567890123456789012";
string iv = "1234567890123456";
byte[] sourceBytes =
AddPKCS7Padding(Encoding.UTF8.GetBytes(source), 32);
var aes = new RijndaelManaged();
aes.Key = Encoding.UTF8.GetBytes(sSecretKey);
aes.IV = Encoding.UTF8.GetBytes(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
ICryptoTransform transform = aes.CreateEncryptor();
return ByteArrayToHex(transform.TransformFinalBlock(sourceBytes, 0,
sourceBytes.Length)).ToLower();
}
private static byte[] AddPKCS7Padding(byte[] data, int iBlockSize)
{
int iLength = data.Length;
byte cPadding = (byte)(iBlockSize - (iLength % iBlockSize));
var output = new byte[iLength + cPadding];
Buffer.BlockCopy(data, 0, output, 0, iLength);
for (var i = iLength; i < output.Length; i++)
output[i] = (byte)cPadding;
return output;
}
private static string ByteArrayToHex(byte[] barray)
{
char[] c = new char[barray.Length * 2];
byte b;
for (int i = 0; i < barray.Length; ++i)
{
b = ((byte)(barray[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(barray[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
return new string(c);
}
解决方案
不同加密数据的原因是您比较不同的纯文本。在您的 Java 代码中,您加密纯文本
MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest
并且您将加密数据与您的参考数据进行比较
ff91c8aa01379e4de621a44e5f11f72e4d25bdb1a18242db6cef9ef07d80b0165e476fd1d9acaa53170272c82d122961e1a0700a7427cfa1cf90db7f6d6593bbc93102a4d4b9b66d9974c13c31a7ab4bba1d4e0790f0cbbbd7ad64c6d3c8012a601ceaa808bff70f94a8efa5a4f984b9d41304ffd879612177c622f75f4214fa
但是,这些参考数据对应的是不同的纯文本。后者您可以通过使用 C# DecryptAES256-method 解密参考数据轻松得出,该方法提供
MerchantID=3430112&RespondType=JSON&TimeStamp=1485232229&Version=1.4&MerchantOrderNo=S_1485232229&Amt=40&ItemDesc=UnitTest
在这里,与 Java 代码中的纯文本相比,使用了& -delimiter。
如果您使用相同的纯文本,Java 加密和 C# EncryptAES256 方法将提供相同的加密数据(假定相同的密钥、IV 和填充;对于后者,请参阅编辑部分)。
在以下测试用例中,使用了 Java 代码中的纯文本:
encrypt("12345678901234567890123456789012", "1234567890123456", "MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest")
和
EncryptAES256("MerchantID=3430112RespondType=JSONTimeStamp=1485232229Version=1.4MerchantOrderNo=S_1485232229Amt=40ItemDesc=UnitTest")
两者都提供加密数据:
ff91c8aa01379e4de621a44e5f11f72ef45b7b9f9663d386da51af13f7f3b8f2b1ed4a3b7ac6b7783402193ea1d766e3046b6acf612d62568ccdbc475e5a14d114273735b069464dcc8281f4e5bf8486eb97d31602c3fe79cfe7140d2848413edad9d96fabf54d103f3d7a9b401c83fa5e4f17b10a280df10b3d61f23e69bbb8
这(如预期的那样)与您的参考数据不同(第一个块除外)。
编辑
关于填充还有第二个问题:您的 C# EncryptAES256-method 使用 C# AddPKCS7Padding-method 提供的自定义填充,填充为32 bytes的倍数。
相反,您的 Java 加密方法使用 PKCS5Padding 填充为16 bytes的倍数。
因此,如果纯文本的长度在 16 * n 字节和 16 * (n + 1) - 1 字节之间,甚至 n (0,2,4 ,...)。
对于奇数 n (1,3,5,...),加密数据是相同的。在上面的示例中,纯文本的字节数组有 116 个字节,即 n = 7 (112 <= 116 <= 127),因此加密数据是相同的。
如果 Java 加密方法应该使用与 C# EncryptAES256 方法相同的填充,您还必须实现类似的 Java 方法,例如:
private static byte[] addPKCS7Padding(byte[] data, int iBlockSize)
{
int iLength = data.length;
byte cPadding = (byte)(iBlockSize - (iLength % iBlockSize));
byte[] output = new byte[iLength + cPadding];
System.arraycopy(data, 0, output, 0, iLength);
for (int i = iLength; i < output.length; i++)
output[i] = (byte)cPadding;
return output;
}
在 Java 加密方法中你必须替换:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
和
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
并且
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
和
byte[] encrypted = cipher.doFinal(addPKCS7Padding(text.getBytes("UTF-8"), 32));
推荐阅读
- asp.net - 如何在 ASP.NET 中重置 Request.ServerVariables("SERVER_NAME")?
- javascript - 说明在一个参数和一个操作数(除法或乘法)的情况下如何使用 RPN 计算器
- ios - 如何使用 section 参数遍历集合内的每个文档以访问子集合的计数?
- prolog - 在 prolog 中使用 abs() 构建列表
- tensorflow - ai-platform + gsutil 权限错误:AttributeError:“GFile”对象没有“可读”属性
- artifactory - Artifactory 旧工件清理 (CLI + AQL)
- javascript - 谷歌地图在 Vaadin 14
- r - R:过滤 %in% 范围不过滤带小数的值
- python - 如何在 python 列表中进行字符串操作(连接)?
- regex - SPARQL - 查找来自特定命名空间的所有属性