Acessando Smart Cards Usando Java
Autor: Ricardo Massao | Publicado: 2010-12-19T18:16:00-08:00

Nesse artigo demonstrarei o quanto é simples acessar Smart Cards utilizando o Java SE.O material necessário:Cartão Java Card de Desenvolvimento.
Leitor/Gravador padrão PC/SC ISO7816.
IDE Netbeans / Eclipse.
Ter completado com sucesso os 3 Artigos Hello World Java Card. 
Como já havia dito tenho uma preferência pelo IDE Netbeans, mas você poderá utilizar tranquilamente o Eclipse se desejar.
Sem muitos rodeios vamos ao que interessa o Código:
import java.util.List;
import javax.smartcardio.*;

public class SmartCardTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //"Fabrica" de Terminais PC/SC
        TerminalFactory factory;
        //Lista de Leitores PC/SC
        List terminals;
        //Terminal PC/SC
        CardTerminal terminal;
        //Smart Card
        Card card;
        //Smart Card ATR
        ATR cardATR;
        //Canal de Comunicação com o Smart Card
        CardChannel cardChannel;
        //APDU de Comando
        CommandAPDU commandAPDU;
        //APDU de Resposta
        ResponseAPDU responseAPDU;
        //Buffer de Auxilio
        byte[] buffer;

        try {
            //Adquire Fabrica de Leitores
            factory = TerminalFactory.getDefault();

            //Adquire Lista de Leitores PC/SC no Sistema
            terminals = factory.terminals().list();
            System.out.println("Lista : " + terminals);

            //Adquire Primeiro Terminal da Lista
            terminal = (CardTerminal)terminals.get(0);
            System.out.println("Terminal Selecionado: "
                    + terminal.getName());

            //Estabelece Conexão com o Cartão na Leitora
            card = terminal.connect("*");
            System.out.println("card: " + card);

            //Adquire ATR do Cartão
            cardATR = card.getATR();
            buffer = cardATR.getBytes();
            System.out.println("ATR : "
                    + formatBuffer(buffer, buffer.length));

            //Adquire Canal de Comunicação
            cardChannel = card.getBasicChannel();

            //AID do HelloWorld
            buffer = new byte[]{0x01, 0x02, 0x03, 0x04,
                        0x05, 0x06, 0x07, 0x08,
                        0x09, 0x00, 0x00};

            //Monta APDU de Envio
            commandAPDU = new CommandAPDU(
                    0x00,       //CLA
                    0xA4,       //INS - SELECT
                    0x04,       //P1
                    0x00,       //P2
                    buffer);    //AID

            //Imprime Comando
            System.out.println("\n[SELECT COMMAND]");
            System.out.println("=&gt; " + formatBuffer(
                    commandAPDU.getBytes(),
                    commandAPDU.getBytes().length));

            //Trasnmite e Recebe
            responseAPDU = cardChannel.transmit(commandAPDU);

            //Verifica Resposta
            if (responseAPDU.getSW() != 0x9000) {
                throw new Exception("Falha ao Selecionar : "
                        + String.format("0x%04X",
                          responseAPDU.getSW()));
            }

            //Imprime Resposta
            System.out.println("&lt;= " + formatBuffer(
                    responseAPDU.getBytes(),
                    responseAPDU.getBytes().length));

            //Bytes de Testes
            buffer =
                new byte[]{0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

            //Monta APDU de Envio
            commandAPDU = new CommandAPDU(
                    0x00, //CLA
                    0x00, //INS
                    0x00, //P1
                    0x00, //P2
                    buffer); //Teste

            //Imprime Comando
            System.out.println("\n[TESTE BYTES]");
            System.out.println("=&gt; " + formatBuffer(
                    commandAPDU.getBytes(),
                    commandAPDU.getBytes().length));

            //Trasnmite e Recebe
            responseAPDU = cardChannel.transmit(commandAPDU);

            //Verifica Resposta
            if (responseAPDU.getSW() != 0x9000) {
                throw new Exception("Falha ao Selecionar : "
                        + String.format("0x%04X",
                          responseAPDU.getSW()));
            }

            //Imprime Resposta
            System.out.println("&lt;= " + formatBuffer(
                    responseAPDU.getBytes(),
                    responseAPDU.getBytes().length));

        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }

    /**
     * Converte Buffer bytes para String
     * @param buffer
     * @param length
     * @return String
     */
    public static String formatBuffer(byte[] buffer,int length)
    {
        StringBuilder strBuff = new StringBuilder("");
        for (int i = 0; i &lt; length; i++) {
            strBuff.append(String.format("%02X", buffer[i]));
        }
        return strBuff.toString();
    }
}O Resultado ao executar deve ser similar a isso:Lista : [PC/SC terminal ACS ACR38U 0]
Terminal Selecionado: ACS ACR38U 0
card: PC/SC card in ACS ACR38U 0, protocol T=1, state OK
ATR : 3BF81300008131FE454A434F5076323431B7

[SELECT COMMAND]
=&gt; 00A404000B0102030405060708090000
&lt;= 9000

[TESTE BYTES]
=&gt; 00000000060A0B0C0D0E0F
&lt;= 00000000060A0B0C0D0E0F9000Vamos a analise do código:&nbsp;
A partir do Java 6, a Sun disponibilizou uma API intitulada "Java Smart Card I/O" que tem como finalidade prover acesso a Smart Cards.

Na linha 11 criamos uma variável factory que instância um objeto TerminalFactory, esse objeto implementa uma pilha de leitores que pode variar conforme a implementação, no nosso caso usaremos o padrão getDefault() que implementa leitores PC/SC, lembrem-se existem diversos leitores com diversos tipos de padrões.

Na linha 13 criamos uma lista terminals que armazena um ou mais terminais (leitor PC/SC) plugados no sistema, que é retornado pelo factory, caso não exista nenhum leitor plugado factory retornará uma exceção.

Na linha 15 criamos uma variável terminal que instância um objeto CardTerminal, esse objeto representa um terminal (leitor PC/SC).

Na linha 17 criamos uma variável card que instância um objeto Card, esse objeto representa o cartão que se encontra inserido no Leitor representado por terminal.

Na linha 19 criamos uma variável cardATR que instância um objeto ATR, esse objeto representa o ATR (Answer to Reset) retornado pelo cartão representado por card.&nbsp;

Na linha 21 criamos uma variável cardChannel que instância um objeto CardChannel, esse objeto provê o canal de comunicação que nossa aplicação utilizará para trocar APDUs com o cartão, esse canal é retornado pelo cartão representado por card.&nbsp;

Na linha 23 criamos uma variável commandAPDU que instância um objeto CommandAPDU, esse objeto representa um APDU de envio do host para o cartão através do canal de comunicação representado por cardChannel.

Na linha 25 criamos uma variável responseAPDU que instância um objeto ResponseAPDU, esse objeto representa um APDU de resposta do cartão para o host e é retornado através do canal de comunicação representado por cardChannel.

Na linha 31 iniciamos factory com uma "Fábrica de Terminais"&nbsp; padrão que no caso é o PC/SC.

Na linha 34 preenchemos a lista terminals com terminais "fabricado" por factory, caso não exista nenhum leitor PC/SC plugado no sistema ou ocorra um erro factory lançará uma exceção, do contrario a lista possuirá ao menos um leitor.

Na linha 38 iniciamos terminal instanciando o primeiro leitor contido na lista terminals.

Na linha 43 estabelecemos conexão com o cartão presente na leitora através do comando terminal.connect("*") o parâmetro * informa para auto detectar o protocolo do cartão esse parâmetro também pode ser explicitamente informado como "T=0", "T=1" ou "T=CL", em caso de sucesso um objeto Card é retornado e card representará o cartão conectado na leitora, caso contrário terminal lançará uma exceção.

Na linha 47 requisitamos o ATR (Answer To Reset) que se trata de um objeto ATR retornado pelo método getATR da instância card.

Na linha 53 abrimos um canal de comunicação entre Host e Cartão, através da instância cardChannel retornado pelo método getBasicChannel de card.

Na linha 61 criamos um APDU a ser enviado para o cartão através do objeto CommandAPDU, esse APDU trata-se do comando de select padrão ISO7816 do nosso applet hello world instalado no cartão.

Na linha 75 transmitimos o APDU para o cartão através do método transmit de cardChannel, esse método sempre retorna um objeto ResponseAPDU, que sempre possui a resposta (Status World) da operação do comando enviado e ocasionalmente dados retornados pelo cartão, em caso de erros durante a transmissão o método lançará uma exceção.

Na linha 94 transmitimos o APDU de teste para o Applet do cartão o qual responde uma cópia exata do APDU de envio seguido do Status World 0x9000(OK).

Bem como podem ver é extremamente simples acessar&nbsp;Smart Cards utilizando a tecnologia Java SE.
Coloco a disposição para download o projeto escrito no NetBeans, que pode ser tranquilamente compilado em outras IDE's como Eclipse e até no Prompt de Comando (DOS) pois o exemplo é Pure Java não dependendo de bibliotecas externas:

Smart Card Test (Link)

Caso queira se aprofundar mais na API Java Smart Card IO segue o link para estudos:

Java Smart Card IO API (Link)

Em artigos futuros, postarei um artigo idêntico porém escrito em C/C++, nesse caso não é tão simples como no caso do Java porém ainda assim é muito similar.
 
Um grande abraço a todos e até a próxima!
&nbsp;