Rust智能卡通信库pcsc-sys的使用,实现PC/SC标准接口与智能卡设备的高效交互
Rust智能卡通信库pcsc-sys的使用,实现PC/SC标准接口与智能卡设备的高效交互
安装
在项目目录中运行以下Cargo命令:
cargo add pcsc-sys
或者在Cargo.toml中添加以下行:
pcsc-sys = "1.3.0"
基本使用示例
以下是使用pcsc-sys库与智能卡设备交互的基本示例:
use pcsc_sys::*;
use std::ffi::CString;
use std::ptr;
fn main() {
// 1. 建立PC/SC上下文
let mut ctx: SCARDCONTEXT = ptr::null_mut();
let result = unsafe {
SCardEstablishContext(
SCARD_SCOPE_SYSTEM,
ptr::null_mut(),
ptr::null_mut(),
&mut ctx,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to establish context: {:?}", result);
return;
}
// 2. 列出所有读卡器
let mut readers_len: DWORD = 0;
unsafe {
SCardListReaders(ctx, ptr::null(), ptr::null_mut(), &mut readers_len);
}
let mut readers_buf = vec![0u8; readers_len as usize];
let result = unsafe {
SCardListReaders(
ctx,
ptr::null(),
readers_buf.as_mut_ptr() as *mut i8,
&mut readers_len,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to list readers: {:?}", result);
unsafe { SCardReleaseContext(ctx) };
return;
}
// 3. 连接到第一个读卡器
let reader_name = unsafe { CString::from_vec_unchecked(readers_buf) };
let mut card: SCARDHANDLE = ptr::null_mut();
let mut active_protocol: DWORD = 0;
let result = unsafe {
SCardConnect(
ctx,
reader_name.as_ptr(),
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&mut card,
&mut active_protocol,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to connect to card: {:?}", result);
unsafe { SCardReleaseContext(ctx) };
return;
}
// 4. 发送APDU命令
let apdu = [0x00, 0xA4, 0x04, 0x00, 0x00]; // SELECT命令
let mut response = [0u8; 256];
let mut response_len = response.len() as DWORD;
let result = unsafe {
SCardTransmit(
card,
ptr::null(),
apdu.as_ptr(),
apdu.len() as DWORD,
ptr::null_mut(),
response.as_mut_ptr(),
&mut response_len,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to transmit APDU: {:?}", result);
} else {
println!("Response: {:?}", &response[..response_len as usize]);
}
// 5. 清理资源
unsafe {
SCardDisconnect(card, SCARD_LEAVE_CARD);
SCardReleaseContext(ctx);
}
}
完整示例代码
以下是一个更完整的示例,展示了如何使用pcsc-sys库进行智能卡通信:
use pcsc_sys::*;
use std::ffi::CString;
use std::ptr;
use std::time::Duration;
fn main() -> Result<(), String> {
// 1. 建立上下文
let mut ctx: SCARDCONTEXT = ptr::null_mut();
let result = unsafe {
SCardEstablishContext(
SCARD_SCOPE_SYSTEM,
ptr::null_mut(),
ptr::null_mut(),
&mut ctx,
)
};
check_result(result, "Failed to establish context")?;
// 2. 获取读卡器列表
let readers = list_readers(ctx)?;
if readers.is_empty() {
unsafe { SCardReleaseContext(ctx) };
return Err("No readers found".to_string());
}
println!("Available readers:");
for reader in &readers {
println!("- {}", reader);
}
// 3. 连接到第一个读卡器
let reader_name = CString::new(readers[0].clone()).unwrap();
let (card, protocol) = connect_to_card(ctx, &reader_name)?;
println!("Connected to card with protocol: {}", protocol);
// 4. 发送测试APDU
let test_apdu = [0x00, 0xA4, 0x04, 0x00, 0x00]; // SELECT命令
let response = transmit_apdu(card, &test_apdu)?;
println!("Response: {:02X?}", response);
// 5. 断开连接并释放资源
unsafe {
SCardDisconnect(card, SCARD_LEAVE_CARD)?;
SCardReleaseContext(ctx)?;
}
Ok(())
}
fn list_readers(ctx: SCARDCONTEXT) -> Result<Vec<String>, String> {
// 获取所需缓冲区大小
let mut readers_len: DWORD = 0;
let result = unsafe {
SCardListReaders(ctx, ptr::null(), ptr::null_mut(), &mut readers_len)
};
check_result(result, "Failed to get readers list length")?;
// 实际获取读卡器列表
let mut readers_buf = vec![0u8; readers_len as usize];
let result = unsafe {
SCardListReaders(
ctx,
ptr::null(),
readers_buf.as_mut_ptr() as *mut i8,
&mut readers_len,
)
};
check_result(result, "Failed to list readers")?;
// 解析读卡器名称列表
let mut readers = Vec::new();
let mut start = 0;
for (i, &b) in readers_buf.iter().enumerate() {
if b == 0 {
if start < i {
readers.push(
String::from_utf8_lossy(&readers_buf[start..i]).into_owned()
);
}
start = i + 1;
}
}
Ok(readers)
}
fn connect_to_card(
ctx: SCARDCONTEXT,
reader_name: &CString,
) -> Result<(SCARDHANDLE, DWORD), String> {
let mut card: SCARDHANDLE = ptr::null_mut();
let mut active_protocol: DWORD = 0;
let result = unsafe {
SCardConnect(
ctx,
reader_name.as_ptr(),
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&mut card,
&mut active_protocol,
)
};
check_result(result, "Failed to connect to card")?;
Ok((card, active_protocol))
}
fn transmit_apdu(
card: SCARDHANDLE,
apdu: &[u8],
) -> Result<Vec<u8>, String> {
let mut response = vec![0u8; 256];
let mut response_len = response.len() as DWORD;
let result = unsafe {
SCardTransmit(
card,
ptr::null(),
apdu.as_ptr(),
apdu.len() as DWORD,
ptr::null_mut(),
response.as_mut_ptr(),
&mut response_len,
)
};
check_result(result, "Failed to transmit APDU")?;
response.truncate(response_len as usize);
Ok(response)
}
fn check_result(result: LONG, context: &str) -> Result<(), String> {
if result != SCARD_S_SUCCESS {
let error_msg = match result {
SCARD_E_NO_READERS_AVAILABLE => "No readers available",
SCARD_E_NOT_READY => "Reader not ready",
SCARD_E_NO_SMARTCARD => "No smart card inserted",
SCARD_E_PROTO_MISMATCH => "Protocol mismatch",
SCARD_E_UNKNOWN_READER => "Unknown reader",
_ => "Unknown error",
};
Err(format!("{}: {} (0x{:X})", context, error_msg, result))
} else {
Ok(())
}
}
关键功能说明
- 建立上下文 - 使用
SCardEstablishContext
初始化PC/SC通信 - 列出读卡器 - 使用
SCardListReaders
获取可用读卡器列表 - 连接卡片 - 使用
SCardConnect
与特定读卡器中的卡片建立连接 - 发送APDU命令 - 使用
SCardTransmit
发送APDU命令并接收响应 - 错误处理 - 检查每个PC/SC调用的返回值并适当处理错误
注意事项
- 所有PC/SC函数都是不安全的,需要unsafe块
- 必须正确管理资源,确保释放上下文和断开连接
- 读卡器名称是以null结尾的字符串列表
- 不同的智能卡可能需要不同的APDU命令和协议
这个示例展示了如何使用pcsc-sys库与智能卡设备进行基本交互。根据实际需求,您可能需要扩展此代码以支持更复杂的智能卡操作。
1 回复
Rust智能卡通信库pcsc-sys使用指南
概述
pcsc-sys
是Rust中用于与智能卡和读卡器交互的低级绑定库,实现了PC/SC (Personal Computer/Smart Card)标准接口。它提供了与Windows PC/SC和Linux/Unix PCSC-lite API的直接绑定。
主要功能
- 与智能卡读卡器建立连接
- 发送APDU命令到智能卡
- 接收智能卡响应
- 管理读卡器状态和上下文
- 处理智能卡插入/移除事件
基本使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
pcsc-sys = "1.0"
基本示例
use pcsc_sys::*;
use std::ptr;
use std::ffi::CString;
fn main() {
// 1. 建立上下文
let mut ctx: SCARDCONTEXT = ptr::null_mut();
let result = unsafe {
SCardEstablishContext(
SCARD_SCOPE_SYSTEM,
ptr::null_mut(),
ptr::null_mut(),
&mut ctx,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to establish context: {:?}", result);
return;
}
// 2. 列出读卡器
let mut readers_len: DWORD = 0;
unsafe {
SCardListReaders(ctx, ptr::null(), ptr::null_mut(), &mut readers_len);
}
let mut readers_buf = vec![0u8; readers_len as usize];
let result = unsafe {
SCardListReaders(
ctx,
ptr::null(),
readers_buf.as_mut_ptr() as *mut i8,
&mut readers_len,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to list readers: {:?}", result);
unsafe { SCardReleaseContext(ctx) };
return;
}
// 3. 连接读卡器
let reader_name = unsafe { CString::from_vec_unchecked(readers_buf) };
let mut card: SCARDHANDLE = ptr::null_mut();
let mut active_protocol: DWORD = 0;
let result = unsafe {
SCardConnect(
ctx,
reader_name.as_ptr(),
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&mut card,
&mut active_protocol,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to connect to card: {:?}", result);
unsafe { SCardReleaseContext(ctx) };
return;
}
println!("Connected to card successfully!");
// 4. 发送APDU命令 (示例: SELECT命令)
let apdu = [0x00, 0xA4, 0x04, 0x00, 0x00]; // SELECT命令
let mut response = [0u8; 256];
let mut response_len = response.len() as DWORD;
let result = unsafe {
SCardTransmit(
card,
ptr::null(),
apdu.as_ptr(),
apdu.len() as DWORD,
ptr::null_mut(),
response.as_mut_ptr(),
&mut response_len,
)
};
if result != SCARD_S_SUCCESS {
eprintln!("Failed to transmit APDU: {:?}", result);
} else {
println!("Response: {:?}", &response[..response_len as usize]);
}
// 5. 断开连接并释放资源
unsafe {
SCardDisconnect(card, SCARD_LEAVE_CARD);
SCardReleaseContext(ctx);
}
}
高级用法
事件监控
use std::thread;
use std::time::Duration;
// 在连接读卡器后...
let mut reader_states = vec![
SCARD_READERSTATE {
szReader: reader_name.as_ptr() as *mut i8,
pvUserData: ptr::null_mut(),
dwCurrentState: SCARD_STATE_UNAWARE,
dwEventState: 0,
cbAtr: 0,
rgbAtr: [0; 36],
}
];
loop {
let result = unsafe {
SCardGetStatusChange(
ctx,
1000, // 超时1秒
reader_states.as_mut_ptr(),
reader_states.len() as DWORD,
)
};
if result == SCARD_S_SUCCESS {
let state = reader_states[0].dwEventState;
if state & SCARD_STATE_PRESENT != 0 {
println!("Card inserted!");
} else if state & SCARD_STATE_EMPTY != 0 {
println!("Card removed!");
}
}
thread::sleep(Duration::from_millis(500));
}
错误处理
fn check(result: DWORD) -> Result<(), String> {
if result != SCARD_S_SUCCESS {
let error_msg = match result {
SCARD_E_NO_READERS_AVAILABLE => "No readers available",
SCARD_E_NO_SMARTCARD => "No smartcard inserted",
SCARD_E_PROTO_MISMATCH => "Protocol mismatch",
SCARD_E_NOT_READY => "Reader not ready",
_ => "Unknown error",
};
return Err(error_msg.to_string());
}
Ok(())
}
// 使用示例
check(unsafe { SCardEstablishContext(SCARD_SCOPE_SYSTEM, ptr::null_mut(), ptr::null_mut(), &mut ctx) })?;
注意事项
pcsc-sys
是低级绑定,使用时需要大量unsafe代码- 考虑使用更高级的封装库如
pcsc
(基于pcsc-sys) - 在Windows和Linux上的行为可能略有不同
- 确保系统已安装PC/SC服务(Windows自带,Linux需要pcsc-lite)
- 处理多线程时需要小心,某些PC/SC实现不是线程安全的
替代方案
如果觉得pcsc-sys
太底层,可以考虑使用pcsc
crate,它提供了更友好的Rust接口:
[dependencies]
pcsc = "2.0"
示例:
use pcsc::*;
fn main() {
let ctx = Context::establish(Scope::User).unwrap();
let readers = ctx.list_readers().unwrap();
for reader in readers {
println!("Found reader: {:?}", reader);
let card = ctx.connect(&reader, ShareMode::Shared, Protocols::ANY).unwrap();
let apdu = [0x00, 0xA4, 0x04, 0x00, 0x00];
let mut response = [0; 256];
let response_len = card.transmit(&apdu, &mut response).unwrap();
println!("Response: {:?}", &response[..response_len]);
}
}
完整示例代码
以下是一个完整的智能卡交互示例,包含错误处理和事件监控:
use pcsc_sys::*;
use std::ptr;
use std::ffi::CString;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), String> {
// 1. 建立上下文
let mut ctx: SCARDCONTEXT = ptr::null_mut();
check(unsafe {
SCardEstablishContext(
SCARD_SCOPE_SYSTEM,
ptr::null_mut(),
ptr::null_mut(),
&mut ctx,
)
})?;
// 2. 列出读卡器
let mut readers_len: DWORD = 0;
unsafe {
SCardListReaders(ctx, ptr::null(), ptr::null_mut(), &mut readers_len);
}
let mut readers_buf = vec![0u8; readers_len as usize];
check(unsafe {
SCardListReaders(
ctx,
ptr::null(),
readers_buf.as_mut_ptr() as *mut i8,
&mut readers_len,
)
})?;
// 3. 连接读卡器
let reader_name = unsafe { CString::from_vec_unchecked(readers_buf) };
let mut card: SCARDHANDLE = ptr::null_mut();
let mut active_protocol: DWORD = 0;
check(unsafe {
SCardConnect(
ctx,
reader_name.as_ptr(),
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&mut card,
&mut active_protocol,
)
})?;
println!("Connected to card successfully!");
// 4. 发送APDU命令 (示例: SELECT命令)
let apdu = [0x00, 0xA4, 0x04, 0x00, 0x00]; // SELECT命令
let mut response = [0u8; 256];
let mut response_len = response.len() as DWORD;
check(unsafe {
SCardTransmit(
card,
ptr::null(),
apdu.as_ptr(),
apdu.len() as DWORD,
ptr::null_mut(),
response.as_mut_ptr(),
&mut response_len,
)
})?;
println!("Response: {:?}", &response[..response_len as usize]);
// 5. 事件监控线程
let event_thread = thread::spawn(move || {
let mut reader_states = vec![
SCARD_READERSTATE {
szReader: reader_name.as_ptr() as *mut i8,
pvUserData: ptr::null_mut(),
dwCurrentState: SCARD_STATE_UNAWARE,
dwEventState: 0,
cbAtr: 0,
rgbAtr: [0; 36],
}
];
loop {
let result = unsafe {
SCardGetStatusChange(
ctx,
1000, // 超时1秒
reader_states.as_mut_ptr(),
reader_states.len() as DWORD,
)
};
if result == SCARD_S_SUCCESS {
let state = reader_states[0].dwEventState;
if state & SCARD_STATE_PRESENT != 0 {
println!("Card inserted!");
} else if state & SCARD_STATE_EMPTY != 0 {
println!("Card removed!");
}
}
thread::sleep(Duration::from_millis(500));
}
});
// 主线程等待用户输入退出
println!("Press Enter to exit...");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
// 6. 断开连接并释放资源
unsafe {
SCardDisconnect(card, SCARD_LEAVE_CARD);
SCardReleaseContext(ctx);
}
Ok(())
}
fn check(result: DWORD) -> Result<(), String> {
if result != SCARD_S_SUCCESS {
let error_msg = match result {
SCARD_E_NO_READERS_AVAILABLE => "No readers available",
SCARD_E_NO_SMARTCARD => "No smartcard inserted",
SCARD_E_PROTO_MISMATCH => "Protocol mismatch",
SCARD_E_NOT_READY => "Reader not ready",
_ => format!("Unknown error: 0x{:X}", result),
};
return Err(error_msg);
}
Ok(())
}
这个完整示例包含了:
- 上下文建立
- 读卡器列表获取
- 智能卡连接
- APDU命令发送
- 事件监控线程
- 完善的错误处理
- 资源清理
使用时可以根据实际需求修改APDU命令和响应处理逻辑。