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(())
    }
}

关键功能说明

  1. 建立上下文 - 使用SCardEstablishContext初始化PC/SC通信
  2. 列出读卡器 - 使用SCardListReaders获取可用读卡器列表
  3. 连接卡片 - 使用SCardConnect与特定读卡器中的卡片建立连接
  4. 发送APDU命令 - 使用SCardTransmit发送APDU命令并接收响应
  5. 错误处理 - 检查每个PC/SC调用的返回值并适当处理错误

注意事项

  1. 所有PC/SC函数都是不安全的,需要unsafe块
  2. 必须正确管理资源,确保释放上下文和断开连接
  3. 读卡器名称是以null结尾的字符串列表
  4. 不同的智能卡可能需要不同的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) })?;

注意事项

  1. pcsc-sys是低级绑定,使用时需要大量unsafe代码
  2. 考虑使用更高级的封装库如pcsc(基于pcsc-sys)
  3. 在Windows和Linux上的行为可能略有不同
  4. 确保系统已安装PC/SC服务(Windows自带,Linux需要pcsc-lite)
  5. 处理多线程时需要小心,某些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(())
}

这个完整示例包含了:

  1. 上下文建立
  2. 读卡器列表获取
  3. 智能卡连接
  4. APDU命令发送
  5. 事件监控线程
  6. 完善的错误处理
  7. 资源清理

使用时可以根据实际需求修改APDU命令和响应处理逻辑。

回到顶部