bank_card_reader.cpp
5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include "stdafx.h"
#include "etradeclient/hardware/bank_card_reader.h"
#include "etradeclient/hardware/ex_reference/bank_card_reader/dcrf32.h"
#include <exception>
#include <cstdint>
#include <sstream>
#include <iomanip> // hex conversion.
#include <intrin.h> // Endian conversion.
template <typename T>
std::string DecToHex(T d, bool show_base = false)
{
std::stringstream ss;
if (show_base)
ss << "0x";
ss << std::setfill('0') << std::setw(sizeof(T) * 2) << std::hex << static_cast<int>(d);
return ss.str();
}
BankCardReader::BankCardReader() : m_icdev(NULL)
{}
BankCardReader::~BankCardReader()
{
Disconnect();
}
bool BankCardReader::Connect() const
{
static const uint8_t RETRY_COUNT = 10;
const int kUSBPort = 100, kBaudRate = 0; // port 为 100时,表示使用USB接口通讯,则波特率无效,因此设置为0.
const int kBeepMs = 10;
uint8_t conn_count = 0;
do
{
m_icdev = dc_init(kUSBPort, kBaudRate);
if (0 < (int)m_icdev)
{
dc_beep(m_icdev, kBeepMs);
return true;
}
++conn_count;
} while (conn_count < RETRY_COUNT);
return false;
}
void BankCardReader::Disconnect() const
{
dc_exit(m_icdev);
}
std::string BankCardReader::ReadBankCardNum() const
{
FindAndResetCard(); // 读卡之前必须要先寻卡并复位卡,否则会读取失败。
SelectDF();
return ParseDFRecord(ReadDFRecord());
}
void BankCardReader::FindAndResetCard() const // 寻卡并复位卡片
{
unsigned long snr = 0;
unsigned char lenth[128] = {0};
unsigned char data[128] = {0};
int st = 0;
st = dc_card(m_icdev, 0, &snr); // 一定要先寻卡
if (0 != st)
throw std::exception("Find bank card failed!");
st = dc_pro_reset(m_icdev, lenth, data);
if (0 != st)
throw std::exception("Reset bank card failed!");
}
// Select 选择文件DF
void BankCardReader::SelectDF() const
{
const int kOK = 0;
const unsigned int kTimeout = 7; // 延迟时间,单位为:10ms, 7 来自与官方示例,该值如果设过大会导致操作失败.
const unsigned int kMaxDataSize = 100;
const unsigned char kSendDataLen = 13; // 发送的信息长度
unsigned char recv_data_len = 0; // 读取信息的长度
unsigned char send_data[kMaxDataSize] = { 0 }, recv_data[kMaxDataSize] = { 0 };
/*APDU指令:【选择文件或应用】,参考PBOC2.0标准.
* 指令格式:[CLA][INS][P1][P2][Lc][Data] ,指令值:[00][A4][04][00][08][A000000333010101]
* 代码 值
* CLA "00"
* INS "A4"
* P1 "00":按FID选择(P2必须等于00); "01":按FID选择DF; "02":按FID选择EF; "04":按文件名选择DF
* P2 "00"第一个或仅有一个, "02"选择下一个(按文件名选择),
* Lc 当P1="00"时,该值设定为"02"; 当P1="04",该值设定为Data的长度,范围为"05"-"10",
* Data 文件标识符或DF文件名
*/
send_data[0] = 0x00; // CLA
send_data[1] = 0xA4; // INS
send_data[2] = 0x04; // P1,按文件名选择DF
send_data[3] = 0x00; // P2
send_data[4] = 0x08; // Lc 数据长度
// Data:DF文件名“A000000333010101”
send_data[5] = 0xA0; send_data[6] = 0x00; send_data[7] = 0x00; send_data[8] = 0x03;
send_data[9] = 0x33; send_data[10] = 0x01; send_data[11] = 0x01; send_data[12] = 0x01;
if (kOK != dc_pro_command(m_icdev, kSendDataLen, send_data, &recv_data_len, recv_data, kTimeout))
throw std::exception("Select DF failed!");
}
// Read Record 读DF记录文件
BankCardReader::RecordDataT BankCardReader::ReadDFRecord() const
{
const int kOK = 0;
const unsigned int kTimeout = 7; // 延迟时间,单位为:10ms, 7 来自与官方示例,该值如果设过大会导致操作失败.
const unsigned int kMaxDataSize = 1024;
const unsigned char kSendDataLen = 5; // 发送的信息长度
unsigned char recv_data_len = 0; // 读取信息的长度
unsigned char send_data[kMaxDataSize] = { 0 }, recv_data[kMaxDataSize] = { 0 };
/*APDU指令:【读记录】,参考PBOC2.0标准
* 指令格式:[CLA][INS][P1][P2][Le] ,指令值:[00][B2][01][14][00]
* 代码 值
* CLA "00"
* INS "B2"
* P1 记录号, "01"
* P2 引用控制参数
* Lc 不存在;
* Data 不存在;
* Le "00"或者要读取的长度*/
send_data[0] = 0x00; // CLA
send_data[1] = 0xB2; // INS
send_data[2] = 0x01; // P1
send_data[3] = 0x14; // P2,按文件记录号访问
send_data[4] = 0x00; // Le
if (kOK != dc_pro_command(m_icdev, kSendDataLen, send_data, &recv_data_len, recv_data, kTimeout))
throw std::exception("Read DF record failed!");
return RecordDataT(recv_data, recv_data_len);// 赋值的时候,指明长度,避免处理过程中遇到'\0'就结束
}
std::string BankCardReader::ParseDFRecord(const RecordDataT& data) const
{
//数据格式:[卡号标签][卡号长度][卡号][填充字符]
//如:5A0A6230910299000378541f --- [5A][0A][6230910299000378541][f].
const unsigned char kCardNumTag = 0x5A;// 银行卡号存放位置指示标签
unsigned char card_num_length = 0;
std::size_t pos = data.find_first_of(kCardNumTag);
if (pos != std::string::npos) // 找到标签
card_num_length = data.at(++pos);// 读取卡号长度
else
throw std::exception("Read bank card num failed!");
std::string card_num;
++pos;
for (int i = 0; i < card_num_length; ++i, ++pos)
card_num.append(DecToHex(data.at(pos)));
// 如果银行卡号是奇数位,最后一位会用“f”补齐,因此读出数据的最后一位需要排除。
const int kPaddingCharCount = 1;
int card_last_num_pos = card_num_length * 2 - kPaddingCharCount;
const char kPaddingChar = 'f';// 卡号填充字符
if (kPaddingChar == card_num.at(card_last_num_pos))
card_num.erase(card_last_num_pos, kPaddingCharCount);
return card_num;
}