我正在尝试实现 VISA DUKPT 算法以从事务 KSN 生成每个事务的唯一密钥。我已逐步遵循 ANS X9.24-1:2009 提供的信息,但我得到的 IPEK 与示例中提供的不同。对于加密/解密/加密,我使用的是充气城堡 API。示例中提供的密钥 (BDK) 是 0123456789ABCDEFFEDCBA9876543210 我知道这是双重加密密钥长度,这意味着
- key1(加密DES)= 0123456789ABCDEF
- key2(解密 DES)= FEDCBA9876543210
- key3(加密DES)=key1=0123456789ABCDEF
我知道在使用 DES 时,您只能使用 8 字节密钥,以便将 16 个十六进制字符串转换为 8 字节数组。(我怀疑我是否在这里做某事。我从教程中得到这部分代码)
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;
}
这是我目前唯一有疑问的部分(如何将十六进制字符串转换为 Java 中的 DES 密钥(ByteArray))。对于 TripleDES 的实现,我正在使用充气城堡。
IPEK的计算过程描述如下:
从基本派生密钥 (BDK) 派生初始密钥 (IPEK)。
初始 PIN 输入设备密钥(最初加载到 PIN 输入设备中的密钥)由以下过程生成:
- 将整个密钥序列号,包括 21 位加密计数器,右对齐复制到 10 字节寄存器中。如果密钥序列号小于 10 个字节,则在左侧填充十六进制“FF”字节。
- 将此 10 字节寄存器的 21 个最低有效位设置为零。
- 取这个 10 字节寄存器的 8 个最高有效字节,并使用双长派生密钥对这 8 个字节进行加密/解密/加密。使用步骤 3 生成的密文作为 Initial Key 的左半部分。
- 从步骤 2 的 10 字节寄存器中取出 8 个最高有效字节,并使用与十六进制 C0C0 C0C0 0000 0000 C0C0 C0C0 0000 0000 异或的双长度派生密钥作为密钥对这 8 个字节进行加密/解密/加密。
- 使用第 5 步产生的密文作为 Initial Key 的右半部分。
我逐字逐句地按照前面的解释,我得到了钥匙的左半部分
67450505DF3A84FF
根据标准的期望值为
6AC292FAA1315B4D
提供的 KSN 为 9876543210E00000
在开始加密/解密/加密之前按照步骤 1-3 之后,要处理的文本是:FFFF9876543210E0
我的 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));
}
}
如您所见,我尝试使用 ECB 模式作为标准推荐以及 CBC 模式,IV 为 00000000,但这两种方法似乎都不起作用。
请我需要一个建议。