java 中的 TripleDES 加密,用于 C# 中的解密
TripleDES Encryption in java for decryption in C#
我有 C# 代码可以解密另一个应用程序传递的加密令牌。我不能改变这部分。
现在我正在 java 中编写一个应用程序,它将加密我的令牌,然后将其传递给 C# 应用程序。
我无法将加密字符串与 java 代码匹配。任何帮助,将不胜感激。
谢谢。
C# Code
public class Crypto
{
private TripleDES DesInstance = null;
public Crypto(string key)
{
byte[] password = Encoding.GetEncoding(1252).GetBytes(key);
DesInstance = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null);
DesInstance.IV = new byte[8];
DesInstance.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, DesInstance.IV);
}
public string Decrypt(string cipheredText)
{
byte[] cipherText = StringToByteArray(cipheredText);
string plainText = null;
ICryptoTransform transform = DesInstance.CreateDecryptor();
MemoryStream memStreamEncryptedData = new MemoryStream(cipherText, 0, cipherText.Length - 1);
CryptoStream encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Read);
using (StreamReader srDecrypt = new StreamReader(encStream, Encoding.GetEncoding(1252)))
{
plainText = srDecrypt.ReadToEnd();
}
return plainText;
}
private byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
Java Code
public class TripleDes {
private static final String UNICODE_FORMAT = "UTF-8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TripleDes() throws Exception {
myEncryptionKey = "045e466ccc34a1f1688640d0441601b7ae2c";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText = null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText = new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
}
C# 代码使用 PasswordDeriveBytes#CryptDeriveKey()
作为密钥派生函数,它是 CAPI 的 CryptDeriveKey()
函数的包装器(参见 here)。 Java 代码中缺少此功能,这是不兼容的原因之一。
PasswordDeriveBytes#CryptDeriveKey()
不要与 PasswordDeriveBytes#GetBytes()
混淆。后者使用了评论中提到的PBKDF1算法的(by MS)扩展实现,此处不适用
CryptDeriveKey()
算法描述here and an implementation can be found here:
public static byte[] CryptDeriveKey(byte[] hBaseData, String hashAlgorithm, int requiredLength) throws NoSuchAlgorithmException {
int keyLength = hBaseData.length;
byte[] derivedKey = new byte[requiredLength];
if (keyLength >= requiredLength) {
for (int i = 0; i < requiredLength; i++) {
derivedKey[i] = hBaseData[i];
}
return derivedKey;
}
byte[] buff1 = new byte[64];
byte[] buff2 = new byte[64];
Arrays.fill(buff1, (byte) 0x36);
Arrays.fill(buff2, (byte) 0x5C);
for (int i = 0; i < keyLength; i++) {
buff1[i] ^= hBaseData[i];
buff2[i] ^= hBaseData[i];
}
MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
md.reset();
byte[] result1 = md.digest(buff1);
md.reset();
byte[] result2 = md.digest(buff2);
for (int i = 0; i < requiredLength; i++) {
if (i < result1.length)
derivedKey[i] = result1[i];
else
derivedKey[i] = result2[i - result1.length];
}
return derivedKey;
}
请注意,CryptDeriveKey()
不希望密码短语作为第一个参数,而是密码短语的哈希值。
用法示例:
String passphrase = "my passphrase";
String digestName = "SHA-1";
MessageDigest md = MessageDigest.getInstance(digestName);
byte[] hash = md.digest(passphrase.getBytes("Cp1252"));
byte[] key = CryptDeriveKey(hash, digestName, 24);
System.out.println(HexFormat.of().formatHex(key)); // 5de508082bfe0924356d881c8cde15a540c92f267ef5a416
请注意,在 DES 或 TripleDES 的情况下,不调整奇偶校验位。这隐含地发生在 SecretKeyFactory#generateSecret()
.
其他问题:除了密钥派生外,还需要在 Java 代码。由于 C# 代码出于未知原因删除了密文的最后一个字节,因此必须在 Java 代码中的密文末尾添加一个虚拟字节。
虽然无法根据描述更改C#代码,但以下是关于漏洞的说明: 密钥推导较弱(这适用于CryptDeriveKey()
和PBKDF1),应替换为专用的password-based 像 Argon2 或 PBKDF2 这样的密钥派生。静态 IV(如零 IV)也很弱。 TripleDES 已被弃用且速度较慢,应替换为 AES 等现代算法。
我有 C# 代码可以解密另一个应用程序传递的加密令牌。我不能改变这部分。 现在我正在 java 中编写一个应用程序,它将加密我的令牌,然后将其传递给 C# 应用程序。
我无法将加密字符串与 java 代码匹配。任何帮助,将不胜感激。 谢谢。
C# Code
public class Crypto
{
private TripleDES DesInstance = null;
public Crypto(string key)
{
byte[] password = Encoding.GetEncoding(1252).GetBytes(key);
DesInstance = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null);
DesInstance.IV = new byte[8];
DesInstance.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, DesInstance.IV);
}
public string Decrypt(string cipheredText)
{
byte[] cipherText = StringToByteArray(cipheredText);
string plainText = null;
ICryptoTransform transform = DesInstance.CreateDecryptor();
MemoryStream memStreamEncryptedData = new MemoryStream(cipherText, 0, cipherText.Length - 1);
CryptoStream encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Read);
using (StreamReader srDecrypt = new StreamReader(encStream, Encoding.GetEncoding(1252)))
{
plainText = srDecrypt.ReadToEnd();
}
return plainText;
}
private byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
Java Code
public class TripleDes {
private static final String UNICODE_FORMAT = "UTF-8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TripleDes() throws Exception {
myEncryptionKey = "045e466ccc34a1f1688640d0441601b7ae2c";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText = null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText = new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
}
C# 代码使用 PasswordDeriveBytes#CryptDeriveKey()
作为密钥派生函数,它是 CAPI 的 CryptDeriveKey()
函数的包装器(参见 here)。 Java 代码中缺少此功能,这是不兼容的原因之一。
PasswordDeriveBytes#CryptDeriveKey()
不要与 PasswordDeriveBytes#GetBytes()
混淆。后者使用了评论中提到的PBKDF1算法的(by MS)扩展实现,此处不适用
CryptDeriveKey()
算法描述here and an implementation can be found here:
public static byte[] CryptDeriveKey(byte[] hBaseData, String hashAlgorithm, int requiredLength) throws NoSuchAlgorithmException {
int keyLength = hBaseData.length;
byte[] derivedKey = new byte[requiredLength];
if (keyLength >= requiredLength) {
for (int i = 0; i < requiredLength; i++) {
derivedKey[i] = hBaseData[i];
}
return derivedKey;
}
byte[] buff1 = new byte[64];
byte[] buff2 = new byte[64];
Arrays.fill(buff1, (byte) 0x36);
Arrays.fill(buff2, (byte) 0x5C);
for (int i = 0; i < keyLength; i++) {
buff1[i] ^= hBaseData[i];
buff2[i] ^= hBaseData[i];
}
MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
md.reset();
byte[] result1 = md.digest(buff1);
md.reset();
byte[] result2 = md.digest(buff2);
for (int i = 0; i < requiredLength; i++) {
if (i < result1.length)
derivedKey[i] = result1[i];
else
derivedKey[i] = result2[i - result1.length];
}
return derivedKey;
}
请注意,CryptDeriveKey()
不希望密码短语作为第一个参数,而是密码短语的哈希值。
用法示例:
String passphrase = "my passphrase";
String digestName = "SHA-1";
MessageDigest md = MessageDigest.getInstance(digestName);
byte[] hash = md.digest(passphrase.getBytes("Cp1252"));
byte[] key = CryptDeriveKey(hash, digestName, 24);
System.out.println(HexFormat.of().formatHex(key)); // 5de508082bfe0924356d881c8cde15a540c92f267ef5a416
请注意,在 DES 或 TripleDES 的情况下,不调整奇偶校验位。这隐含地发生在 SecretKeyFactory#generateSecret()
.
其他问题:除了密钥派生外,还需要在 Java 代码。由于 C# 代码出于未知原因删除了密文的最后一个字节,因此必须在 Java 代码中的密文末尾添加一个虚拟字节。
虽然无法根据描述更改C#代码,但以下是关于漏洞的说明: 密钥推导较弱(这适用于CryptDeriveKey()
和PBKDF1),应替换为专用的password-based 像 Argon2 或 PBKDF2 这样的密钥派生。静态 IV(如零 IV)也很弱。 TripleDES 已被弃用且速度较慢,应替换为 AES 等现代算法。