如何在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
- 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. - Transmit the Raw APDU: Use
transmitControlCommand()with your original APDU byte array. - Parse the Response: Convert the raw response bytes into a
ResponseAPDUfor 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_nostopcommand, 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
相关产品推荐
相关产品推荐

