银联PINBLOCK算法的实现,算法规则参考pos销售终端规范(如有需要,可联系博主索取或自行百度,本文仅上实现)
偶然翻到之前写的对于ANSI X9.8标准获取PINBlOCK,发现可能之前比较仓促,并没有对整个过程描述的比较清楚,特意重新整理了下实现,添加对应注释以便于理解该过程。
1.格式化PIN(ANSI X9.8分为带主账号信息与不带主账号信息,如果不带主账号信息那么下面函数处理后返回的即为PINBLOCK)
/**
* 格式化PIN
*
* @param pin
* @return
*/
public static byte[] formatPinByX98(byte[] pin) {
//pin长度
int l = pin.length;
//pin合法性检查,规范要求PIN长度为4-12
if (l < 4 || l > 12) {
throw new RuntimeException("pin长度错误");
}
//固定为8字节长度
byte[] encode = new byte[8];
//规范要求不足8自己后补F,先提前补好填充内容方便后面处理
Arrays.fill(encode, (byte) 0xFF);
//第一位为pin长度
encode[0] = (byte) l;
//计算填充剩余字节(填充规则,按照BCD压缩码填充,即一个字节可以表示2位密码
// 这里因为一个字节长度为8位,密码确定为0-9中任意数字,这代表用4bit即可完全表示一位密码,所以一个字节可以表示两位密码。
// 需要注意的是为无符号位的字节,所以填充前我们需要运用按位运算与0x0F运算去掉符号位)
int cl = l / 2;
for (int i = 0; i < cl; i++) {
encode[i + 1] = (byte) ((pin[i * 2] & 0x0F) << 4 | pin[i * 2 + 1] & 0x0F);
if (i == cl - 1) {
if ((l & 1) != 0) {
//如果传入密码为奇数位,因为上面计算长度整除丢失一位,所以循环到最后一位的时候需要判断传入的pin字节数组长度是否为奇数位
//如果为奇数位则需要填充上最后一位密码
encode[i + 2] = (byte) ((pin[l - 1] & 0x0F) << 4 | 0x0F);
}
}
}
return encode;
}
2.下面为处理主账号信息
/**
* 格式化PAN
*
* @param pan
* @return
*/
private byte[] formartPan(byte[] pan) {
//PAN合法性检查,规范要求PIN长度为12
if (pan.length != 12) {
throw new RuntimeException("PAN长度错误");
}
//固定为8字节长度
byte[] encode = new byte[8];
int cl = pan.length / 2;
//前面2字节固定填充为0x00
encode[0] = 0x00;
encode[1] = 0x00;
//计算填充剩余字节(填充规则,按照BCD压缩码填充,即一个字节可以表示2位卡号(原因同PIN)
for (int i = 0; i < cl; i++) {
encode[i + 2] = (byte) ((pan[i * 2] & 0x0F) << 4 | pan[i * 2 + 1] & 0x0F);
}
return encode;
}
3.下面为调用上面两个函数异或后获取带主账号信息的PINBLOCK(ANSI X9.8分带主账号信息)
byte[] pan = formartPan(pan);
byte[] pin = formatPinByX98(pin);
for (int i = 0; i < pin.length; i++) {//异或
pin[i] ^= pan[i];
}
//异或完后执行加密,以下逻辑可根据自己实际需求处理
经过上面运算得到明文的PINBLOCK,然后通过PIN密钥加密就可以得到密文PINBLOCK
以上函数 传入的 pan 和 pin 均为ASCII。
转载请注明出处!