Sunday, April 5, 2020

PCSC sample in Java using intarsys smartcard-io

Here is a new PCSC sample in Java language I promised in PC/SC sample in different languages.

We already saw in a previous article "PCSC sample in Java" that the standard JVM includes javax.smartcardio to access smart cards. We will now see another Java wrapper.

intarsys smartcard-io

The project intarsys smartcard-io is hosted at https://github.com/intarsys/smartcard-io.
The licence is 3-Clause BSD.

Installation

Installation is easy. Just get the provided is-smartcard-io.jar files from deploy/ directory and the 3 runtime dependencies from lib/ directory.

I have not tried to rebuild the library from source.

Source code

The API is easy to use since it is a direct mapping to the PC/SC WinScard API.


package com.company;

import de.intarsys.security.smartcard.pcsc.IPCSCCardReader;
import de.intarsys.security.smartcard.pcsc.IPCSCConnection;
import de.intarsys.security.smartcard.pcsc.IPCSCContext;
import de.intarsys.security.smartcard.pcsc.PCSCContextFactory;
import de.intarsys.security.smartcard.pcsc.nativec._IPCSC;

import java.util.List;

public class Blog {
    public static void main(String[] args) {
        try {
            /* Establish context */
            IPCSCContext context = PCSCContextFactory.get().establishContext();

            /* Display the list of readers */
            List<IPCSCCardReader> readers = context.listReaders();
            for (IPCSCCardReader reader : readers) {
                System.out.println("found " + reader + " named " + reader.getName());
            }

            /* Use the first reader */
            IPCSCCardReader reader = readers.get(0);

            /* Connect to the card */
            IPCSCConnection connection = context.connect(
                    reader.getName(), _IPCSC.SCARD_SHARE_SHARED,
                    _IPCSC.SCARD_PROTOCOL_Tx);

            /* Send Select Applet command */
            byte[] select = {0x00, (byte)0xA4, 0x04, 0x00, 10, (byte)0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01};
            byte[] answer;
            answer = connection.transmit(select, 0, select.length, 256, false);
            System.out.println("answer: " + answer.length + " bytes");
            for (int i=0; i<answer.length; i++) {
                System.out.print(String.format("%02X ", answer[i]));
            }
            System.out.println();

            /* Send test command */
            byte[] command = {0x00, 0x00, 0x00, 0x00};
            answer = connection.transmit(command, 0, command.length, 256, false);
            System.out.println("answer: " + answer.length + " bytes");
            for (int i=0; i<answer.length; i++) {
                System.out.print(String.format("%02X ", answer[i]));
            }
            System.out.println();
            for (int i=0; i<answer.length - 2; i++) {
                System.out.print((char)answer[i]);
            }
            System.out.println();

            /* Disconnect */
            connection.disconnect(_IPCSC.SCARD_LEAVE_CARD);

            /* Release context */
            context.dispose();
        } catch(Exception e) {
            System.out.println("Ouch: " + e.toString());
        }
    }
}

Output

found pcscreader 0 named Gemalto PC Twin Reader
found pcscreader 1 named Cherry KC 1000 SC Z
answer: 2 bytes
90 00 
answer: 14 bytes
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 
Hello world!

High level API

Intarsys smartcard-io also provides a higher level API compatible with javax.smartcardio.
As documented in the project README:
javax.smartcardio Provider

The library comes with an alternative javax.smartcardio provider. There are a couple of things to consider:

Intended differences
  • dedicated PCSC context for terminals, terminal and card
  • waitForChange(timeout) semantics improved(?), state change is reset even in case of timeout
  • reader insertion is handled, too
  • no finalizer for card!

javax.smartcardio bug/limitation

One big problem with the SUN/Oracle implementation of javax.smartcardio is that only one PC/SC context is created.
It works fine with a mono-threaded application. But it is very problematic with a multi-threaded application if you want to access different readers at the same time from different threads.

As documented in pcsc-lite SCardEstablishContext():
Each thread of an application shall use its own SCARDCONTEXT

This is because only one thread can use a SCARDCONTEXT at the same time. So 2 SCardTransmit() calls on 2 different cards from 2 threads but using the same SCARDCONTEXT will be serialized by pcsc-lite. This is NOT what you want if you intent to execute the 2 card commands at the same time.

The problem is not present on Windows because the Microsoft WinSCard implement is different.
I don't know how javax.smartcardio behaves on macOS. The WinSCard on macOS has been rewritten using the low level API CryptoTokenKit since macOS Yosemite 10.10 in 2014 (see "OS X Yosemite and smart cards status") and is no more using pcsc-lite.

Conclusion

This wrapper is easy to use and provides access to all the PC/SC functions.