Android DUKPT - 3DES

 一、DUKPT概述

DUKPT 即Derived Unique Key Per Transaction(每个事务的派生唯一密钥)。ANSI X9.24规范定义的密钥管理体系,主要用于对称密钥加密场景(如MAC、PIN等敏感数据保护)。通过动态生成唯一交易密钥,解决传统固定密钥易被破解的安全风险。

二、组成结构

DUKPT由BDK及KSN组成。

  1. 基础主密钥(BDK)

    • 作为根密钥(Base Derivation Key),通过加密模块生成初始密钥

    • 通常为双倍长3DES密钥(如128位或192位

  2. 密钥序列号(KSN)

    • 包含三部分:
      • 密钥标识(10位:9位基础派生标识 + 1位子密钥标识)

      • 设备标识(5位,含二进制位扩展)

      • 交易计数器(5位,记录交易次数)

    • 确保终端密钥唯一性,防止重复

三、秘钥衍生过程

  1. 根密钥准备
    收单机构通过HSM生成双倍长3DES的BDK(如0123456789ABCDEFFEDCBA9876543210),该密钥需满足FIPS 140-2 Level 3以上安全标准。

  2. KSN结构解析
    KSN由三部分构成:

    • 设备标识:10位十六进制(如2900080124)包含厂商代码和设备序列号

    • 交易计数器:21位二进制(如00021E00001)记录交易次数

    • 扩展位:1位二进制用于标识密钥用途

  3. IPEK生成算法
    通过3DES算法对BDK和设备标识进行加密运算

  4.  动态密钥派生
    使用IPEK及KSN进行系列异或运算最终获得当前加密PIN的PEK。

四、代码实现   

        代码作者 Antoine Averlant

// 基于BDK及KSN生成当前PIN秘钥。
public static byte[] computeKeyFromBDK(byte[] baseDerivationKey, byte[] keySerialNumber) throws Exception {BitSet ksn = toBitSet(keySerialNumber);BitSet bdk = toBitSet(baseDerivationKey);BitSet ipek = getIpek(bdk, ksn);// convert key for returningBitSet key = _getCurrentKey(ipek, ksn);byte[] rkey = toByteArray(key);// secure memoryobliviate(ksn);obliviate(bdk);obliviate(ipek);obliviate(key);return rkey;
}// 基于BDK及KSN生成Ipek
public static BitSet getIpek(BitSet key, BitSet ksn) throws Exception {byte[][] ipek = new byte[2][];BitSet keyRegister = key.get(0, key.length());BitSet data = ksn.get(0, ksn.length());data.clear(59, 80);ipek[0] = encryptTripleDes(toByteArray(keyRegister), toByteArray(data.get(0, 64)));keyRegister.xor(toBitSet(toByteArray("C0C0C0C000000000C0C0C0C000000000")));ipek[1] = encryptTripleDes(toByteArray(keyRegister), toByteArray(data.get(0, 64)));byte[] bipek = concat(ipek[0], ipek[1]);BitSet bsipek = toBitSet(bipek);// secure memoryobliviate(ipek[0]);obliviate(ipek[1]);obliviate(bipek);obliviate(keyRegister);obliviate(data);return bsipek;
}// 基于IPEK及KSN生成PEK
private static BitSet _getCurrentKey(BitSet ipek, BitSet ksn) throws Exception {BitSet key = ipek.get(0, ipek.length());BitSet counter = ksn.get(0, ksn.length());counter.clear(59, ksn.length());for (int i = 59; i < ksn.length(); i++) {if (ksn.get(i)) {counter.set(i);BitSet tmp = _nonReversibleKeyGenerationProcess(key, counter.get(16, 80));// secure memoryobliviate(key);key = tmp;}}key.xor(toBitSet(toByteArray("00000000000000FF00000000000000FF"))); // data encryption variant (To PIN)// key.xor(toBitSet(toByteArray("F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0"))); // data encryption variant// key.xor(toBitSet(toByteArray("3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C"))); // data encryption variant// secure memoryobliviate(counter);return key;
}private static BitSet _nonReversibleKeyGenerationProcess(BitSet p_key, BitSet data) throws Exception {BitSet keyreg = p_key.get(0, p_key.length());BitSet reg1 = data.get(0, data.length());// step 1: Crypto Register-1 XORed with the right half of the Key Register goes to Crypto Register-2.BitSet reg2 = reg1.get(0, 64); // reg2 is being used like a temp herereg2.xor(keyreg.get(64, 128));   // and here, too, kind of// step 2: Crypto Register-2 DEA-encrypted using, as the key, the left half of the Key Register goes to Crypto Register-2reg2 = toBitSet(encryptDes(toByteArray(keyreg.get(0, 64)), toByteArray(reg2)));// step 3: Crypto Register-2 XORed with the right half of the Key Register goes to Crypto Register-2reg2.xor(keyreg.get(64, 128));// done messing with reg2// step 4: XOR the Key Register with hexadecimal C0C0 C0C0 0000 0000 C0C0 C0C0 0000 0000keyreg.xor(toBitSet(toByteArray("C0C0C0C000000000C0C0C0C000000000")));// step 5: Crypto Register-1 XORed with the right half of the Key Register goes to Crypto Register-1reg1.xor(keyreg.get(64, 128));// step 6: Crypto Register-1 DEA-encrypted using, as the key, the left half of the Key Register goes to Crypto Register-1reg1 = toBitSet(encryptDes(toByteArray(keyreg.get(0, 64)), toByteArray(reg1)));// step 7: Crypto Register-1 XORed with the right half of the Key Register goes to Crypto Register-1reg1.xor(keyreg.get(64, 128));// donebyte[] reg1b = toByteArray(reg1), reg2b = toByteArray(reg2);byte[] key = concat(reg1b, reg2b);BitSet rkey = toBitSet(key);// secure memoryobliviate(reg1);obliviate(reg2);obliviate(reg1b);obliviate(reg2b);obliviate(key);obliviate(keyreg);return rkey;
}public static byte[] encryptTripleDes(byte[] key, byte[] data) throws Exception {return encryptTripleDes(key, data, true);
}public static byte[] encryptTripleDes(byte[] key, byte[] data, boolean padding) throws Exception {BitSet bskey = toBitSet(key);BitSet k1, k2, k3;if (bskey.length() == 64) {// single lengthk1 = bskey.get(0, 64);k2 = k1;k3 = k1;} else if (bskey.length() == 128) {// double lengthk1 = bskey.get(0, 64);k2 = bskey.get(64, 128);k3 = k1;} else {// triple lengthif (bskey.length() != 192) {throw new InvalidParameterException("Key is not 8/16/24 bytes long.");}k1 = bskey.get(0, 64);k2 = bskey.get(64, 128);k3 = bskey.get(128, 192);}byte[] kb1 = toByteArray(k1), kb2 = toByteArray(k2), kb3 = toByteArray(k3);byte[] key16 = concat(kb1, kb2);byte[] key24 = concat(key16, kb3);IvParameterSpec iv = new IvParameterSpec(new byte[8]);SecretKey encryptKey = SecretKeyFactory.getInstance("DESede").generateSecret(new DESedeKeySpec(key24));Cipher encryptor;if (padding)encryptor = Cipher.getInstance("DESede/CBC/PKCS5Padding");elseencryptor = Cipher.getInstance("DESede/CBC/NoPadding");encryptor.init(Cipher.ENCRYPT_MODE, encryptKey, iv);byte[] bytes = encryptor.doFinal(data);// secure memoryobliviate(k1);obliviate(k2);obliviate(k3);obliviate(kb1);obliviate(kb2);obliviate(kb3);obliviate(key16);obliviate(key24);obliviate(bskey);return bytes;
}public static byte[] MEKQ(byte[] bPEK) throws Exception {BitSet pek = toBitSet(bPEK);pek.xor(toBitSet(toByteArray("000000000000FFFF000000000000FFFF")));byte[] rkey = toByteArray(pek);// secure memoryobliviate(pek);return rkey;
}// 核心MAC计算逻辑 ISO-9797-PART1public static byte[] calculateMac(byte[] keyData, byte[] inputData) throws Exception {// 1. 处理双倍长密钥(16字节 -> 24字节)byte[] fullKey = Arrays.copyOf(keyData, 24);byte[] key1 = new byte[16];System.arraycopy(fullKey, 0, fullKey, 16, 8);System.arraycopy(keyData, 0, key1, 0, 8);System.arraycopy(keyData, 0, key1, 8, 8);// 2. 初始化加密器(CBC模式 + 零向量)Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key1, "DESede"), new IvParameterSpec(new byte[8]));// 3. 数据填充(补零至8字节倍数)byte[] paddedData;if (inputData.length % 8 != 0) {paddedData = Arrays.copyOf(inputData, inputData.length + (8 - inputData.length % 8));} else {paddedData = Arrays.copyOf(inputData, inputData.length);}int count = paddedData.length/8;byte[] paddedBlock = new byte[8];byte[] encryptData = new byte[8];//拆分数据--加密--异或for (int i = 0; i < count; i++) {if (i == 0) {System.arraycopy(paddedData, 0, paddedBlock, 0, 8);}encryptData = cipher.doFinal(paddedBlock);if (i + 2 < count) {System.arraycopy(paddedData, (i+1) * 8, paddedBlock, 0, 8);myXor(paddedBlock, encryptData);} else {System.arraycopy(paddedData, (i+1) * 8, paddedBlock, 0, 8);myXor(paddedBlock, encryptData);break;}}cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(fullKey, "DESede"), new IvParameterSpec(new byte[8]));encryptData = cipher.doFinal(paddedBlock);// 4. 执行加密并取前8字节return Arrays.copyOfRange(encryptData, 0, encryptData.length);
}public static void myXor(byte[] a, byte[] b) {if (a.length != b.length)return;for (int i = 0; i < a.length; i++)a[i] ^= b[i];
}/*** Converts a byte array to an extended BitSet.*/public static BitSet toBitSet(byte[] b) {BitSet bs = new BitSet(8 * b.length);for (int i = 0; i < b.length; i++) {for (int j = 0; j < 8; j++) {if ((b[i] & (1L << j)) > 0) {bs.set(8 * i + (7 - j));}}}return bs;}/*** Converts an extended BitSet into a byte.* <p>* Requires that the BitSet be exactly 8 bits long.*/public static byte toByte(BitSet b) {byte value = 0;for (int i = 0; i < b.length(); i++) {if (b.get(i))value = (byte) (value | (1L << 7 - i));}return value;}/*** Converts a BitSet into a byte array.* <p>* Pads to the left with zeroes.*/public static byte[] toByteArray(BitSet b) {int size = (int) Math.ceil(b.length() / 8.0d);byte[] value = new byte[size];for (int i = 0; i < size; i++) {value[i] = toByte(b.get(i * 8, Math.min(b.length(), (i + 1) * 8)));}return value;}/*** Converts a hexadecimal String into a byte array (Big-Endian).** @param s A representation of a hexadecimal number without any leading qualifiers such as "0x" or "x".*/public static byte[] toByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));}return data;}/*** Converts a byte array into a hexadecimal string (Big-Endian).** @return A representation of a hexadecimal number without any leading qualifiers such as "0x" or "x".*/public static String toHex(byte[] bytes) {BigInteger bi = new BigInteger(1, bytes);return String.format("%0" + (bytes.length << 1) + "X", bi);}/*** Concatenates two byte arrays.** @return The array a concatenated with b.  So if r is the returned array, r[0] = a[0] and r[a.length] = b[0].*/public static byte[] concat(byte[] a, byte[] b) {byte[] c = new byte[a.length + b.length];for (int i = 0; i < a.length; i++) {c[i] = a[i];}for (int i = 0; i < b.length; i++) {c[a.length + i] = b[i];}return c;}/*** Overwrites the extended BitSet NUM_OVERWRITES times with random data for security purposes.*/public static void obliviate(BitSet b) {obliviate(b, NUM_OVERWRITES);}/*** Overwrites the byte array NUM_OVERWRITES times with random data for security purposes.*/public static void obliviate(byte[] b) {obliviate(b, NUM_OVERWRITES);}/*** Overwrites the extended BitSet with random data for security purposes.*/public static void obliviate(BitSet b, int n) {java.security.SecureRandom r = new java.security.SecureRandom();for (int i = 0; i < NUM_OVERWRITES; i++) {for (int j = 0; j < b.length(); j++) {b.set(j, r.nextBoolean());}}}/*** Overwrites the byte array with random data for security purposes.*/public static void obliviate(byte[] b, int n) {for (int i = 0; i < n; i++) {b[i] = 0x00;b[i] = 0x01;}java.security.SecureRandom r = new java.security.SecureRandom();for (int i = 0; i < n; i++) {r.nextBytes(b);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/32026.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于腾讯云高性能HAI-CPU的跨境电商客服助手全链路解析

跨境电商的背景以及痛点 根据Statista数据&#xff0c;2025年全球跨境电商市场规模预计达6.57万亿美元&#xff0c;年增长率保持在12.5% 。随着平台规则趋严&#xff08;如亚马逊封店潮&#xff09;&#xff0c;更多卖家选择自建独立站&#xff0c;2024年独立站占比已达35%。A…

神经网络探秘:原理、架构与实战案例

神经网络探秘&#xff1a;原理、架构与实战案例 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;可以分享一下给大家。点击跳转到网站。https://www.captainbed.cn/ccc 在人工智能的浪潮中&#xff0c;神经网络作为核心驱动力之一…

Web网页制作(静态网页):千年之恋

一、是用的PyCharm来写的代码 二、代码中所用到的知识点&#xff08;无 js&#xff09; 这段HTML代码展示了一个简单的注册页面&#xff0c;包含了多个HTML元素和CSS样式的应用。 这段HTML代码展示了一个典型的注册页面&#xff0c;包含了常见的HTML元素和表单控件。通过CSS样…

win32汇编环境,对话框中使用树形视图示例四

;运行效果,当点击张辽时,展示张辽的图像 ;当点击曹仁时,展示曹仁的图像 ;win32汇编环境,对话框中使用树形视图示例四 ;当点击树形视图treeview控件中的某项时,展示某些功能。这里展示的是当点到某个将领时,显示某个将领的图像 ;直接抄进RadAsm可编译运行。重要部分加备注。…

基于SpringBoot的“体育购物商城”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“体育购物商城”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体模块设计 前台用户登录界面 系统首页界面…

c#面试题整理9

1.遍历xml文档 2.解释一下这段 String s new String("xyz"); 这段在C#平台中&#xff0c;编译失败 3.说明一下抽象类 抽象类可以有构造函数 抽象类不能是静态和密封的类&#xff0c;密封的类表示无法继承&#xff0c;抽象类本身就不可实例化&#xff0c;加不好…

第85期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

如何安全处置旧设备?

每年&#xff0c;数百万台旧设备因老化、故障或被新产品取代而被丢弃&#xff0c;这些设备上存储的数据可能带来安全风险。 如果设备没有被正确删除数据&#xff0c;这些数据往往仍可被恢复。因此&#xff0c;安全处置旧设备至关重要。 旧设备可能包含的敏感数据 旧设备中可能…

【物联网-WIFI】

物联网-WIFI ■ ESP32-C3-模块简介■ ESP32-C3-■ ESP32-C3-■ WIFI-模组■ WIFI-■ WIFI- ■ ESP32-C3-模块简介 ■ ESP32-C3- ■ ESP32-C3- ■ WIFI-模组 ■ WIFI- ■ WIFI-

Linux——system V共享内存

共享内存区是最快的IPC(进程内通信)形式&#xff0c;不再通过执行进入内核的系统调用来传递彼此的数据 1.共享内存的原理 IPC通信的本质是让不同的进程先看到同一份资源&#xff0c;然后再进行通信&#xff0c;所以想要通过共享内存进行通信&#xff0c;那么第一步一定是让两个…

爱普生可编程晶振SG-8200CJ特性与应用

在高速发展的电子技术领域&#xff0c;时钟源作为电子系统的“心脏”&#xff0c;其性能直接影响设备的稳定性与可靠性。爱普生SG-8200CJ可编程晶振凭借其优秀的频率精度、低抖动性能及广泛的环境适应性&#xff0c;正成为众多领域的得力之选&#xff0c;为各类设备的高效运行与…

基于YOLO11深度学习的运动品牌LOGO检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

小程序 wxml 语法 —— 36 wxml 语法 - setData() 修改数据

在小程序中修改数据不推荐通过赋值的方式进行修改&#xff0c;通过赋值的方式修改数据无法改变页面的数据&#xff1b; 在微信小程序中&#xff0c;推荐调用 setData() 方式进行修改&#xff0c;setData() 方法接收对象作为参数&#xff0c;key 是需要修改的数据&#xff0c;v…

Linux 生成静态库

文章目录 前提小知识生成和使用.a库操作步骤 在应用程序中&#xff0c;有一些公共的代码需要反复使用的&#xff0c;可以把这些代码制作成“库文件”&#xff1b;在链接的步骤中&#xff0c;可以让链接器在“库文件”提取到我们需要使用到的代码&#xff0c;复制到生成的可执行…

校验pytorch是否支持显卡GPU 不支持卸载并安装支持版本

1.输入如下命令 pythonimport torchtorch.__version__torch.cuda.is_available() // 输出False 就是不支持如下图 2.可以看到我电脑目前是不支持的 我们现在开始卸载 exit() //先退出pip uninstall torch //开始卸载这就卸载完成了 3.我们开始安装 nvidia-smi.exe //运行…

日常debug——苍穹外卖套餐修改时不回显数据

发现问题 今天在改套餐相关接口时&#xff0c;出现了一些问题。根据之前写的菜品和口味两个表的增删改查操作的时候&#xff0c;修改菜品数据时&#xff0c;前端页面会向后端发送请求&#xff0c;将菜品信息回显&#xff0c;口味数据也会出现。但是在写套餐相关的接口时&#…

微信小程序引入vant-weapp组件教程

本章教程,介绍如何在微信小程序中引入vant-weapp。 vant-weapp文档:https://vant-ui.github.io/vant-weapp/#/button 一、新建一个小程序 二、npm初始化 npm init三、安装 Vant Weapp‘ npm i @vant/weapp -

定时器Tim输出比较(output compare)

输出比较OC(Output Compare) 输出比较可以通过比较CNT与CCR寄存器值的关系&#xff0c;来对输出电平进行置1、置0或翻转的操作&#xff0c;用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道&#xff0c;高级定时器的前3个通道额外拥有死区生…

计算机网络-应用层

客户/服务器方式&#xff08;C/S方式&#xff09; 对等方式(P2P) 域名系统DNS 作用 DNS含有域名和IP地址对应数据库&#xff0c;查询后将域名对应的IP地址发送给主机。 域名系统结构 域名服务器类型 域名解析方式 动态主机配置协议DHCP 作用&#xff1a;为局域网中的个主机…

代码优化——基于element-plus封装组件:表单封装

前言 今天实现一个基于element-plus表单组件的二次封装&#xff0c;什么是二次封装&#xff1f;查看以下表单&#xff0c;传统表单组件是不是用<el-form>嵌套几个<el-form-item>即可实现&#xff0c;那么一个表单可不可以实现&#xff0c;传入一个对象给封装组件&a…