AES 加密问题(加密的字符串不是它应该的 - Java & .NET)
Problems with AES encryption (encrypted string is not what it should be - 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();
}
}
}
}
}
我已经尝试用 PasswordDeriveBytes() 函数中的 IV 值替换 TOKEN 值,但它仍然给我同样的错误(接收系统无法解密此请求)。
- 这是我要加密的纯文本:CXQPM4656P
- 这是我从 .NET 代码中得到的:pKjfaKu4AxBEbagiAWoLkg==
- 这是我应该得到的:kY8lgWh97fqkm9gS8zgMHg==
我现在没主意了,也得不到另一端的支持。如果有人能帮我解决就太好了。
多亏了 Topaco(问题中的第一条评论),我才得以解决。
这是 Topaco 的原始答案:
Java代码使用PBKDF2和HMAC/SHA1,C#代码基于PBKDF1算法。对于 C# 代码中的 PBKDF2,PasswordDeriveBytes 必须替换为 Rfc2898DeriveBytes(默认为 HMAC/SHA1)。请注意,.NET 实现需要至少 8 个字节的盐。此外,Java 使用 32 字节密钥,C# 代码使用 16 字节密钥。通过一致的密钥派生和密钥大小,生成的密文是相同的。 –
Topaco
我唯一需要更改的是从 .NET 5 升级到 .NET 6。
我正在尝试使用 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();
}
}
}
}
}
我已经尝试用 PasswordDeriveBytes() 函数中的 IV 值替换 TOKEN 值,但它仍然给我同样的错误(接收系统无法解密此请求)。
- 这是我要加密的纯文本:CXQPM4656P
- 这是我从 .NET 代码中得到的:pKjfaKu4AxBEbagiAWoLkg==
- 这是我应该得到的:kY8lgWh97fqkm9gS8zgMHg==
我现在没主意了,也得不到另一端的支持。如果有人能帮我解决就太好了。
多亏了 Topaco(问题中的第一条评论),我才得以解决。
这是 Topaco 的原始答案:
Java代码使用PBKDF2和HMAC/SHA1,C#代码基于PBKDF1算法。对于 C# 代码中的 PBKDF2,PasswordDeriveBytes 必须替换为 Rfc2898DeriveBytes(默认为 HMAC/SHA1)。请注意,.NET 实现需要至少 8 个字节的盐。此外,Java 使用 32 字节密钥,C# 代码使用 16 字节密钥。通过一致的密钥派生和密钥大小,生成的密文是相同的。 – Topaco
我唯一需要更改的是从 .NET 5 升级到 .NET 6。