2

I have the following C# implementation of triple DES

byte[] bKey = HexToBytes("C67DDB0CE47D27FAF6F32ECA5C99E8AF");
byte[] bMsg = HexToBytes("ff00");
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Padding = PaddingMode.Zeros;
des.Mode = CipherMode.CBC;
byte[] bK1 = new byte[8];
for (int i = 0; i < 8; i++) bK1[i] = bKey[i];
byte[] bK2 = new byte[8];
for (int i = 0; i < 8; i++) bK2[i] = bKey[i + 8];
ICryptoTransform ict1 = des.CreateEncryptor(bK1, iv);
byte[] bFt = ict1.TransformFinalBlock(bMsg, 0, bMsg.Length);
byte[] bLCb = new byte[8];
for (int i = 0; i < 8; i++) bLCb[i] = bFt[i + bFt.Length - 8];
des.Mode = CipherMode.ECB;
ICryptoTransform ict1_5 = des.CreateDecryptor(bK2, iv);
bLCb = ict1_5.TransformFinalBlock(bLCb, 0, bLCb.Length);
ICryptoTransform ict2 = des.CreateEncryptor(bK1, iv);
byte[] bMac = ict2.TransformFinalBlock(bLCb, 0, bLCb.Length);

ToHex(bMac); // outputs: 4BC0479D7889CF8E

I need to produce same result in Java/Groovy, in which I'm apparently stuck. The code I have for now is as follows:

byte[] bKey = Hex.decode("C67DDB0CE47D27FAF6F32ECA5C99E8AF")
byte[] bMsg = Hex.decode("ff00")

byte[] keyBytes = Arrays.copyOf(sKey.bytes, 24)
int j = 0, k = 16
while (j < 8) {
    keyBytes[k++] = keyBytes[j++]
}

SecretKey key3 = new SecretKeySpec(keyBytes, "DESede")
IvParameterSpec iv3 = new IvParameterSpec(new byte[8])
Cipher cipher3 = Cipher.getInstance("DESede/CBC/PKCS5Padding")
cipher3.init(Cipher.ENCRYPT_MODE, key3, iv3)

byte[] bMac = cipher3.doFinal(bMsg)
println new String(Hex.encode(bMac))

This one outpus: ef2c57c3fa18d0a5

Hex.decode() here is of bouncy castle

I have also tried to reproduce same C# code in java by using DES/CBC twice and EBC in final round, which gave me even different result: 48f63c809c38e1eb

It'd be great if someone could give me a hint of what I may be doing wrong

Update:

Thanks everyone for your help! Final code that works as needed without much tweaking:

Security.addProvider(new BouncyCastleProvider())
byte[] bKey = Hex.decode("C67DDB0CE47D27FAF6F32ECA5C99E8AF")
byte[] bMsg = Hex.decode("ff00")

byte[] keyBytes = Arrays.copyOf(sKey.bytes, 24)
int j = 0, k = 16
while (j < 8) {
    keyBytes[k++] = keyBytes[j++]
}

SecretKey key3 = new SecretKeySpec(keyBytes, "DESede")
IvParameterSpec iv3 = new IvParameterSpec(new byte[8])
Cipher cipher3 = Cipher.getInstance("DESede/CBC/ZeroBytePadding")
cipher3.init(Cipher.ENCRYPT_MODE, key3, iv3)

byte[] bMac = cipher3.doFinal(bMsg)
println new String(Hex.encode(bMac))
gunr2171
  • 16,104
  • 25
  • 61
  • 88
lia ant
  • 408
  • 5
  • 10
  • What are you expecting `HexToBytes("foobar")` to give you? You haven't shown the method, but it *sounds* like it's expecting hex - and `foobar` isn't hex... (You're not doing any hex conversion in the Java code, I notice...) – Jon Skeet Jul 03 '12 at 12:51
  • @JonSkeet here is a link to full C# code [link](http://rextester.com/UWUW35818) – lia ant Jul 03 '12 at 12:55
  • @JonSkeet I tried to decode/encode key and message to HEX using Bouncy Castle `Hex.decode()`. This one results in different output than ones above – lia ant Jul 03 '12 at 13:02
  • @liaant As Jon says, what are you expecting `HexToBytes("foobar")` to do? – tim_yates Jul 03 '12 at 13:08
  • @tim_yates It expected to return byte array with hexadecimal numbers from a string. – lia ant Jul 03 '12 at 13:25
  • @liaant But that function parses a hex String, and `foobar` isn't hex... – tim_yates Jul 03 '12 at 13:28
  • @tim_yates My bad for that. Probably the end of a day. Updated code for hex string representation. Still `HexToBytes()` returns the same value for each string as `Hex.decode()` so I doubt that problem is there. – lia ant Jul 03 '12 at 13:52
  • 1
    Your C# code uses Zero padding and two different encryption modes, ECB and CBC. Your Java code uses PKCS5 padding and CBC mode. You are likely to have problems if the settings don't match exactly on both sides. You might get away with the CBC/ECB switch for a single block message with a zero IV, but it will fail for a multi-block message. – rossum Jul 03 '12 at 15:40
  • @rossum According to docs 3DES is using ECB mode on final 8 bytes. Thanks for your hint about PKCS5! Changing it to ZeroBytePadding gave desired results – lia ant Jul 03 '12 at 16:53
  • Nice. That's simpler than my answer. You'll need bouncy castle for ZeroBytePadding though. It's [not supported out of the box by the JDK](http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#Cipher). – ataylor Jul 03 '12 at 17:04
  • @ataylor yeah, noticed. Thank you for your answer, it's way much simpler than my attempt to clone c# code to java and made me understand :) – lia ant Jul 03 '12 at 17:18

1 Answers1

4

You're using some non-standard padding and block chaining. You won't be able to use DESede. Try DES instead:

import javax.crypto.*
import javax.crypto.spec.*

def key1 = new SecretKeySpec("C67DDB0CE47D27FA".decodeHex(), "DES")
def key2 = new SecretKeySpec("F6F32ECA5C99E8AF".decodeHex(), "DES")
def plaintext = ("ff00" + "000000000000").decodeHex() // manually zero pad

def c1 = Cipher.getInstance("DES/CBC/NoPadding")
c1.init(Cipher.ENCRYPT_MODE, key1, new IvParameterSpec(new byte[8]))
def cipherText1 = c1.doFinal(plaintext) 

def c2 = Cipher.getInstance("DES/CBC/NoPadding")
c2.init(Cipher.DECRYPT_MODE, key2, new IvParameterSpec(new byte[8]))
def cipherText2 = c2.doFinal(cipherText1)

def c3 = Cipher.getInstance("DES/ECB/NoPadding")
c3.init(Cipher.ENCRYPT_MODE, key1)
def cipherText3 = c3.doFinal(cipherText2)

assert cipherText3.encodeHex().toString() == "4bc0479d7889cf8e"
ataylor
  • 64,891
  • 24
  • 161
  • 189