【问题标题】:TripleDES Encryption in java for decryption in C#Java中的TripleDES加密用于C#中的解密
【发布时间】:2022-03-01 01:58:13
【问题描述】:

我有 C# 代码可以解密另一个应用程序传递的加密令牌。我不能改变这部分。 现在我正在用 java 编写一个应用程序,它将加密我的令牌,并将其传递给 C# 应用程序。

我无法将加密字符串与 java 代码匹配。任何帮助,将不胜感激。 谢谢。

C#代码

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 代码

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;
    }
}

【问题讨论】:

  • myEncryptionKey 是否与传递给Crypto 构造函数的字符串相同?
  • @JimRhodes 甚至不尝试实现PasswordDeriveBytes 是否重要?请注意,这基本上是 PBKDF1,但如果结果大于散列(在本例中为 SHA-1),则使用“特殊”Microsoft 代码对其进行扩展。特殊之处在于它完全不安全,应该直接替换。
  • C#代码没有使用MS扩展的基于PBKDF1的算法,而是MS CAPI的CryptoDeriveKey()的算法。

标签: java c# encryption tripledes


【解决方案1】:

C# 代码使用 PasswordDeriveBytes#CryptDeriveKey() 作为密钥派生函数,它是 CAPI 的 CryptDeriveKey() 函数的包装器(请参阅 here)。 Java 代码中缺少此功能,这是不兼容的原因之一。

PasswordDeriveBytes#CryptDeriveKey() 不能与PasswordDeriveBytes#GetBytes() 混淆。后者使用了cmets中提到的PBKDF1算法的(由MS)扩展实现,这里不应用。

CryptDeriveKey() 的算法在here 中描述,可以在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 代码中还将使用具有零 IV、PKCS#7 填充和适当的纯文本 (Cp1252) 和密文(十六进制编码)编码的 CBC 模式。由于 C# 代码出于未知原因删除了密文的最后一个字节,因此 Java 代码中必须在密文的末尾添加一个虚拟字节。

虽然C#代码不能根据描述进行修改,但是关于漏洞的注意事项如下: 密钥派生较弱(这适用于CryptDeriveKey()和PBKDF1),应替换为专用的基于密码的密钥派生如 Argon2 或 PBKDF2。静态 IV(如零 IV)也很弱。 TripleDES 已被弃用且速度较慢,应替换为 AES 等现代算法。

【讨论】:

  • 谢谢@Topaco。我能够在 java 中加密并在 C# 和数据匹配中解密。但是,如果我的输入字符串包含扩展的 ASCII 字符(例如,带有尖音符的拉丁小写字母 y),则会返回一些不正确的字符来代替它。你有什么建议,为什么会这样。
  • @MahendraSingh,您的 C# 代码使用 Encoding.GetEncoding(1252).GetBytes(key),但您的 Java 代码使用 myEncryptionKey.getBytes(UTF-8)
  • @MahendraSingh - 这可能是由于明文的不同编码,我在第二部分中提到了这一点。 C# 代码使用 Cp1252,而 Java 代码使用 UTF-8。如果不允许更改 C# 代码,还必须在 Java 代码中使用 Cp1252。
猜你喜欢
  • 2021-01-09
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 2015-12-12
相关资源
最近更新 更多