DUKPT implementation process using Java and converting a hexadecimal string to a DES key (ByteArray)

I am trying to implement the VISA DUKPT algorithm to create a unique key for each transaction from a KSN transaction. I followed the information provided by ANS X9.24-1: 2009 step by step, but the IPEC I receive does not match the information presented in this example. For encryption / decryption / encryption, I use the bouncy castle API. The key (BDK) shown in the examples is 0123456789ABCDEFFEDCBA9876543210 I understand that this is the double length of the encryption key, meaning that

  • key1 (DES encryption) = 0123456789ABCDEF
  • key2 (DES decryption) = FEDCBA9876543210
  • key3 (DES encryption) = key1 = 0123456789ABCDEF

I know that when using DES you can only use an 8-byte key so that 16 hexadecimal characters are converted to an 8-byte array. (I have doubts if I do something here. I get this piece of code from the tutorial)

public byte[] hexStringToByteArray(String hexstring) { int i = 0; if (hexstring == null || hexstring.length() <= 0) { return null; } String stringvector = "0123456789ABCDEF"; byte[] bytevector = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; byte[] out = new byte[hexstring.length() / 2]; while (i < hexstring.length() - 1) { byte ch = 0x00; //Convert high nibble charater to a hex byte ch = (byte) (ch | bytevector[stringvector.indexOf(hexstring.charAt(i))]); ch = (byte) (ch << 4); //move this to the high bit //Convert the low nibble to a hexbyte ch = (byte) (ch | bytevector[stringvector.indexOf(hexstring.charAt(i + 1))]); //next hex value out[i / 2] = ch; i++; i++; } return out; } 

This is the only part where I doubt (how to convert a hexadecimal string to a DES key (ByteArray) in Java) at the moment. I use a bouncy castle to implement TripleDES.

The description of the IPEK calculation process is as follows:

The derivation of the initial key (IPEK) from the basic derivation key (BDK).

The initial PIN key (the key originally downloaded to the PIN input device) is generated by the following process:

  • Copy the entire key serial number, including the 21-bit encryption counter, legitimately justified in a 10-byte register. If the key serial number is less than 10 bytes, press the left button with the hexadecimal byte β€œFF”.
  • Set the 21 least significant bit of this 10-byte register to zero.
  • Take the eight most significant bytes of this 10-byte register and encrypt / decrypt / encrypt these eight bytes with a double-length division key. Use the encrypted text created in step 3 as the left half of the initial key.
  • Take the 8 most significant bytes from the 10-byte register of step 2 and encrypt / decrypt / encrypt these 8 bytes using the XORed double-length derivation key with the hexadecimal C0C0 C0C0 0000 0000 C0C0 C0C0 0000 0000 as the key.
  • Use the encrypted text created in step 5 as the right half of the initial key.

I followed the word previous explanation that I get for the left half of the key

67450505DF3A84FF

Expected value in accordance with the standard

6AC292FAA1315B4D

Provided by KSN - 9876543210E00000

After completing steps 1-3, before starting encryption / decryption / encryption, the text to be processed is as follows: FFFF9876543210E0

My implementation of TripleDES:

 import java.io.UnsupportedEncodingException; import java.security.*; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Hex; /** * * @author aealvarenga */ public class TripleDesCipherFromDES { public byte[] desEncryptionECBCipher(String key, String text) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException { Security.addProvider(new BouncyCastleProvider()); SecretKey keySpec = new SecretKeySpec(this.hexStringToByteArray(key), "DES"); final Cipher encrypter = Cipher.getInstance("DES/ECB/ZeroBytePadding", "BC"); encrypter.init(Cipher.ENCRYPT_MODE, keySpec); final byte[] plainTextBytes = text.getBytes("utf-8"); final byte[] cipherText = encrypter.doFinal(plainTextBytes); return cipherText; } public String desDecriptionECBCipher(String key, byte[] cipherText) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, UnsupportedEncodingException, BadPaddingException { Security.addProvider(new BouncyCastleProvider()); SecretKey keySpec = new SecretKeySpec(this.hexStringToByteArray(key), "DES"); final Cipher decrypter = Cipher.getInstance("DES/ECB/ZeroBytePadding", "BC"); decrypter.init(Cipher.DECRYPT_MODE, keySpec); final byte[] plainText = decrypter.doFinal(cipherText); return new String(plainText, "UTF-8"); } public byte[] desEncryptionCBCCipher(String key, String text) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { Security.addProvider(new BouncyCastleProvider()); byte[] iv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; IvParameterSpec ivSpec = new IvParameterSpec(iv); SecretKey keySpec = new SecretKeySpec(this.hexStringToByteArray(key), "DES"); final Cipher encrypter = Cipher.getInstance("DES/CBC/ZeroBytePadding", "BC"); encrypter.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec); final byte[] plainTextBytes = text.getBytes("utf-8"); final byte[] cipherText = encrypter.doFinal(plainTextBytes); return cipherText; } public String desDecriptionCBCCipher(String key, byte[] cipherText) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, UnsupportedEncodingException, BadPaddingException, InvalidAlgorithmParameterException { Security.addProvider(new BouncyCastleProvider()); byte[] iv = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; IvParameterSpec ivSpec = new IvParameterSpec(iv); SecretKey keySpec = new SecretKeySpec(this.hexStringToByteArray(key), "DES"); final Cipher decrypter = Cipher.getInstance("DES/CBC/ZeroBytePadding", "BC"); decrypter.init(Cipher.DECRYPT_MODE, keySpec,ivSpec); final byte[] plainText = decrypter.doFinal(cipherText); return new String(plainText, "UTF-8"); } public String asciiToHex(String ascii) { StringBuilder hex = new StringBuilder(); for (int i = 0; i < ascii.length(); i++) { hex.append(Integer.toHexString(ascii.charAt(i))); } return hex.toString(); } public byte[] hexStringToByteArray(String hexstring) { int i = 0; if (hexstring == null || hexstring.length() <= 0) { return null; } String stringvector = "0123456789ABCDEF"; byte[] bytevector = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; byte[] out = new byte[hexstring.length() / 2]; while (i < hexstring.length() - 1) { byte ch = 0x00; //Convert high nibble charater to a hex byte ch = (byte) (ch | bytevector[stringvector.indexOf(hexstring.charAt(i))]); ch = (byte) (ch << 4); //move this to the high bit //Convert the low nibble to a hexbyte ch = (byte) (ch | bytevector[stringvector.indexOf(hexstring.charAt(i + 1))]); //next hex value out[i / 2] = ch; i++; i++; } return out; } public String tdesedeECBCipher(String text, String doubleLenghtKey) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException { //key definition String key1 = doubleLenghtKey.substring(0, 16); String key2 = doubleLenghtKey.substring(16, 32); String key3 = key1; byte[] codedText = new TripleDesCipherFromDES().desEncryptionECBCipher(key1, text); String decodedText = new TripleDesCipherFromDES().desDecriptionECBCipher(key2, codedText); byte[] codedTextFinal = new TripleDesCipherFromDES().desEncryptionECBCipher(key3, decodedText); return new String(Hex.encode(codedTextFinal)); } public String tdesedeCBCCipher(String text, String doubleLenghtKey) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { //key definition String key1 = doubleLenghtKey.substring(0, 16); String key2 = doubleLenghtKey.substring(16, 32); String key3 = key1; byte[] codedText = new TripleDesCipherFromDES().desEncryptionCBCCipher(key1, text); String decodedText = new TripleDesCipherFromDES().desDecriptionCBCCipher(key2, codedText); byte[] codedTextFinal = new TripleDesCipherFromDES().desEncryptionCBCCipher(key3, decodedText); return new String(Hex.encode(codedTextFinal)); } public static void main(String[] args) throws Exception { String text = "FFFF9876543210E0"; String key = "0123456789ABCDEFFEDCBA9876543210"; System.out.println(new TripleDesCipherFromDES().tdesedeECBCipher(text,key)); System.out.println(new TripleDesCipherFromDES().tdesedeCBCCipher(text,key)); } } 

As you can see, I am trying to use ECB mode as standard, as well as CBC mode with IV 00000000, but neither of both approaches seems to work.

Please, I need some advice.

+4
source share

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


All Articles