3DES / DES using JCE - creating an acceptable key

I am working on a project that requires 3DES encryption in Java. The problem is that I was (and will continue to) come with a 128-bit hexadecimal key, for example, "0123456789ABCDEF0123456789ABCDEF". Converting to bytes is not a problem. but the problem is that the Java cryptographic extension API will overwhelm this key, stating that it is invalid. I understand that the MSB of each byte is just a parity bit, so JCE expects me to delete them (or I think). However, in .NET, I can specify the key as provided, and it quietly handles encryption / decryption without any complaints.

Is there a way to generate the type of key that JCE expects from the type of key that is provided to me?

I found that JCE allows you to specify an 8-byte key for DES encryption, so I tried to implement 3DES as DES EDE using half of the key provided. However, I am still getting inconsistent results with .NET.

Here's the Java code:

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

and here is the .NET code:

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

Both produce different outputs (some characters in the Base64 string are the same)

+4
source share
3 answers

the Sun provider does not accept 16-byte 3DES keys, but the BouncyCastle provider does. I just tried this and it works like a charm - it produces the same result as the .NET code!

 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)); } } 
+1
source

The 3DES keys are 192 bits long.

How do you create an instance of SecretKey ? What error message do you get?


The Java code in your question uses DES, not "Triple DES". The algorithm name should be "DESede/CBC/PKCS5Padding" . The code in your answer probably works because you got the algorithm correctly, and not because you switched providers. The SunJCE provider in Java 6 will accept 128-bit keys (and use option 2). I am not sure about older versions.

+4
source

The jPOS project resolved the issue by always using either single-line (8-byte) or three-line (24 bytes). Say your clear double-length key (in bytes) is AAAAAAAA BBBBBBBBB. All the code in the jPOS project that I have seen so far that uses JCE adds the first 8 bytes again to the clear key, so it becomes a triple-length key as such: AAAAAAAA BBBBBBBB AAAAAAAA. It seems that the Sun vendor is really taking this stuff to create SecreKeySpec, as it is 192 bits long, as @erickson mentioned.

+1
source

Source: https://habr.com/ru/post/1332075/


All Articles