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

如何在Java中直接向智能卡发送原始APDU报文?

How to Send Raw APDU to Smart Card in Java (Avoid CLA Adjustment)

You're exactly right about the root cause: Java's CardChannel.transmit() method automatically modifies the CLA byte of your APDU to match the channel number of the CardChannel. Since your smart card expects the fixed CLA 0x80 (as confirmed by your successful gpshell test), this adjustment leads to the 0x6E00 (Class not supported) error.

To send the exact APDU without any modifications, you need to bypass the CardChannel and use Card.transmitControlCommand() instead. This method lets you send raw bytes directly to the card via the PC/SC interface, skipping any automatic CLA adjustments.

Step-by-Step Solution

  1. Define the PC/SC Control Code: For sending raw APDUs, we use the control code corresponding to SCARD_CTL_CODE(3500), which varies slightly between Windows and non-Windows systems.
  2. Transmit the Raw APDU: Use transmitControlCommand() with your original APDU byte array.
  3. Parse the Response: Convert the raw response bytes into a ResponseAPDU for easy handling.

Modified Java Code

import javax.smartcardio.*;
import java.util.List;

public class SmartCardRawSender {

    void testCard() {
        TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals;
        try {
            terminals = factory.terminals().list();
        } catch (CardException ex) {
            ex.printStackTrace();
            return;
        }

        if (terminals.isEmpty()) {
            System.out.println("No card terminals detected.");
            return;
        }

        CardTerminal cardTerm = terminals.get(0);
        Card card;
        try {
            // Connect using T=0 protocol, matching your gpshell test
            card = cardTerm.connect("T=0");
        } catch (CardException ex) {
            ex.printStackTrace();
            return;
        }

        try {
            // Your original APDU bytes (exact match to gpshell command)
            byte[] apduBytes = new byte[]{
                (byte)0x80,(byte)0x2A,(byte)0x80,(byte)0xB0,(byte)0x5F,(byte)0x87,(byte)0x41,(byte)0x00,
                (byte)0x61,(byte)0xDA,(byte)0x7A,(byte)0x1E,(byte)0x2F,(byte)0x02,(byte)0x60,(byte)0x2A,
                (byte)0x25,(byte)0x50,(byte)0x63,(byte)0x71,(byte)0x3F,(byte)0xD6,(byte)0x57,(byte)0x79,
                (byte)0x70,(byte)0x63,(byte)0xC6,(byte)0xC7,(byte)0xAC,(byte)0xC1,(byte)0x20,(byte)0x72,
                (byte)0xF5,(byte)0x34,(byte)0x0B,(byte)0x1C,(byte)0x01,(byte)0x26,(byte)0xA6,(byte)0x16,
                (byte)0xBC,(byte)0x66,(byte)0xC6,(byte)0x5F,(byte)0x49,(byte)0x13,(byte)0x2E,(byte)0xED,
                (byte)0x10,(byte)0xAE,(byte)0x07,(byte)0x1D,(byte)0xC6,(byte)0x61,(byte)0xAA,(byte)0x13,
                (byte)0x33,(byte)0xBE,(byte)0xA9,(byte)0x2F,(byte)0x67,(byte)0xA5,(byte)0xBE,(byte)0xFF,
                (byte)0xDF,(byte)0xA7,(byte)0xA0,(byte)0xF3,(byte)0x1F,(byte)0xC8,(byte)0xB3,(byte)0xD9,
                (byte)0x81,(byte)0x10,(byte)0x5D,(byte)0x1E,(byte)0xF9,(byte)0x6B,(byte)0x00,(byte)0x0F,
                (byte)0xD9,(byte)0x00,(byte)0x98,(byte)0xC7,(byte)0xFF,(byte)0x03,(byte)0x19,(byte)0x06,
                (byte)0xA1,(byte)0x01,(byte)0x8E,(byte)0x08,(byte)0x97,(byte)0xC5,(byte)0xDA,(byte)0x58,
                (byte)0x00,(byte)0x59,(byte)0xAD,(byte)0x29,(byte)0x00
            };

            // Get the correct control code for raw APDU transmission
            int controlCode = getRawApduControlCode();
            // Send the raw APDU directly to the card
            byte[] responseBytes = card.transmitControlCommand(controlCode, apduBytes);

            // Parse the response into a ResponseAPDU object
            ResponseAPDU response = new ResponseAPDU(responseBytes);
            System.out.println("Raw Response: " + bytesToHex(responseBytes));
            System.out.println("Status Word: 0x" + Integer.toHexString(response.getSW()).toUpperCase());

        } catch (CardException ex) {
            ex.printStackTrace();
        } finally {
            // Always disconnect the card when done
            try {
                card.disconnect(false);
            } catch (CardException e) {
                e.printStackTrace();
            }
        }
    }

    // Helper method to get the PC/SC control code for raw APDU transmission
    private int getRawApduControlCode() {
        String os = System.getProperty("os.name").toLowerCase();
        // SCARD_CTL_CODE(3500) - standard for sending raw APDUs
        if (os.contains("win")) {
            return 0x00000000 | (3500 << 2);
        } else {
            return 0x40000000 | (3500 << 2);
        }
    }

    // Helper method to convert byte arrays to hex strings for debugging
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}

Why This Works

  • transmitControlCommand() sends the exact byte array you provide to the smart card, with no modifications to the CLA or any other part of the APDU.
  • This matches the behavior of gpshell's send_apdu_nostop command, which also sends the raw APDU without adjustments.
  • The control code SCARD_CTL_CODE(3500) is a standard PC/SC command for bypassing the usual channel handling and sending raw data.

内容的提问来源于stack exchange,提问作者Rudy Barbieri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:18:31