VISA DUKPT アルゴリズムを実装して、トランザクション KSN からトランザクションごとに一意のキーを生成しようとしています。ANS X9.24-1:2009 によって提供される情報を順を追って説明しましたが、取得している IPEK は例で提供されているものと同じではありません。暗号化/復号化/暗号化には、弾む城 API を使用しています。例で提供されているキー (BDK) は 0123456789ABCDEFFEDCBA9876543210 です。これは、暗号化キーの長さが 2 倍であることを理解しています。
- key1 (暗号化 DES) = 0123456789ABCDEF
- key2 (復号化 DES) = FEDCBA9876543210
- key3 (暗号化 DES) = key1 = 0123456789ABCDEF
DES を使用している間は、8 バイトのキーしか使用できないため、16 の 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 で 16 進文字列を DES キー (ByteArray) に変換する方法)。TripleDES の実装には、弾む城を使用しています。
IPEK を計算するプロセスの説明は次のとおりです。
ベース派生キー (BDK) からの初期キー (IPEK) の派生。
最初の PIN エントリ デバイス キー (最初に PIN エントリ デバイスにロードされたキー) は、次のプロセスによって生成されます。
- 21 ビットの暗号化カウンターを含むキーのシリアル番号全体を、右寄せで 10 バイトのレジスターにコピーします。キーのシリアル番号が 10 バイト未満の場合は、左側に 16 進数の「FF」バイトを埋め込みます。
- この 10 バイト レジスタの最下位 21 ビットをゼロに設定します。
- この 10 バイト レジスタの上位 8 バイトを取得し、倍長の派生キーを使用してこれらの 8 バイトを暗号化/復号化/暗号化します。手順 3 で生成された暗号文を初期キーの左半分として使用します。
- 手順 2 の 10 バイト レジスタから上位 8 バイトを取得し、16 進数の C0C0 C0C0 0000 0000 C0C0 C0C0 0000 0000 で XOR された倍長派生キーをキーとして使用して、これらの 8 バイトを暗号化/復号化/暗号化します。
- 手順 5 で生成された暗号文を初期キーの右半分として使用します。
前の説明の単語ごとに、キーの左半分を取得しています
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 モードを標準推奨として使用し、IV が 00000000 の CBC モードも使用しようとしましたが、両方のアプローチのどちらも機能していないようです。
アドバイスが必要です。