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("=> " + 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("<= " + 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("=> " + 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("<= " + 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 < 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]
- => 00A404000B0102030405060708090000
- <= 9000
- [TESTE BYTES]
- => 00000000060A0B0C0D0E0F
- <= 00000000060A0B0C0D0E0F9000
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.
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.
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" 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 terminallançará uma exceção.
Na linha 47 requisitamos o ATR (Answer To Reset) que se trata de um objeto ATRretornado 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 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)
0 comments:
Post a Comment