银行卡读卡涉及相关APDU命令发送。网上有很多相关博客进行了详细说明,这里就不在赘述。本文主要讲55域获取流程及本人联调时遇到的相关问题。在智能卡读取使用方面本人也属于初学者,其中如有疑问或表述不正确的地方还望谅解。
银联IC卡读卡流程详解--读卡器与卡交互指令 请参考:https://blog.csdn.net/kxd_ysheng/article/details/21178101/
金融tag对照表 请参考:https://blog.csdn.net/juvary/article/details/51487245
常用APDU指令错误码 请参考:https://blog.csdn.net/lonet/article/details/7541265
不多说直接上源码(55域获取部分):
55域获取:
'*************************************************************************************************
'*函数名: get55Date
'*程序功能:银行卡读卡 55域获取(TYPE A CPU类型)
'*开发人员:Anna
'*异动人员:无
'*入口参数:hexMoney 传入金额(例:¥201.05表示为:000000020105)
' merchantName 商户名称 (10字节)
'*返回参数:返回55域信息
' -1101 卡机内无卡
' -1102 暂不支持此类卡片,请使用借记卡
' -1103 操作不成功
' -1104 卡不在允许操作的位置上
' -1105 复卡位失败
' -1106 APDU执行失败
' -1107 暂不支持该卡支付
'*************************************************************************************************
Public Function get55Date(ByVal hexMoney As String, ByVal merchantName As String)'冷复位Dim CpuType As ByteDim CpuResetData(300) As ByteDim CpuResetDataLen As Long'定义接受判断标识符Dim resDateFlag As String'定义接收执行apdu返回数据Dim resDate As StringDim rc As IntegerIf hcom <= 0 ThenMsgBox ("请先打开串口")Exit FunctionEnd If'CPU 卡复位rc = CPU_ColdReset(hcom, 0, CpuType, CpuResetData(0), CpuResetDataLen)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "MC_ReadTrack", rc, "CPU 卡复位", "ComHandle=" & hcom & ",_CPUMode = 0x0,_CPUType = 0x0")If (rc >= 0) ThenSelect Case rcCase 0Dim Strtmp As StringStrtmp = ""For i = 0 To CpuResetDataLen - 1Strtmp = Strtmp + DoubleType(Hex(CpuResetData(i)))Next i'1. 应用选择CPUAPDUCOMMAND = "00A404000E315041592E5359532E4444463031"crt310resStr = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", crt310resStr, "APDU 应用选择", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)'2.通过循环读记录,选择 PSE 关联文件,判断是否是借记卡CPUAPDUCOMMAND = "00B2010C00"crt310resStr = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", crt310resStr, "APDU 通过循环读记录,选择 PSE 关联文件", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)'截取返回值判断是否是储蓄卡resDateFlag = Right$(crt310resStr, 4)'MsgBox ("resDateFlag")'MsgBox (resDateFlag)If (resDateFlag = "9000") ThenDim aidStr As StringaidStr = Mid$(crt310resStr, 13, 16)'3.选择文件CPUAPDUCOMMAND = "00A4040008" & aidStrcrt310resStr = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", crt310resStr, "APDU 选择文件", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)Dim resIncludeStrPosition As IntegerresIncludeStrPosition = InStr(crt310resStr, "9F38")If (resIncludeStrPosition > 0) ThenDim CPUAPDUCOMMAND55 As String'获取截取长度Dim interceptLenStr As StringinterceptLenStr = Mid$(crt310resStr, resIncludeStrPosition + 4, 2)'获取9F38数据Dim date9F38res As Stringdate9F38res = Mid$(crt310resStr, resIncludeStrPosition + 6, HEXToDEC(interceptLenStr) * 2)'定义获取信息长度Dim date9F38resHex As StringDim date9F38resLen As Integerdate9F38resHex = interceptLenStrdate9F38resLen = HEXToDEC(date9F38resHex)'定义9F38获取信息返回值Dim date9F7A As Stringdate9F7A = "00"'终端交易属性Dim date9F66 As Stringdate9F66 = "60000000"'授权金额Dim date9F02 As Stringdate9F02 = hexMoney'其他金额Dim date9F03 As Stringdate9F03 = "000000000000"'终端国家代码Dim date9F1A As Stringdate9F1A = "0156"'终端验证结果Dim date95 As Stringdate95 = "0000000000"'交易货币代码Dim date5F2A As Stringdate5F2A = "0156"'交易日期Dim date9A As Stringdate9A = Format(Now, "yymmdd")'交易类型Dim date9C As Stringdate9C = "00"'不可预知数Dim date9F37 As StringDim RandomizeInt As LongRandomizeInt = Rnd * 100000000date9F37 = Trim(RandomizeInt)'SM算法支持指示器Dim dateDF69 As StringdateDF69 = "00"'交易时间Dim date9F21 As Stringdate9F21 = Format(Now, "HHmmss")'商户名称Dim date9F4E As Stringdate9F4E = merchantNamedate9F4E = "0000000000000000000000000000000000000000"CPUAPDUCOMMAND = ""CPUAPDUCOMMAND55 = ""Dim itemLen As IntegeritemLen = 0'判断是否包含9F7AIf (InStr(date9F38res, "9F7A")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F7AitemLen = itemLen + 1End If'判断是否包含9F66If (InStr(date9F38res, "9F66")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F66itemLen = itemLen + 4End If'判断是否包含9F02(实际金额/分)If (InStr(date9F38res, "9F02")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F02itemLen = itemLen + 6End If'判断是否包含9F03If (InStr(date9F38res, "9F03")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F03itemLen = itemLen + 6End If'判断是否包含9F1AIf (InStr(date9F38res, "9F1A")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F1AitemLen = itemLen + 2End If'判断是否包含95If (InStr(date9F38res, "95")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date95itemLen = itemLen + 5End If'判断是否包含5F2AIf (InStr(date9F38res, "5F2A")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date5F2AitemLen = itemLen + 2End If'判断是否包含9AIf (InStr(date9F38res, "9A")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9AitemLen = itemLen + 3End If'判断是否包含9CIf (InStr(date9F38res, "9C")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9CitemLen = itemLen + 1End If'判断是否包含9F37If (InStr(date9F38res, "9F37")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + date9F37itemLen = itemLen + 4End If'判断是否包含DF69If (InStr(crt310resStr, "DF69")) ThenCPUAPDUCOMMAND = CPUAPDUCOMMAND + "00"itemLen = itemLen + 1End IfCPUAPDUCOMMAND = "80A80000" + DecToHex(itemLen + 2) + "83" + DecToHex(itemLen) + CPUAPDUCOMMAND'定义接受55域返回值信息Dim date55ResInfo As Stringdate55ResInfo = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", date55ResInfo, "APDU 根据9F38返回组合报文发送命令", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)If (resDateFlag = "9000") Then'获取82值Dim date82 As Stringdate82 = Mid$(date55ResInfo, 5, 4)'根据 GPO 返回的 AFLDim gpoDate As StringgpoDate = Mid$(date55ResInfo, 9, Len(date55ResInfo) - 12)Dim GPOCPUAPDUCOMMAND As StringresBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取获取gpoDate", gpoDate, "APDU 读应用数据")resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "判断Len(gpoDate) Mod 8 = 0结果:", Len(gpoDate) Mod 8 = 0, "APDU 读应用数据")'定义存储5F34数据Dim date5F34 As Stringdate5F34 = ""'定义存储8C数据Dim date8C As StringIf (Len(gpoDate) Mod 8 = 0) ThenDim forNum As IntegerforNum = Len(gpoDate) / 8For i = 1 To forNum'定义截取命令数据Dim GPOCPUAPDUCOMMANDDate As StringGPOCPUAPDUCOMMANDDate = Mid$(gpoDate, (i - 1) * 8 + 1, 8)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "定义截取命令数据GPOCPUAPDUCOMMANDDate:", GPOCPUAPDUCOMMANDDate, "APDU 读应用数据")'计算SFI值Dim SFIDate As StringSFIDate = Left$(GPOCPUAPDUCOMMANDDate, 2)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "计算SFIDate:", SFIDate, "APDU 读应用数据")Dim SFIDateRes As StringSFIDateRes = HexForSFI(SFIDate)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "计算SFI值:", SFIDateRes, "APDU 读应用数据")'截取Record 值Dim RecordDate As StringDim RecordDateNum As IntegerRecordDate = Mid$(GPOCPUAPDUCOMMANDDate, 5, 2)RecordDateNum = HEXToDEC(RecordDate)For k = 1 To RecordDateNum'拼接GPO命令GPOCPUAPDUCOMMAND = "00B2" & DecToHex(k) & SFIDateRes & "00"resDate = CPUSendCmd(GPOCPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", resDate, "APDU 读应用数据", "GPOCPUAPDUCOMMAND = " & GPOCPUAPDUCOMMAND)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "InStr(resDate, 8C):", InStr(resDate, "8C"), "APDU 截取8C数据")If (Len(resDate) > 0 And InStr(resDate, "8C") = 5 And InStr(resDate, "8D") > InStr(resDate, "8C")) Then'获取截取长度Dim interceptLen8C As StringinterceptLen8C = Mid$(resDate, 7, 2)'获取9F38数据date8C = Mid$(resDate, 9, HEXToDEC(interceptLen8C) * 2)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取8C数据", date8C, "APDU 截取8C数据")End IfIf (Len(resDate) > 0 And InStr(resDate, "5F34") > 0) Then'定义获取包含5F34位置Dim resInclude5F34Position As IntegerresInclude5F34Position = InStr(resDate, "5F34")If (resInclude5F34Position > 0) Then'获取截取长度Dim interceptLen5F34 As StringinterceptLen5F34 = Mid$(resDate, resInclude5F34Position + 4, 2)'获取9F38数据date5F34 = Mid$(resDate, resInclude5F34Position + 6, HEXToDEC(interceptLen5F34) * 2)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取5F34数据", date5F34, "APDU 截取5F34数据")End IfEnd IfNextNextElse'命令截取错误End If'组合报文获取55域9F10 9F26 9F27信息'CPUAPDUCOMMAND = "80AE800034" + CPUAPDUCOMMAND55 + date82 + date9F36 组合报文发送如上报文时 部分卡不能获取返回值报错 6A80 数据域参数不正确 将所有包含数据全部发送时 都可以正常返回If (Len(date8C) > 0) Then'此次ATC寄存器值Dim date9F36 As Stringdate9F36 = ""CPUAPDUCOMMAND = "80CA9F3600"resDate = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", resDate, "APDU 获取9F36此次ATC寄存器值", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)resDateFlag = Right$(resDate, 4)If (resDateFlag = "9000") Thendate9F36 = Mid$(resDate, 7, 4)End If'判断是否包含9F02(实际金额/分)If (InStr(date8C, "9F02")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9F02End If'判断是否包含9F03If (InStr(date8C, "9F03")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9F03End If'判断是否包含9F1AIf (InStr(date8C, "9F1A")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9F1AEnd If'判断是否包含95If (InStr(date8C, "95")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date95End If'判断是否包含5F2AIf (InStr(date8C, "5F2A")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date5F2AEnd If'判断是否包含9AIf (InStr(date8C, "9A")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9AEnd If'判断是否包含9CIf (InStr(date8C, "9C")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9CEnd If'判断是否包含9F37If (InStr(date8C, "9F37")) ThenCPUAPDUCOMMAND55 = CPUAPDUCOMMAND55 + date9F37End IfEnd IfCPUAPDUCOMMAND = "80AE800034" + CPUAPDUCOMMAND55 + date82 + date9F36resDate = CPUSendCmd(CPUAPDUCOMMAND, 0)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "CPUSendCmd", resDate, "APDU 发送组合报文获取55域信息", "CPUAPDUCOMMAND = " & CPUAPDUCOMMAND)resDateFlag = Right$(resDate, 4)If (resDateFlag = "9000") ThenDim date9F26 As StringDim date9F27 As StringDim date9F10 As Stringdate9F27 = Mid$(resDate, 5, 2)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取", date9F27, "截取 date9F27")date9F26 = Mid$(resDate, 11, 16)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取", date9F26, "截取 date9F26")date9F10 = Mid$(resDate, 27, Len(resDate) - 30)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取", date9F10, "截取 date9F10")'封装组合55域数据Dim str9F33 As StringDim resDate55 As StringresDate55 = ""str9F33 = "6040E8"resDate55 = resDate55 + "9F26" + DecToHex(Len(date9F26) / 2) + date9F26resDate55 = resDate55 + "9F27" + DecToHex(Len(date9F27) / 2) + date9F27resDate55 = resDate55 + "9F10" + DecToHex(Len(date9F10) / 2) + date9F10resDate55 = resDate55 + "9F37" + DecToHex(Len(date9F37) / 2) + date9F37resDate55 = resDate55 + "9F36" + DecToHex(Len(date9F36) / 2) + date9F36resDate55 = resDate55 + "95" + DecToHex(Len(date95) / 2) + date95resDate55 = resDate55 + "9A" + DecToHex(Len(date9A) / 2) + date9AresDate55 = resDate55 + "9C" + DecToHex(Len(date9C) / 2) + date9CresDate55 = resDate55 + "9F02" + DecToHex(Len(date9F02) / 2) + date9F02resDate55 = resDate55 + "5F2A" + DecToHex(Len(date5F2A) / 2) + date5F2AresDate55 = resDate55 + "82" + DecToHex(Len(date82) / 2) + date82resDate55 = resDate55 + "9F1A" + DecToHex(Len(date9F1A) / 2) + date9F1AresDate55 = resDate55 + "9F03" + DecToHex(Len(date9F03) / 2) + date9F03resDate55 = resDate55 + "9F33" + DecToHex(Len(date9F33) / 2) + date9F33resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "55域信息", resDate55, " resDate55结果")get55Date = resDate55 & "|" & date5F34Else'-1107 暂不支持该卡支付get55Date = -1107End IfEnd IfEnd IfEnd If'下电crt310resStr = CPUDownPower()Case 78'-1103 操作不成功get55Date = -1103Case 69'-1101 卡机内无卡get55Date = -1101Case 87get55Date = -1104End SelectElse'-1105 复卡位失败get55Date = -1105End If
End Function
55域获取使用公用函数:
Public Function HexForSFI(ByVal Dnum As String) As String'截取第一个值Dim numStr1 As StringnumStr1 = Left$(Dnum, 1)Dim numStr1Hex As StringnumStr1Hex = ASCII_to_bin(numStr1)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取第一个值", numStr1Hex, "APDU 读应用数据")'截取第二个值Dim numStr2 As StringnumStr2 = Right$(Dnum, 1)Dim numStr2Hex As StringnumStr2Hex = ASCII_to_bin(numStr2)resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "截取第二个值", numStr2Hex, "APDU 读应用数据")'拼接SFIDim SFIStr As StringSFIStr = numStr1Hex & Left$(numStr2Hex, 1) & "100"resBool = WriteErrLog("CRT_310--银行卡读卡 55域获取 (TYPE A CPU类型)--", "拼接SFI", SFIStr, "APDU 读应用数据")HexForSFI = BIN_to_HEX(SFIStr)End FunctionPublic Function BIN_to_HEX(ByVal Bin As String) As StringDim i As LongDim H As StringIf Len(Bin) Mod 4 <> 0 ThenBin = String(4 - Len(Bin) Mod 4, "0") & BinEnd IfFor i = 1 To Len(Bin) Step 4Select Case Mid(Bin, i, 4)Case "0000": H = H & "0"Case "0001": H = H & "1"Case "0010": H = H & "2"Case "0011": H = H & "3"Case "0100": H = H & "4"Case "0101": H = H & "5"Case "0110": H = H & "6"Case "0111": H = H & "7"Case "1000": H = H & "8"Case "1001": H = H & "9"Case "1010": H = H & "A"Case "1011": H = H & "B"Case "1100": H = H & "C"Case "1101": H = H & "D"Case "1110": H = H & "E"Case "1111": H = H & "F"End SelectNext i'While Left(H, 1) = "0"' H = Right(H, Len(H) - 1)' WendBIN_to_HEX = H
End Function'*************************************************************************************************
'*函数名: HEXToDEC
'*程序功能:16进制转10进制值
'*开发人员:Anna
'*异动人员:无
'*入口参数:Hex 16进制字符串
'*返回参数:10进制Integer值
'*************************************************************************************************
Public Function HEXToDEC(ByVal Hex As String) As IntegerDim i As IntegerDim B As IntegerFor i = 1 To Len(Hex)Select Case Mid(Hex, Len(Hex) - i + 1, 1)Case "0": B = B + 16 ^ (i - 1) * 0Case "1": B = B + 16 ^ (i - 1) * 1Case "2": B = B + 16 ^ (i - 1) * 2Case "3": B = B + 16 ^ (i - 1) * 3Case "4": B = B + 16 ^ (i - 1) * 4Case "5": B = B + 16 ^ (i - 1) * 5Case "6": B = B + 16 ^ (i - 1) * 6Case "7": B = B + 16 ^ (i - 1) * 7Case "8": B = B + 16 ^ (i - 1) * 8Case "9": B = B + 16 ^ (i - 1) * 9Case "A": B = B + 16 ^ (i - 1) * 10Case "B": B = B + 16 ^ (i - 1) * 11Case "C": B = B + 16 ^ (i - 1) * 12Case "D": B = B + 16 ^ (i - 1) * 13Case "E": B = B + 16 ^ (i - 1) * 14Case "F": B = B + 16 ^ (i - 1) * 15End SelectNext iHEXToDEC = B
End Function'*************************************************************************************************
'*函数名: DecToHex
'*程序功能:10进制转16进制
'*开发人员:Anna
'*异动人员:无
'*入口参数:decInt 10进制Integer值
'*返回参数:16进制字符串
'*************************************************************************************************
Public Function DecToHex(ByVal decInt As Integer) As StringDim resHex As StringIf (decInt < 16) ThenresHex = "0" + Hex(decInt)ElseresHex = Hex(decInt)End IfDecToHex = resHex
End Function'*************************************************************************************************
'*函数名: GetTLVData
'*程序功能:获取响应结果TLV对应值
'*开发人员:Anna
'*异动人员:无
'*入口参数:dateStr 主数据串
' tlvStr 要获取的tab
'*返回参数:tlvStr对应数据
'*************************************************************************************************
Public Function GetTLVData(ByVal dateStr As String, ByVal tlvStr As String) As String'定义获取tvl数据Dim tvlDate As StringtvlDate = "-1"'定义获取包含tvl位置Dim resIncludeStrPosition As IntegerresIncludeStrPosition = InStr(crt310resStr, tlvStr)If (resIncludeStrPosition >= 0) Then'获取截取长度Dim interceptLenStr As StringinterceptLenStr = Mid$(dateStr, resIncludeStrPosition + 4, 2)'获取tvl数据tvlDate = Mid$(dateStr, resIncludeStrPosition + 6, HEXToDEC(interceptLenStr) * 2)End IfGetTLVData = tvlDate
End FunctionFunction DoubleType(ByVal Str1 As String) As String '双字符函数If Len(Str1) = 0 ThenDoubleType = "00"ElseIf Len(Str1) = 1 ThenDoubleType = "0" + Str1ElseIf Len(Str1) = 2 ThenDoubleType = Str1End IfEnd IfEnd If
End FunctionFunction ASCII_to_bin(ByRef ASCData As String) As String
Dim ASC_BinBuf As String
Dim i As Integer
ASC_BinBuf = ""For i = 1 To Len(ASCData)Select Case Mid$(ASCData, i, 1)Case "0"ASC_BinBuf = ASC_BinBuf + "0000"Case "1"ASC_BinBuf = ASC_BinBuf + "0001"Case "2"ASC_BinBuf = ASC_BinBuf + "0010"Case "3"ASC_BinBuf = ASC_BinBuf + "0011"Case "4"ASC_BinBuf = ASC_BinBuf + "0100"Case "5"ASC_BinBuf = ASC_BinBuf + "0101"Case "6"ASC_BinBuf = ASC_BinBuf + "0110"Case "7"ASC_BinBuf = ASC_BinBuf + "0111"Case "8"ASC_BinBuf = ASC_BinBuf + "1000"Case "9"ASC_BinBuf = ASC_BinBuf + "1001"Case "A"ASC_BinBuf = ASC_BinBuf + "1010"Case "B"ASC_BinBuf = ASC_BinBuf + "1011"Case "C"ASC_BinBuf = ASC_BinBuf + "1100"Case "D"ASC_BinBuf = ASC_BinBuf + "1101"Case "E"ASC_BinBuf = ASC_BinBuf + "1110"Case "F"ASC_BinBuf = ASC_BinBuf + "1111"End SelectNext iASCII_to_bin = ASC_BinBufEnd Function
55域获取注意说明:
(1)获取55域信息有固定的流程,按照网上前辈博客中所写流程基本上没有问题都能获取到55域信息。但是需注意的是,获取55域发送apdu命令一般最为最后命令发送,获取9F36应用交易计数器(ATC)请放在发送获取55域apdu命令之前,经本人测试,若流程不对会导致获取55的信息中9F26数据不正确。
(2)对于根据GPO返回的AFL,读文件时,这里指出两点个人理解:
应用初始化
请求命令报文:80A800000B83099F02065F2A02
卡片返回:80167C0008010100100101011003060018010100200101009000
0801010010010101100306001801010020010100为AFL,AFL(应用文件定位器),每个AFL包括4个字节
字节1:bit8-bit4:SFI(短文件标识符)
bit3-bit1:000
字节2:文件中要读的第1个记录的记录号(不能为0)
字节3:文件中要读的最后一个记录的记录号(大于或等于字节2)
字节4:从字节2的记录好开始,用于静态数据记录的个数(从0开始,不大于(字节3)-(字节2)+1)
根据GPO返回的AFL,读文件。读文件号格式为:SFI左移3位,右边补100。
比如上面的08 十六进制就是 0000 1000 bit8-bit4才是SFI,所以真实的是:0000 0001 ,读取文件的时候,右补0100(表明读取指定记录) ,得到0000 1100,就是0x0c。同理,02文件就是:0x14
08 01 01 00
也就是说发送命令报文为:00B2 01 0C 00
其中,00B2 读取IC卡固定命令 0C 为08计算结果 ;00 一般固定为00即可, 01 一般为字节3 如果字节3为03 则发送命令报文可有三个
00B2010C 00 , 00B2020C00, 00B2030C00
(3)在获取55域9F26信息,即生成应用密文,
网上博客说可根据读取的“卡片风险管理数据对象列表1(CDOL1)”生成该命令。
即如果8C卡片风险管理数据对象列表1(CDOL1)返回为:
8C : 9F02069F03069F1A0295055F2A029A039C019F37049F21039F4E14
则请求命令报文:80AE40002E00000000000900000000000001560000800000015610041000B84FBA072019024C4E00000000000000000000000000000000000000
80AE4000 固定命令
2E 数据长度
000000000009 9F02
000000000000 9F03
0156 9F1A
0000800000 95
0156 5F2A
100410 9A
00 9C
B84FBA07 9F37
201902 9F21
4C4E00000000000000000000000000000000000000 9F4E
上述命令本人未做尝试,本人使用
"80AE800034" + 8C包含数据(除去9F21 和9F4E)+ date82 + date9F36,
即如果date82 82为7C00, date9F36为0001,
请求命令报文为:80AE800034 + 00000000000900000000000001560000800000015610041000B84FBA07 +7C00 + 0001,即80AE80003400000000000900000000000001560000800000015610041000B84FBA077C000001,经测试联调测试成功