AES 加密的问题(加密的字符串不是它应该的样子 - Java 和 .NET)
我正在尝试使用 AES 加密来加密纯文本字符串以与第三方系统集成。接收者没有任何文档来解释他们的加密算法是什么,他们只是分享了以下 Java 代码来解释加密的工作原理:
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class CipherData {
private static final String SALT = "pa(MS"; //Salt. Required for key generation
private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; //Encryption Algorithm/Cipher/Padding
private static String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
private static int KEY_SIZE = 256;
private static final String TOKEN = "Pre123454sk"; //Password. Used for Key Generation
private static String initVector = "pre1234Init12345"; //IV. Required for Key generation
private static int KEY_ITERATIONS = 22123;
public static String encrypt(String value) throws Exception { //Encryption Module
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
cipher.init(1, key, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return base64Encode(encrypted);
}
public static String decrypt(String value) throws Exception { //Decryption module
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(2, key, iv);
byte[] original = cipher.doFinal(base64Decode(value));
return new String(original);
}
private static Key generateKey() throws Exception { //AES Key Generation
byte[] saltBytes = SALT.getBytes("UTF-8");
SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
PBEKeySpec spec = new PBEKeySpec(TOKEN.toCharArray(), saltBytes, KEY_ITERATIONS, KEY_SIZE);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
return key;
}
private static String base64Encode(byte[] token) {
String encoded = DatatypeConverter.printBase64Binary(token);
return encoded;
}
private static byte[] base64Decode(String token) {
return DatatypeConverter.parseBase64Binary(token);
}
public static void main(String[] args) {
String clearPAN="ABCDE1234F", encrPAN="", decrPAN="";
try {
encrPAN=encrypt(clearPAN);
decrPAN=decrypt(encrPAN);
System.out.println("Clear PAN: " + clearPAN);
System.out.println("Encrypted PAN: " + encrPAN);
System.out.println("Decrypted PAN: " + decrPAN);
}
catch (Exception e) {
System.out.print("Exception Occured in main()");
e.printStackTrace();
}
}
}
我正在 .NET 中开发我的应用程序,但我无法获得相同的结果接收者通过使用上面的 Java 代码获取字符串,但我们不知道应该做什么。
这是我的.NET算法(我刚刚从Java代码中推断出这个逻辑,这是我第一次使用Java,所以如果我犯了一个愚蠢的错误,请友善):
private static String TOKEN = "Pre123454sk";
private static String initVector = "pre1234Init12345";
private static string Encryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
//String SALT = "pa(MS";
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
Byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
ICryptoTransform encryptor = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
using (var memStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
Byte[] cipherTextBytes = memStream.ToArray();
memStream.Close();
memStream.Flush();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
private static string Decryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.Zeros;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
byte[] cipherTextBytes1 = Convert.FromBase64String(plainText);
ICryptoTransform decryptor = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes1))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
我已经尝试过替换TOKEN值与PasswordDeriveBytes()函数中的IV值,但它仍然给我同样的错误(接收系统无法解密此请求)。
- 这是我尝试加密的纯文本: CXQPM4656P
- 这是我从 .NET 代码中得到的内容: pKjfaKu4AxBEbagiAWoLkg==
- 这是我应该得到的内容: kY8lgWh97fqkm9gS8zgMHg==
I'此时我已经没有想法了,而且我也没有得到另一端的支持。如果有人能帮我弄清楚,那就太好了。
I am trying to encrypt a plain text string to integrate with a third-party system using AES encryption. The receiver does not have any document to explain what their encryption algorithm is, and they've simply shared the below Java code to explain how the encryption works:
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class CipherData {
private static final String SALT = "pa(MS"; //Salt. Required for key generation
private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; //Encryption Algorithm/Cipher/Padding
private static String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
private static int KEY_SIZE = 256;
private static final String TOKEN = "Pre123454sk"; //Password. Used for Key Generation
private static String initVector = "pre1234Init12345"; //IV. Required for Key generation
private static int KEY_ITERATIONS = 22123;
public static String encrypt(String value) throws Exception { //Encryption Module
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
cipher.init(1, key, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return base64Encode(encrypted);
}
public static String decrypt(String value) throws Exception { //Decryption module
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
Key key = generateKey();
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(2, key, iv);
byte[] original = cipher.doFinal(base64Decode(value));
return new String(original);
}
private static Key generateKey() throws Exception { //AES Key Generation
byte[] saltBytes = SALT.getBytes("UTF-8");
SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
PBEKeySpec spec = new PBEKeySpec(TOKEN.toCharArray(), saltBytes, KEY_ITERATIONS, KEY_SIZE);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
return key;
}
private static String base64Encode(byte[] token) {
String encoded = DatatypeConverter.printBase64Binary(token);
return encoded;
}
private static byte[] base64Decode(String token) {
return DatatypeConverter.parseBase64Binary(token);
}
public static void main(String[] args) {
String clearPAN="ABCDE1234F", encrPAN="", decrPAN="";
try {
encrPAN=encrypt(clearPAN);
decrPAN=decrypt(encrPAN);
System.out.println("Clear PAN: " + clearPAN);
System.out.println("Encrypted PAN: " + encrPAN);
System.out.println("Decrypted PAN: " + decrPAN);
}
catch (Exception e) {
System.out.print("Exception Occured in main()");
e.printStackTrace();
}
}
}
I am developing my application in .NET, and I'm unable to get the same string as the receiver gets by using the above Java code, and we're out of ideas on what we should be doing.
Here is my .NET algorithm (I have just inferred this logic from the Java code, and this is my first time with Java, so please be kind if I've made a stupid error):
private static String TOKEN = "Pre123454sk";
private static String initVector = "pre1234Init12345";
private static string Encryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
//String SALT = "pa(MS";
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
Byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
ICryptoTransform encryptor = aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV);
using (var memStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
Byte[] cipherTextBytes = memStream.ToArray();
memStream.Close();
memStream.Flush();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
private static string Decryption(string plainText)
{
using (var aesProvider = new AesCryptoServiceProvider())
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(TOKEN, Encoding.UTF8.GetBytes("pa(MS"));
pdb.IterationCount = 22123;
aesProvider.KeySize = 256;
aesProvider.Padding = PaddingMode.Zeros;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Key = pdb.GetBytes(16);
aesProvider.IV = Encoding.UTF8.GetBytes(initVector);
byte[] cipherTextBytes1 = Convert.FromBase64String(plainText);
ICryptoTransform decryptor = aesProvider.CreateDecryptor(aesProvider.Key, aesProvider.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes1))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
I've already tried replacing the TOKEN value with the IV value in the PasswordDeriveBytes() function, but it's still giving me the same error (the receiving system is unable to decrypt this request).
- Here is the plain text that I'm trying to encrypt: CXQPM4656P
- Here is what I'm getting from the .NET code: pKjfaKu4AxBEbagiAWoLkg==
- Here is what I should be getting: kY8lgWh97fqkm9gS8zgMHg==
I'm out of ideas at this point, and I have no support from the other end. Would be great if someone can help me figure it out.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
感谢 Topaco(问题中的第一条评论),我能够解决这个问题。
这是Topaco的原始答案:
Java代码使用带有HMAC/SHA1的PBKDF2,C#代码基于PBKDF1的算法。对于 C# 代码中的 PBKDF2,PasswordDeriveBytes 必须替换为 Rfc2898DeriveBytes(默认使用 HMAC/SHA1)。请注意,.NET 实现需要至少 8 字节的 salt。此外,Java 使用 32 字节密钥,C# 代码使用 16 字节密钥。通过一致的密钥派生和密钥大小,生成的密文是相同的。 –
Topaco
我唯一需要更改的就是从 .NET 5 升级到 .NET 6。
Thanks to Topaco (first comment in the question), I was able to sort it out.
Here is Topaco's original answer:
The Java code uses PBKDF2 with HMAC/SHA1, the C# code an algorithm based on PBKDF1. For PBKDF2 in the C# code PasswordDeriveBytes has to be replaced by Rfc2898DeriveBytes (with HMAC/SHA1 as default). Note that the .NET implementation expects a minimum 8 bytes salt. Also, Java uses a 32 bytes key, the C# code a 16 bytes key. With consistent key derivation and key size, the generated ciphertexts are identical. –
Topaco
The only thing I had to change was to upgrade from .NET 5 to .NET 6.