You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于芯片读取全类型卡片PAN Code的C语言实现技术咨询

Alright, let's break down how to implement PAN code reading for all card types using C, building on the APDU steps you've already started with. Since you don't have existing routines to lean on, we'll build this from the ground up with practical code snippets and explanations.

Core Prerequisites

First, you'll need to handle communication between your C program and the chip. Most chip readers use a serial interface (UART/USB-UART), so we'll use standard Linux system calls (open(), read(), write()) for this. For embedded systems, adapt this to your hardware's UART driver.

Step 1: Implement the SELECT + GET RESPONSE Sequence

You've already outlined the initial APDU commands for selecting 1PAY.SYS.DDF01. Let's turn that into reusable C code.

APDU Send/Response Helper Function

First, create a helper function to send APDU commands and validate responses:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

// Configure serial port (adjust baud rate, parity to match your chip specs)
int configure_serial(int fd) {
    struct termios tty;
    memset(&tty, 0, sizeof(tty));

    if (tcgetattr(fd, &tty) != 0) {
        perror("tcgetattr failed");
        return -1;
    }

    // Set baud rate (example: 9600)
    cfsetospeed(&tty, B9600);
    cfsetispeed(&tty, B9600);

    // 8 data bits, no parity, 1 stop bit
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;

    // Disable flow control
    tty.c_cflag &= ~CRTSCTS;
    tty.c_cflag |= CREAD | CLOCAL;

    // Set raw input mode
    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO;
    tty.c_lflag &= ~ECHOE;
    tty.c_lflag &= ~ECHONL;

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);

    tty.c_oflag &= ~OPOST;
    tty.c_oflag &= ~ONLCR;

    // Set timeout
    tty.c_cc[VTIME] = 10; // 1 second timeout
    tty.c_cc[VMIN] = 0;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        perror("tcsetattr failed");
        return -1;
    }

    return 0;
}

// Send APDU and read response, returns 0 on success
int send_apdu(int fd, unsigned char *apdu, int apdu_len, unsigned char *response, int *resp_len) {
    // Write APDU to the device
    int bytes_written = write(fd, apdu, apdu_len);
    if (bytes_written != apdu_len) {
        perror("Failed to write full APDU");
        return -1;
    }

    // Read response (buffer size should match max expected response length)
    *resp_len = read(fd, response, 256);
    if (*resp_len < 0) {
        perror("Failed to read response");
        return -1;
    }

    // Check for success status word (90 00)
    if (*resp_len >= 2 && response[*resp_len - 2] == 0x90 && response[*resp_len - 1] == 0x00) {
        return 0;
    } else {
        fprintf(stderr, "Command failed with SW: %02X %02X\n", 
                response[*resp_len - 2], response[*resp_len - 1]);
        return -1;
    }
}

Execute SELECT + GET RESPONSE

Now use this helper to run your initial commands:

int main() {
    // Open serial device (adjust path to your reader, e.g., /dev/ttyUSB0)
    int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0) {
        perror("Failed to open serial device");
        return 1;
    }

    if (configure_serial(fd) != 0) {
        close(fd);
        return 1;
    }

    // Step 1: Send SELECT 1PAY.SYS.DDF01
    unsigned char select_apdu[] = {0x00, 0xA4, 0x04, 0x00, 0x0E, 
                                   0x31, 0x50, 0x41, 0x59, 0x2E, 
                                   0x53, 0x59, 0x53, 0x2E, 0x44, 
                                   0x44, 0x46, 0x30, 0x31};
    unsigned char select_resp[256];
    int select_resp_len;

    if (send_apdu(fd, select_apdu, sizeof(select_apdu), select_resp, &select_resp_len) != 0) {
        close(fd);
        return 1;
    }

    // Step 2: Send GET RESPONSE to retrieve full FCI data
    unsigned char get_resp_apdu[] = {0x00, 0xC0, 0x00, 0x00, 0x22};
    unsigned char fci_data[256];
    int fci_len;

    if (send_apdu(fd, get_resp_apdu, sizeof(get_resp_apdu), fci_data, &fci_len) != 0) {
        close(fd);
        return 1;
    }

    // The fci_data now contains your full response: 6f 20 e 84 ... 90 00
    // We'll parse this to get info needed for READ RECORD

Step 2: Implement READ RECORD for Specification Data

To get the PAN, we first need to read the relevant record from the card. The FCI response from the SELECT command includes a Short File Identifier (SFI, tag 0x88) which tells us which file to read.

Parse FCI for SFI

Add a simple TLV parser to extract the SFI:

// Find a TLV tag in the data (simplified for single-byte lengths)
unsigned char* find_tlv_tag(unsigned char *data, int data_len, unsigned char tag, int *value_len) {
    int i = 0;
    while (i < data_len - 2) { // Skip status word at end
        if (data[i] == tag) {
            i++;
            *value_len = data[i];
            i++;
            return &data[i];
        }
        // Skip current TLV element
        int len = data[i+1];
        i += 2 + len;
    }
    *value_len = 0;
    return NULL;
}

Then use this to get the SFI and send the READ RECORD command:

// Extract SFI from FCI (tag 0x88)
    int sfi_len;
    unsigned char* sfi_ptr = find_tlv_tag(fci_data, fci_len, 0x88, &sfi_len);
    if (!sfi_ptr || sfi_len != 1) {
        fprintf(stderr, "Failed to extract SFI from FCI\n");
        close(fd);
        return 1;
    }
    unsigned char sfi = sfi_ptr[0];

    // Step 3: Send READ RECORD (adjust record number as needed; start with 1)
    unsigned char read_record_apdu[] = {0x00, 0xB2, 0x01, (sfi << 3) | 0x04, 0x00};
    unsigned char record_data[256];
    int record_len;

    if (send_apdu(fd, read_record_apdu, sizeof(read_record_apdu), record_data, &record_len) != 0) {
        close(fd);
        return 1;
    }

Step 3: Extract the PAN Code

The PAN is stored under the 0x5A (Application Primary Account Number) TLV tag. Use our parser to extract it:

// Extract PAN from record data (tag 0x5A)
    int pan_len;
    unsigned char* pan_ptr = find_tlv_tag(record_data, record_len, 0x5A, &pan_len);
    if (!pan_ptr) {
        fprintf(stderr, "PAN tag (0x5A) not found in record\n");
        close(fd);
        return 1;
    }

    // Print the PAN (raw byte format)
    printf("PAN Code (Raw): ");
    for (int i = 0; i < pan_len; i++) {
        printf("%02X ", pan_ptr[i]);
    }
    printf("\n");

    // Optional: Convert BCD-encoded bytes to ASCII digits
    printf("PAN Code (ASCII): ");
    for (int i = 0; i < pan_len; i++) {
        printf("%c%c", 
               '0' + ((pan_ptr[i] >> 4) & 0x0F), 
               '0' + (pan_ptr[i] & 0x0F));
    }
    printf("\n");

    close(fd);
    return 0;
}

Key Notes for Production Use

  • TLV Parsing: The example parser only handles single-byte lengths. For cards with longer data elements (length > 0x7F), extend the parser to handle multi-byte length fields.
  • Error Handling: Add logic to handle status words like 61 XX (more data available) which requires additional GET RESPONSE commands.
  • Card Compatibility: Different card types (EMV, ISO 7816) may vary in record numbers or required APDU parameters. Refer to the EMV Book 3 specification for edge cases.
  • Security: Ensure your code handles sensitive PAN data securely (avoid logging, clear memory after use).

内容的提问来源于stack exchange,提问作者Valerio Colonnese

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:51:45