【问题标题】:How to read a smart card/ microprocessor card using a smart card reader in android programmatically如何以编程方式在 android 中使用智能卡读卡器读取智能卡/微处理器卡
【发布时间】:2017-12-19 08:53:58
【问题描述】:

所以最近我一直在使用包含一些信息的智能卡,我在这里想要实现的是通过任何 Android 智能手机使用智能卡读卡器从这些智能卡中获取这些数据。 我一直在使用HID OMNIKEY 3021 USB 智能卡读卡器,它可以读取这些卡(我知道这个读卡器可以通过 Windows 应用程序使用这些卡,因为我已经亲自测试过)

现在 Android 提供了USB Host,只要 Android 智能手机支持,它就可以读取任何 USB 主机。

我正在尝试使用 USB Host 提供的这些类来访问此卡内的数据。

我检测任何 USB 主机的代码:

private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);

IntentFilter attachedFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
registerReceiver(mUsbAttachedReceiver, attachedFilter);

private final BroadcastReceiver mUsbAttachedReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Utils.writeStringToTextFile("\n1 .Get an action : " + action, FileName);
        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
            synchronized (this) {
                device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (device != null) {
                    showToast("Plugged In");
                    mUsbManager.requestPermission(device, mPermissionIntent);
                }
            }
        } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                showToast("Plugged Out");
                // call your method that cleans up and closes communication with the device
            }
        }
    }
};

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if (device != null) {
                        //call method to set up device communication
                        Utils.writeStringToTextFile("2 .Get an action : " + action + "\nDevice is : " + device, FileName);
                        showToast("Permission Granted for device");

                        Handler h = new Handler();
                        h.postDelayed(run, 1000);

                    }
                } else {
                    showToast("Permission denied for device" + device);
                }
            }
        }
    }
};

一切正常,因为我得到了UsbDevice device,它给出了设备的信息,例如:

Device is : UsbDevice[mName=/dev/bus/usb/001/002,mVendorId=1899,mProductId=12322,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=OMNIKEY AG,mProductName=Smart Card Reader USB,mVersion=2.0,mSerialNumber=null,mConfigurations=[
UsbConfiguration[mId=1,mName=CCID,mAttributes=160,mMaxPower=50,mInterfaces=[
UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=11,mSubclass=0,mProtocol=0,mEndpoints=[
UsbEndpoint[mAddress=131,mAttributes=3,mMaxPacketSize=8,mInterval=24]
UsbEndpoint[mAddress=132,mAttributes=2,mMaxPacketSize=64,mInterval=0]
UsbEndpoint[mAddress=5,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]

现在我正在尝试使用此 UsbDevice device 从卡中获取数据和详细信息,但我没有成功,也找不到任何有用的帖子。

我知道我必须使用 UsbInterfaceUsbEndpointUsbDeviceConnection 从卡中获取我想要的东西,但我无法这样做。

另外,我找不到任何样品或类似的东西。 谁能指出我正确的方向?

很抱歉,很长的帖子也提前感谢:)

编辑: 感谢Mr. Michael Roland,我能够阅读有关 CCID 的信息,因为阅读器设备通过 USB 接口说出 CCID。

所以我使用了以下代码:

        UsbDeviceConnection connection = mUsbManager.openDevice(device);
        UsbEndpoint epOut = null, epIn = null;

        for (int i = 0; i < device.getInterfaceCount(); i++) {
            UsbInterface usbInterface = device.getInterface(i);
            connection.claimInterface(usbInterface, true);

            for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
                UsbEndpoint ep = usbInterface.getEndpoint(j);
                showToast("Endpoint is : " + ep.toString() + " endpoint's type : " + ep.getType() + " endpoint's direction : " + ep.getDirection());
                Log.d(" ", "EP " + i + ": " + ep.getType());
                if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                    if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
                        epOut = ep;

                    } else if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
                        epIn = ep;
                    }

                }
            }

            int dataTransferred = 0;
            byte[] PC_to_RDR_IccPowerOn = hexStringToByteArray("62" + "00000000" + "00" + "00" + "00" + "0000");

            if (epOut != null) {
                //Firstly send Power in on Bulk OUT endpoint
                dataTransferred = connection.bulkTransfer(epOut, PC_to_RDR_IccPowerOn, PC_to_RDR_IccPowerOn.length, TIMEOUT);
            }

            StringBuilder result = new StringBuilder();

            if (epIn != null) {
                final byte[] RDR_to_PC_DataBlock = new byte[epIn.getMaxPacketSize()];
                result = new StringBuilder();
                //Secondly send Power out on Bulk OUT endpoint
                dataTransferred = connection.bulkTransfer(epIn, RDR_to_PC_DataBlock, RDR_to_PC_DataBlock.length, TIMEOUT);
                for (byte bb : RDR_to_PC_DataBlock) {
                    result.append(String.format(" %02X ", bb));
                }

                if (dataTransferred > 0) {
                    Utils.writeStringToTextFile("\n2nd buffer received was : " + result.toString(), "Card_communication_data.txt");
                    String s1 = Arrays.toString(RDR_to_PC_DataBlock);
                    String s2 = new String(RDR_to_PC_DataBlock);
                    showToast("received - " + s1 + " - " + s2);
                } else {
                    showToast("received length at 2nd buffer transfer was " + dataTransferred);
                }
            }
        }

我收到了80 13 00 00 00 00 00 00 00 00 3B 9A 96 C0 10 31 FE 5D 00 64 05 7B 01 02 31 80 90 00 76 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 但我仍然不确定如何处理数据字段:ATR 或如何为PC_to_RDR_XfrBlock 命令形成Command APDU..

我认为我应该这样做

发送封装到 PC_to_RDR_XfrBlock 命令中的命令 APDU

现在;谁能帮我解决这个问题?

编辑 2: 我弄清楚了 ATR 的含义以及如何形成命令 APDU。

但现在我应该切换协议

默认协议是 T=0。要设置 T=1 协议,设备必须向卡发送 PTS(也称为 PPS) 由于 T=0 和 T=1 协议对卡片都是强制性的,因此协议切换的基本 PTS 对卡片是强制性的。 卡片。 如 ISO/IEC 7816-3 中所述,PTS 可用于切换到比以下建议的默认波特率更高的波特率 ATR 中的卡(如果有)(TA(1) 字节)。

我不确定这意味着什么以及如何实现!

【问题讨论】:

    标签: java android smartcard smartcard-reader


    【解决方案1】:

    由于没有适当的指南或任何示例来列出人们可以遵循的基本步骤,所以这就是我设法沟通的方式(这更像是一个新手指南,如果我错了,请纠正我): 首先使用USB Host API,我能够通过智能卡读卡器连接到智能卡。

    关于连接,这里有一个sn-p来帮助你理解:

        //Allows you to enumerate and communicate with connected USB devices.
        UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        //Explicitly asking for permission
        final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
    
        UsbDevice device = deviceList.get("//the device you want to work with");
        if (device != null) {
            mUsbManager.requestPermission(device, mPermissionIntent);
        }
    

    现在您必须了解,在 java 中,通信是使用 package javax.smarcard 进行的,这对 Android 不可用,因此请使用 look here 了解如何通信或发送/接收 APDU(智能卡命令) .

    现在就像上面提到的答案中所说的那样

    您不能简单地通过批量输出端点发送 APDU(智能卡命令)并期望通过批量输入端点接收响应 APDU。

    要获取端点,请参见下面的代码 sn-p:

    UsbEndpoint epOut = null, epIn = null;
    UsbInterface usbInterface;
    
    UsbDeviceConnection connection = mUsbManager.openDevice(device);
    
            for (int i = 0; i < device.getInterfaceCount(); i++) {
                usbInterface = device.getInterface(i);
                connection.claimInterface(usbInterface, true);
    
                for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
                    UsbEndpoint ep = usbInterface.getEndpoint(j);
    
                    if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
                            // from host to device
                            epOut = ep;
    
                        } else if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
                            // from device to host
                            epIn = ep;
                        }
                    }
                }
            }
    

    现在您有了批量输入和批量输出端点来发送和接收 APDU 命令和 APDU 响应块:

    发送命令见下方代码sn-p:

    public void write(UsbDeviceConnection connection, UsbEndpoint epOut, byte[] command) {
        result = new StringBuilder();
        connection.bulkTransfer(epOut, command, command.length, TIMEOUT);
        //For Printing logs you can use result variable
        for (byte bb : command) {
            result.append(String.format(" %02X ", bb));
        }
    }
    

    对于接收/读取响应,请参见下面的代码 sn-p:

    public int read(UsbDeviceConnection connection, UsbEndpoint epIn) {
    result = new StringBuilder();
    final byte[] buffer = new byte[epIn.getMaxPacketSize()];
    int byteCount = 0;
    byteCount = connection.bulkTransfer(epIn, buffer, buffer.length, TIMEOUT);
    
        //For Printing logs you can use result variable
        if (byteCount >= 0) {
            for (byte bb : buffer) {
                result.append(String.format(" %02X ", bb));
            }
    
            //Buffer received was : result.toString()
        } else {
            //Something went wrong as count was : " + byteCount
        }
    
        return byteCount;
    }
    

    现在,如果您看到this answer here,第一个要发送的命令是:

    PC_to_RDR_IccPowerOn命令激活卡。

    您可以通过阅读USB Device Class Specifications doc here 的第 6.1.1 节来创建它。

    现在让我们以这个命令为例,如下所示:62000000000000000000 你如何发送这个是:

    write(connection, epOut, "62000000000000000000");
    

    现在,在您成功发送 APDU 命令后,您可以使用以下命令读取响应:

    read(connection, epIn);
    

    并收到类似的东西

    80 18000000 00 00 00 00 00 3BBF11008131FE45455041000000000000000000000000F1

    现在在此处代码中收到的响应将位于上述代码中read() 方法的result 变量中,您可以使用该变量从相同的RDR_to_PC_DataBlock 获取Data field: ATR

    你必须知道/阅读如何阅读这个ATR,它可以用来检测卡的类型和其他东西,比如它是使用T0还是T1协议,然后是什么TA(1 ) 可以告诉FI Index into clock conversion factor tableDI Index into Baud rate adjustment factor table等等

    Check this website 用于解析ATR

    现在如果你想切换协议说T0T1即发送PPS/PTS或者你想设置任何参数,那么你可以使用PC_to_RDR_SetParameters命令(第6.1节.7 document)。

    在我的例子中,PPS/PTST0 切换到T1 的示例是:"61 00000007 00 00 01 0000 9610005D00FE00"

    这将给出一些结果,例如:"82 07000000 00 00 00 00 01 96 10 00 5D 00 FE 00"。您可以通过 RDR_to_PC_Parametersdocument 的第 6.2.3 节)进行检查

    有关此命令的详细信息,请参阅CCID protocol Document 的第 6.1.7 节。 我能够使用从ATR 块收到的详细信息来形成此命令,然后使用write() 方法发送命令,然后使用read() 方法读取响应。

    对于选择任何文件或发送任何命令,您可以使用 PC_to_RDR_XfrBlock 使用 write() 方法发送,然后使用代码中的 read() 方法接收响应。您也可以在 read() 方法中更改要读取的 btyes 的数量。

    记得使用线程进行交流,也可以read here 获取更多提示。

    【讨论】:

    • 一篇有用的帖子。谢谢。我也在尝试使用 OmniKey。我只对当人们在读卡器上扫描他们的卡时收到通知感兴趣。我已关注您的实施。但是,我无法阅读任何内容。结果 = -1。还有一件事,在读卡器上尝试换卡几次后,读卡器停止响应交换(没有哔声也没有蓝光)。请问有办法吗?谢谢
    • @AbdulkarimKanaan 我不确定交换问题,对于通知检查 android 是否为此发出任何系统广播,如果没有,您将不得不使用 USB 管理器并继续查询 USBManager对于是否存在任何设备(检查答案的第一部分)。
    • 你认为我需要使用线程来阅读吗?
    • 还有一点,写命令write(connection, epOut, "62000000000000000000")返回36 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
    • @AbdulkarimKanaan 自从我从事这项工作以来已经有一段时间了,所以我不确定确切的命令.. 请查看文档 CCID 协议
    【解决方案2】:

    典型的 USB 智能卡读卡器实现 USB CCID device class specification。因此,您需要在应用程序中实现该协议,以便与读卡器(和卡)进行通信。请参阅Communicate with smartcard reader through Android USB host 了解如何实现它的(部分工作的)起点。

    【讨论】:

    • 感谢您为我指明了正确的方向,但是很难弄清楚将哪个命令用于什么目的。 PTS/PPS - 6.1.7 PC_to_RDR_SetParameters 的示例将在 CCID 规范文档中引用
    • @shadygoneinsane 如果你有一个可行的解决方案,如果你能在自己的答案中分享它会很棒。
    • 这将是一个很长的答案,我会尝试在这里发布。
    猜你喜欢
    • 1970-01-01
    • 2013-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多