将 .Net 中的 TripleDES 算法应用到 Java 时出错
Error applying TripleDES algorith from .Net to Java
对于我们当前的项目,我们需要将 TripleDES MD5 CBC 模式的加密数据发送给另一家公司。 pass、salt 和 init vector 的值由公司给出,与此处给出的值不同(但长度相同)。
他们已经向我们发送了用于加密的代码(在 .Net C# 中),如下所示:
//Encription parameter def
static private string _pass = "12345678901234";
static private string _salt = "123456";
static private string _alg = "MD5";
static private string _iv = "1234567890123456";
public static string EncriptString(string textPlain)
{
return EncriptString(textPlain, _pass, _salt, _alg, 1, _iv, 128);
}
public static string EncriptString(string textPlain, string passBase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(textPlain);
PasswordDeriveBytes password = new PasswordDeriveBytes(passBase, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}
我一直在 Google 上搜索一天,Whosebug 试图将其翻译成 JAVA 但我做不到。这是我目前的做法:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author alvaro
*/
public class Encriptor {
// Constants -----------------------------------------------------
private static final String PASS_PHRASE = "12345678901234";//says wrong length
private static final String SALT_VALUE = "123456";
private static final int PASSWORD_ITERATIONS = 1;
private static final String INIT_VECTOR = "1234567890123456";
private static final int KEY_SIZE = 128;
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
public String encrypt(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
MessageDigest digest = MessageDigest.getInstance(DIGEST);
digest.update(SALT_VALUE.getBytes());
byte[] bytes = digest.digest(PASS_PHRASE.getBytes(ENCODING));
SecretKey password = new SecretKeySpec(bytes, "AES");
//Initialize objects
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] IV = INIT_VECTOR.getBytes();
IvParameterSpec ivParamSpec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, password, ivParamSpec);
byte[] encryptedData = cipher.doFinal(text.getBytes(ENCODING));
return new BASE64Encoder().encode(encryptedData).replaceAll("\n", "");
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}
问题是 MD5 哈希在 Java 中的处理方式与在 C# 中的处理方式不同。
测试代码:
import org.junit.Test;
import static org.junit.Assert.*;
public class EncriptorTest {
@Test
public void shouldEncriptTextCorrectly() {
// GIVEN
String input = '<xml><oficina>1234</oficina><empleado>123456</empleado></xml>'
String expected = 'Lz1aG3CFYoyzjGcMzJXDB7DQgscrv9scP+d5JY8/fiUN6LV2RsnSPqDU/E5BGKz3QbeSl3RyhUgnYyN3uBBRJA=='
// WHEN
String output = new Encriptor().encrypt(input)
//THEN
assertEquals('Wrong encription', expected, output)
}
}
解决方案
最后我使用了另一个 中给出的解决方案,其中给出了 Java 的 PasswordDeriveBytes 端口。
您与对方沟通不畅。
您说您正在尝试使用 Triple DES、MD5、CBC;但是他们使用的 .Net 代码是这样说的:
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
Rijndael 是被选为 AES 的算法的名称;他们给你的例子是使用 AES,而不是 Triple DES。
Triple DES 密钥占用 3*56 位(扩展为 3*64),即 168 位或 192 位,具体取决于您如何计算位数; API 可能消耗并期望 168 位。 AES 仅支持 128、192 和 256 位密钥大小,这解释了为什么您可能会收到 wrong size
错误。
你需要弄清楚他们是打算使用 Triple DES 还是 AES 并修复两边的代码。
顺便说一句:
众所周知,单一 DES 非常不安全;在 Amazon EC2 上花费几美元就可以破解 DES。然而,三重 DES 大致是安全的,但不鼓励进行新的开发。
众所周知,MD5 非常不安全,特别是它很容易受到长度扩展攻击、原像攻击、碰撞攻击等的攻击。请考虑使用 SHA 1、2 或 3。
对于我们当前的项目,我们需要将 TripleDES MD5 CBC 模式的加密数据发送给另一家公司。 pass、salt 和 init vector 的值由公司给出,与此处给出的值不同(但长度相同)。
他们已经向我们发送了用于加密的代码(在 .Net C# 中),如下所示:
//Encription parameter def
static private string _pass = "12345678901234";
static private string _salt = "123456";
static private string _alg = "MD5";
static private string _iv = "1234567890123456";
public static string EncriptString(string textPlain)
{
return EncriptString(textPlain, _pass, _salt, _alg, 1, _iv, 128);
}
public static string EncriptString(string textPlain, string passBase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(textPlain);
PasswordDeriveBytes password = new PasswordDeriveBytes(passBase, saltValueBytes, hashAlgorithm, passwordIterations);
byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}
我一直在 Google 上搜索一天,Whosebug 试图将其翻译成 JAVA 但我做不到。这是我目前的做法:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author alvaro
*/
public class Encriptor {
// Constants -----------------------------------------------------
private static final String PASS_PHRASE = "12345678901234";//says wrong length
private static final String SALT_VALUE = "123456";
private static final int PASSWORD_ITERATIONS = 1;
private static final String INIT_VECTOR = "1234567890123456";
private static final int KEY_SIZE = 128;
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
public String encrypt(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
MessageDigest digest = MessageDigest.getInstance(DIGEST);
digest.update(SALT_VALUE.getBytes());
byte[] bytes = digest.digest(PASS_PHRASE.getBytes(ENCODING));
SecretKey password = new SecretKeySpec(bytes, "AES");
//Initialize objects
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] IV = INIT_VECTOR.getBytes();
IvParameterSpec ivParamSpec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, password, ivParamSpec);
byte[] encryptedData = cipher.doFinal(text.getBytes(ENCODING));
return new BASE64Encoder().encode(encryptedData).replaceAll("\n", "");
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}
问题是 MD5 哈希在 Java 中的处理方式与在 C# 中的处理方式不同。
测试代码:
import org.junit.Test;
import static org.junit.Assert.*;
public class EncriptorTest {
@Test
public void shouldEncriptTextCorrectly() {
// GIVEN
String input = '<xml><oficina>1234</oficina><empleado>123456</empleado></xml>'
String expected = 'Lz1aG3CFYoyzjGcMzJXDB7DQgscrv9scP+d5JY8/fiUN6LV2RsnSPqDU/E5BGKz3QbeSl3RyhUgnYyN3uBBRJA=='
// WHEN
String output = new Encriptor().encrypt(input)
//THEN
assertEquals('Wrong encription', expected, output)
}
}
解决方案
最后我使用了另一个
您与对方沟通不畅。
您说您正在尝试使用 Triple DES、MD5、CBC;但是他们使用的 .Net 代码是这样说的:
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
Rijndael 是被选为 AES 的算法的名称;他们给你的例子是使用 AES,而不是 Triple DES。
Triple DES 密钥占用 3*56 位(扩展为 3*64),即 168 位或 192 位,具体取决于您如何计算位数; API 可能消耗并期望 168 位。 AES 仅支持 128、192 和 256 位密钥大小,这解释了为什么您可能会收到 wrong size
错误。
你需要弄清楚他们是打算使用 Triple DES 还是 AES 并修复两边的代码。
顺便说一句:
众所周知,单一 DES 非常不安全;在 Amazon EC2 上花费几美元就可以破解 DES。然而,三重 DES 大致是安全的,但不鼓励进行新的开发。
众所周知,MD5 非常不安全,特别是它很容易受到长度扩展攻击、原像攻击、碰撞攻击等的攻击。请考虑使用 SHA 1、2 或 3。