cpu卡的发卡,消费,圈存过程
cpu卡的cos系统
目前了解的cpu厂商有山东华翼,上海复旦微电子;各个厂商又会有多个不同的芯片型号 比如华翼微 HYm4616A3,上海复旦FM1280;典型的应用有公交卡、消费、银行卡、社保卡、健康卡、门禁、身 份识别等应用
Psam卡
psam卡作为密钥的载体,会存储密钥。可以直接写入或者从加密机中获取。 在进行消费的时候psam会进行验证密钥的正确性
cpu卡发卡
-
创建主控目录FID为3F00(默认) 80E03F001638FFFFCA009572FF315041592E5359532E4444463031
-
创建主目录密钥文件 (文件id)(data长度)(3F采用加密+mac写入) (文件空间0050)
80E0 0000 07 FF 0050 FFCFFF00 -
追加主控密钥(密钥类型为39) 80D4 01 00 15 39 0000FF33+16字节密钥 (01追加密钥或者密钥类型为更改密钥) (00为密钥索引)
-
追加应用维护密钥(密钥类型为36) 80D40100153600000033+16字节密钥
-
创建3F01目录 80E03F0111381000CF009573FFA00000000386980701
-
创建钱包目录下的密钥文件 80E0000007FF02000000FF00
-
追加如下密钥类型的密钥文件
8.创建15文件(基本信息文件)
80E0001507 A8 001E0000FF FD A8以MAC的方式进行更新 FD使用线路保护02的密钥索引来计算MAC(FD的计算根据下图把二进制转成16进制)
9. 更新15文件
`04D6950022000000010000000001010001${asn}20220606204206060000`; asn为8字节长度用户卡号
15文件格式如下图
至此发卡完成。
消费
- 寻卡获取到卡片的UID,最后一个字节为20的代表卡类型为CPU卡
- 复位操作
- 选择互联互通应用即发卡时创建钱包目录A00000000386980701 返回结果包含发卡时写入的数据。根据发卡方代码来进行分割
val basicInfoIndex = walletResult.indexOf("XXXX")
val basicInfo = walletResult.substring(basicInfoIndex + 6)
cityCode = basicInfo.substring(4, 8)
//卡号
asn = basicInfo.substring(24, 40)
- 如果是使用住建部规则,则需要先进行住建部认证操作
终端向卡片发送80CA000009 获取认证码
终端向PSAM卡发送80CA000009+认证码
结果返回9000代码表示认证成功
- 进行消费初始化 805001020B 01 + 00000001 + 6字节psam卡编号 从结果中获取电子钱包交易序号atc和伪随机数
//电子钱包交易序号
val atc = initConsumerResp.substring(8, 12)
//伪随机数
val rnd = initConsumerResp.substring(22, 30)
- 终端向PSAM发送指令计算mac
"8070000024$rnd$atc${amountHex}06${datetimeStr}0100$asn${PSamCardInit.cityCode}FF0000000000".pSamOperating()
- 终端向CPU卡发送消费交易指令
val consumeResp ="805401000F$terminalCounter$datetimeStr${mac1}08".cPUOperating()
val mac2 = consumeResp.substring(8, 16)
8.终端向PSAM卡发送MAC2认证指令
"8072000004$mac2".pSamOperating()
如果返回结果为9000 代表消费成功
至此消费流程全部结束
圈存交易
- 进行消费前三步操作
- 验证个人pin 取决于发卡时是否写入了个人pin,如果没写可以省略验证
- 进行圈存
val loadInitRst =805000020B01$amountHex${terminalId}10".cPUOperating()
//旧余额
val oldBalance = loadInitRst.substring(0, 8)
//交易序号
val trn = loadInitRst.substring(8, 12)
val rnd = loadInitRst.substring(16, 24)
val mac1 = loadInitRst.substring(24, 32)
//生成过程密钥 4随机数 + 2交易序号 + 8000
val processKeyCmd = "${rnd}${trn}8000"
Logger.i("processKey=$processKeyCmd")
//使用发卡时分散的圈存密钥
val sec = calcSecret3(LOAD_SEC)
//3des计算过程密钥
val processKeyRst =
tdesECB(sec.toBytes(), processKeyCmd.toBytes(), Crypto.DesMode.Encrypt)
Logger.i("processKeyRst结果=${processKeyRst.toHexString()}")
// 计算mac1 4旧余额 4交易金额 1类型标识(01存折02钱包) 6终端机编号
val tMac1Cmd = "$oldBalance${amountHex}02$terminalId"
Logger.i("tmacCmd=$tMac1Cmd")
val calcMac1Rst = pbocDesMac(tMac1Cmd.toBytes(), processKeyRst, defaultIv)
Logger.i("mac1=$mac1 mac11=${calcMac1Rst.toHexString()} ")
//判断初始化时mac与加密完成mac是否相等
if (mac1 != calcMac1Rst.toHexString()) throw CardException("")
datetimeStr = timestamp.unix2Date("yyyyMMddHHmmss")
//4字节交易金额+1字节交易标识+6字节终端机编号+7字节终端交易日期时间)
val tMac2Cmd = "${amountHex}02$terminalId$datetimeStr"
val calcMac2Rst = pbocDesMac(tMac2Cmd.toBytes(), processKeyRst, defaultIv)
val creditForLoadRst =
"$creditForLoad$datetimeStr${calcMac2Rst.toHexString()}".cPUOperating()
返回代码9000 圈存完成
以上代码包含下面4个步骤
- 圈存初始化获得mac
- 3DES计算过程密钥
- 使用过程密钥对4交易金额 1类型标识(01存折02钱包) 6终端机编号进行pbocMac计算
- 如果初始化结果得到的mac和使用过程密钥计算的mac相等,则圈存成功
注: psam卡和cpu卡发卡的分散因子必须一致