3

我正在开发一个需要在 Java 中进行 3DES 加密的项目。问题是我已经(并将继续)提供了一个 128 位十六进制密钥,例如“0123456789ABCDEF0123456789ABCDEF”。转换为字节没有问题。然而,问题是Java Cryptographic Extensions API 会卡在这个密钥上,说它是无效的。我收集到每个字节的 MSB 只是一个奇偶校验位,所以 JCE 希望我删除那些(或者我认为)。然而,在 .NET 中,我可以指定提供的密钥,它会安静地处理加密/解密,没有任何抱怨。

有什么方法可以从我提供的密钥类型中生成 JCE 期望的密钥类型?

我发现 JCE 允许您为 DES 加密指定一个 8 字节的密钥,因此我尝试使用提供的一半密钥将 3DES 实现为 DES EDE。但是,我仍然得到与 .NET 不一致的结果。

这是Java代码:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class Main{
    public static void main(String[] args) throws Exception {
        byte [] plain = "I eat fish every day".getBytes("utf-8");

        byte [] keyBytes = new byte [] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
            };

        byte [] key2Bytes = new byte [] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0  }; // actual keys replaced with dummies.

        SecretKey keySpec = new SecretKeySpec(keyBytes, "DES");
        SecretKey keySpec2 = new SecretKeySpec(key2Bytes, "DES");

        IvParameterSpec iv = new IvParameterSpec(new byte[8]);

        Cipher e_cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");

        e_cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec2, iv);

        byte [] cipherText = e_cipher.doFinal(plain);
        cipherText = cipher.doFinal(cipherText);
        cipherText = e_cipher.doFinal(cipherText);

        System.out.println("Ciphertext: " + new sun.misc.BASE64Encoder().encode(cipherText));
    }
}

这是.NET代码:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace EncryptionDemo
{
    class Program
    {
    public static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        // TODO: Implement Functionality Here
        var plainBytes = Encoding.UTF8.GetBytes("I eat fish every day");
        var keyBytes = new byte [] { 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00,
         0x00,  0x00, 0x00, 0x00  };

        var tripleDES = TripleDESCryptoServiceProvider.Create();
        var transform = tripleDES.CreateEncryptor(keyBytes, new byte [8]);

        var memStream = new MemoryStream();
        var cStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);

        cStream.Write(plainBytes, 0, plainBytes.Length);
        cStream.FlushFinalBlock();

        //memStream.Position = 0;
        var cipherBytes = memStream.ToArray();

        Console.WriteLine("Ciphertext: " + Convert.ToBase64String(cipherBytes));

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}

两者都产生不同的输出(Base64 字符串中的某些字符是相同的)

4

3 回答 3

4

3DES 密钥长度为 192 位。

你是如何创建SecretKey实例的?你得到什么错误信息?


您问题中的 Java 代码使用的是 DES,而不是“三重 DES”。算法名称应该是"DESede/CBC/PKCS5Padding". 您答案中的代码可能有效,因为您的算法正确,而不是因为您切换了提供商。Java 6 中的 SunJCE 提供程序将接受 128 位密钥(并使用密钥选项 2)。我不确定旧版本。

于 2010-12-14T06:19:15.737 回答
1

Sun 提供程序不接受 16 字节 3DES 密钥,但 BouncyCastle 提供程序接受。我刚试了一下,它就像一个魅力 - 它产生与 .NET 代码相同的输出!

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Main{
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        byte [] plain = "I eat fish every day".getBytes("utf-8");

        byte [] keyBytes = new byte [] { (byte) 0xC1, (byte) 0x57, (byte) 0x45, (byte) 0x08,
            (byte) 0x85, (byte) 0x02, (byte) 0xB0, (byte) 0xD3,
            (byte) 0xA2, (byte) 0xEF, (byte) 0x68, (byte) 0x43,
            (byte) 0x5E, (byte) 0xE6, (byte) 0xD0, (byte) 0x75 };


        SecretKey keySpec = new SecretKeySpec(keyBytes, "DESede");

        IvParameterSpec iv = new IvParameterSpec(new byte[8]);

        Cipher e_cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding", "BC");

        e_cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

        byte [] cipherText = e_cipher.doFinal(plain);

        System.out.println("Ciphertext: " + new sun.misc.BASE64Encoder().encode(cipherText));
    }
}
于 2010-12-14T08:07:47.737 回答
1

jPOS项目中,该问题通过始终使用单长度(8 字节)或三长度(24 字节)密钥来解决。假设您的明确双长度密钥(以字节为单位)是 AAAAAAAA BBBBBBBB。到目前为止,我在 jPOS 项目中看到的所有使用 JCE 的代码都将前 8 个字节再次附加到清除密钥,因此它变成了一个三倍长度的密钥:AAAAAAAAA BBBBBBBB AAAAAAA。正如@erickson 所提到的,Sun 提供商似乎确实接受了这种材料来创建 SecreKeySpec,因为它有 192 位长。

于 2011-03-15T16:01:04.633 回答