【问题标题】:How to read file from smart card using java如何使用java从智能卡中读取文件
【发布时间】:2016-08-10 03:50:18
【问题描述】:

我是智能卡技术的新手。我想从智能卡读取文件。我正在使用 javax.smartcardio 来读取值。我开发了一些代码来将系统连接到卡(这很好)。我也有卡片 ATR 和其他详细信息。但没有获得与智能卡通信的 APDU 命令的适当帮助。卡在 APDU 命令中。

【问题讨论】:

  • 你做了什么?你期望会发生什么?发生了什么?
  • @Henry。完成:将智能连接到系统。期望:想与智能卡通信。 Happened : Throe 错误,p1 和 p2 参数错误。当我将命令传递给智能卡时。我对 apdu 命令一无所知。
  • 我认为您没有使用 java 卡库。您可能正在使用 javax.smarcardio。这是一个 JAVA 库。有关如何读取您的卡的更多信息,请阅读 ISO7816-4 和您的智能卡供应商文档
  • @PaulBastian 是的,我使用的是 javax.smarcardio。并且没有得到 APDU 命令的正确指导。
  • @PaulBastian,很抱歉询问您能否分享相同的链接。我正在阅读cardwerk.com/smartcards/…

标签: java smartcard javacard apdu


【解决方案1】:

首先:

“不是”所有的 Java 卡内部都有 MFDFEF!这些词依次代表主文件专用文件基本文件。它们是 ISO7816 定义的智能卡系统文件 (refer to part 4 of ISO7816) 的组件,因此您的卡可能有也可能没有这个文件系统。

典型的 java 卡有一个存储,您可以在其中安装您的小程序(在成功验证之后)并注册您的小程序的名称(我们称之为 AID,代表 Applet IDentifier 和它是一个 5 到 16 字节的十六进制序列)在卡的注册表(加载/安装的小程序和包含生命周期和特权的包的表 - read Global Platform Card Spec)中。

然后:

假设您的读卡器中插入了智能卡,并连接到您的计算机。您有不同的选择来在您的计算机和卡之间进行通信。

1-您可以使用可用的工具,例如您的读者工具(几乎所有读者都有一个工具)、PyAPDUTool 等。

2-您可以使用Javax.smartcardio 库来编写您的Java 程序以与智能卡进行通信:

import java.util.List;
import java.util.Scanner;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.xml.bind.DatatypeConverter;

public class TestPCSC {

    public static void main(String[] args) throws CardException {

        TerminalFactory tf = TerminalFactory.getDefault();
        List< CardTerminal> terminals = tf.terminals().list();
        System.out.println("Available Readers:");
        System.out.println(terminals + "\n");

        Scanner scanner = new Scanner(System.in);
        System.out.print("Which reader do you want to send your commands to? (0 or 1 or ...): ");
        String input = scanner.nextLine();
        int readerNum = Integer.parseInt(input);
        CardTerminal cardTerminal = (CardTerminal) terminals.get(readerNum);
        Card connection = cardTerminal.connect("DIRECT");
        CardChannel cardChannel = connection.getBasicChannel();

        System.out.println("Write your commands in Hex form, without '0x' or Space charaters.");
        System.out.println("\n---------------------------------------------------");
        System.out.println("Pseudo-APDU Mode:");
        System.out.println("---------------------------------------------------");
        while (true) {
            System.out.println("Pseudo-APDU command: (Enter 0 to send APDU command)");
            String cmd = scanner.nextLine();
            if (cmd.equals("0")) {
                break;
            }
            System.out.println("Command  : " + cmd);
            byte[] cmdArray = hexStringToByteArray(cmd);
            byte[] resp = connection.transmitControlCommand(CONTROL_CODE(), cmdArray);
            String hex = DatatypeConverter.printHexBinary(resp);
            System.out.println("Response : " + hex + "\n");
        }

        System.out.println("\n---------------------------------------------------");
        System.out.println("APDU Mode:");
        System.out.println("---------------------------------------------------");

        while (true) {
            System.out.println("APDU command: (Enter 0 to exit)");
            String cmd = scanner.nextLine();
            if (cmd.equals("0")) {
                break;
            }
            System.out.println("Command  : " + cmd);
            byte[] cmdArray = hexStringToByteArray(cmd);
            ResponseAPDU resp = cardChannel.transmit(new CommandAPDU(cmdArray));
            byte[] respB = resp.getBytes();
            String hex = DatatypeConverter.printHexBinary(respB);
            System.out.println("Response : " + hex + "\n");
        }

        connection.disconnect(true);

    }

    public static int CONTROL_CODE() {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.indexOf("windows") > -1) {
            /* Value used by both MS' CCID driver and SpringCard's CCID driver */
            return (0x31 << 16 | 3500 << 2);
        } else {
            /* Value used by PCSC-Lite */
            return 0x42000000 + 1;
        }

    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

}

3- 您可以使用PySCard 库来编写您的 Python 程序以与智能卡进行通信:

#Importing required modules.
import sys
import time
#--- You may need to change the following "line" based on your pyScard library installation path
sys.path.append("D:\\PythonX\\Lib\\site-packages")
from smartcard.scard import *
import smartcard.util
from smartcard.System import readers


#---This is the list of commands that we want to send device
cmds =[[,0xFF,0x69,0x44,0x42,0x05,0x68,0x92,0x00,0x04,0x00],]


#--- Let's to make a connection to the card reader
r=readers()
print "Available Readers :",r
print
target_reader = input("--- Select Reader (0, 1 , ...): ")
print

while(True):
    try:
        print "Using :",r[target_reader]
        reader = r[target_reader]
        connection=reader.createConnection()
        connection.connect()
        break
    except:
        print "--- Exception occured! (Wrong reader or No card present)"
        ans = raw_input("--- Try again? (0:Exit/1:Again/2:Change Reader)")
        if int(ans)==0:
            exit()
        elif int(ans)==2:
            target_reader = input("Select Reader (0, 1 , ...): ")

#--- An struct for APDU responses consist of Data, SW1 and SW2
class stru:
    def __init__(self):
        self.data = list()
        self.sw1 = 0
        self.sw2 = 0

resp = stru()

def send(cmds):
    for cmd in cmds:

        #--- Following 5 line added to have a good format of command in the output.
        temp = stru() ;
        temp.data[:]=cmd[:]
        temp.sw1=12
        temp.sw2=32
        modifyFormat(temp)
        print "req: ", temp.data

        resp.data,resp.sw1,resp.sw2 = connection.transmit(cmd)
        modifyFormat(resp)
        printResponse(resp)

def modifyFormat(resp):
    resp.sw1=hex(resp.sw1)
    resp.sw2=hex(resp.sw2)   
    if (len(resp.sw2)<4):
        resp.sw2=resp.sw2[0:2]+'0'+resp.sw2[2]
    for i in range(0,len(resp.data)):
        resp.data[i]=hex(resp.data[i])
        if (len(resp.data[i])<4):
            resp.data[i]=resp.data[i][0:2]+'0'+resp.data[i][2]

def printResponse(resp):
    print "res: ", resp.data,resp.sw1,resp.sw2


send(cmds)
connection.disconnect()

4- 您可以使用WinSCard 库在 C++/.Net 中编写程序(不确定)以与智能卡进行通信。

以上程序是向智能卡发送 APDU 命令的示例程序。但是这些命令本身取决于您的卡以及 [您的] 安装在卡上的小程序。

例如,假设您正在编写一个 AID = 01 02 03 04 05 00 的小程序,当它收到 00 00 00 00 00 作为 APDU 命令时返回 11 22 33 44 55。收到此回复(即11 22 33 44 55)所需的操作如下:

  1. 发送一个 SELECT APDU 命令,在其数据字段中包含您的小程序 AID。
  2. 00 00 00 00 00 发送到您的小程序。
  3. 您的小程序对上述命令的响应是预期的。

如果你的卡已经实现了ISO7816系统文件,你需要文件ID来选择它们。但命令本身是在 ISO7816-P4 中定义的。

即使您的卡没有实现 ISO7816 系统文件,您也可以编写一个小程序来像实现了 ISO7816-P 系统文件的智能卡一样运行(无论如何都不容易)。

由于 MF 的 ID 始终为3F00,尝试选择此文件,将显示您的卡是否实现了系统文件。

通常,当您的卡启动时,卡中名为 Card Manager 的强制实体会接收您的 APDU 命令。通过使用 SELECT APDU 命令,您请求卡管理器将下一个传入命令发送到选定的 APPLET

【讨论】:

  • 是否有命令查询当前选中的小程序是什么?
  • @typelogic 不,很遗憾没有。
  • @Oddomir 欢迎您。好吧,如果您有卡管理器(AKA 安全域)的相互验证密钥,那么您可以从卡管理器询问已安装的小程序和软件包的 AID 列表。为此,您可以在GlobalPlatformPro 工具中使用gp -l
  • @Oddomir 但是如果您没有卡管理器的相互身份验证密钥(AKA GlobalPlatform Keys),您需要进行暴力搜索(发送大量带有所有可能 AID 的 SELECT APDU 命令)。但是除非您的卡确实支持“部分 APDU 选择”功能,否则暴力破解在实践中是不可行的(我们有 2^40 + 2^48 + 2^56 + ... + 2^128 个不同的可能 AID!)。如果您的卡支持部分 APDU 选择,那么您可以在一个或几个小时内找到所有 AID。通过部分 APDU 选择 Card Manger 在 thr APDU 命令中选择与 AID 的子字符串匹配的第一个小程序。
  • @Oddomir 所以你可以发送00 A4 04 00 01 00 到卡,如果卡中有任何AID 以00 开头的小程序,你会收到9000 状态字。然后通过从00 A4 04 00 02 00 0000 A4 04 00 02 00 FF 发送APDU 命令来获取AID 的第二个字节。以此类推,直到小程序 AID 的最后一个字节。这有点棘手,所以如果我的解释含糊不清,请毫不犹豫地提出更多问题。您也可以发布一个新的 SO 问题,我会在那里提供答案。
【解决方案2】:

市场上有各种各样的智能卡,每种都使用不同的结构来保存数据。文件结构在 ISO 7816-4:组织、安全和交换命令中定义

您可以查看link1Link2 了解有关智能卡文件结构的更多信息。

是否使用这种文件结构将数据保存在芯片内是卡片个人化实体的选择。印度标准SCOSTA 完全符合 ISO 7816 标准,这意味着任何符合 SCOSTA 标准的产品都将使用 ISO 7816 -4(MF、DF、EF)中定义的结构。

在向它发送任何命令之前,您应该知道卡的结构。比如选择 MF -> 选择 DF -> 选择 EF -> 读取记录命令。

在 java 卡(智能卡)的情况下,并不总是创建文件结构来保存数据,它可以使用数组 [即Persistent memory] ​​并在操作期间为其赋值。此值驻留在智能卡生命周期中。想要获取值只需发送一个适当的定义命令,卡就会从数组中返回值。就这些。

这意味着我们可以说所有智能卡都不遵循文件结构规则

读取任何智能卡都有一些预定义的规则,如果卡是结构明智的,我们应该知道结构,否则我们可以得到Error status word

如果谈论命令,它也被定义为Here -Smart Card Command Rules,您可以阅读这里以增强您对智能卡命令的了解,以便您可以发送正确的命令。

But not getting proper help for APDU command for communication with Smart card. Stuck in APDU command.

在向其发送任何命令之前,您应该在此处了解有关您正在使用的智能卡的更多信息。最好分享命令响应以检查确切的问题。 javax.smartcardio 是非常好的与智能卡通信的 API,还有各种示例 shared 可以帮助您编写代码来访问任何智能卡。

希望在了解您正在使用的智能卡的详细信息之后,一旦您构建了适当的命令,您就不会遇到任何错误。希望能帮助到你。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-11
    相关资源
    最近更新 更多