ACESSANDO SMART CARD USANDO JAVA

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:
  1. import java.util.List;  
  2. import javax.smartcardio.*;  
  3.   
  4. public class SmartCardTest {  
  5.   
  6.     /** 
  7.      * @param args 
  8.      */  
  9.     public static void main(String[] args) {  
  10.         //"Fabrica" de Terminais PC/SC  
  11.         TerminalFactory factory;  
  12.         //Lista de Leitores PC/SC  
  13.         List terminals;  
  14.         //Terminal PC/SC  
  15.         CardTerminal terminal;  
  16.         //Smart Card  
  17.         Card card;  
  18.         //Smart Card ATR  
  19.         ATR cardATR;  
  20.         //Canal de Comunicação com o Smart Card  
  21.         CardChannel cardChannel;  
  22.         //APDU de Comando  
  23.         CommandAPDU commandAPDU;  
  24.         //APDU de Resposta  
  25.         ResponseAPDU responseAPDU;  
  26.         //Buffer de Auxilio  
  27.         byte[] buffer;  
  28.   
  29.         try {  
  30.             //Adquire Fabrica de Leitores  
  31.             factory = TerminalFactory.getDefault();  
  32.   
  33.             //Adquire Lista de Leitores PC/SC no Sistema  
  34.             terminals = factory.terminals().list();  
  35.             System.out.println("Lista : " + terminals);  
  36.   
  37.             //Adquire Primeiro Terminal da Lista  
  38.             terminal = (CardTerminal)terminals.get(0);  
  39.             System.out.println("Terminal Selecionado: "  
  40.                     + terminal.getName());  
  41.   
  42.             //Estabelece Conexão com o Cartão na Leitora  
  43.             card = terminal.connect("*");  
  44.             System.out.println("card: " + card);  
  45.   
  46.             //Adquire ATR do Cartão  
  47.             cardATR = card.getATR();  
  48.             buffer = cardATR.getBytes();  
  49.             System.out.println("ATR : "  
  50.                     + formatBuffer(buffer, buffer.length));  
  51.   
  52.             //Adquire Canal de Comunicação  
  53.             cardChannel = card.getBasicChannel();  
  54.   
  55.             //AID do HelloWorld  
  56.             buffer = new byte[]{0x010x020x030x04,  
  57.                         0x050x060x070x08,  
  58.                         0x090x000x00};  
  59.   
  60.             //Monta APDU de Envio  
  61.             commandAPDU = new CommandAPDU(  
  62.                     0x00,       //CLA  
  63.                     0xA4,       //INS - SELECT  
  64.                     0x04,       //P1  
  65.                     0x00,       //P2  
  66.                     buffer);    //AID  
  67.   
  68.             //Imprime Comando  
  69.             System.out.println("\n[SELECT COMMAND]");  
  70.             System.out.println("=> " + formatBuffer(  
  71.                     commandAPDU.getBytes(),  
  72.                     commandAPDU.getBytes().length));  
  73.   
  74.             //Trasnmite e Recebe  
  75.             responseAPDU = cardChannel.transmit(commandAPDU);  
  76.   
  77.             //Verifica Resposta  
  78.             if (responseAPDU.getSW() != 0x9000) {  
  79.                 throw new Exception("Falha ao Selecionar : "  
  80.                         + String.format("0x%04X",  
  81.                           responseAPDU.getSW()));  
  82.             }  
  83.   
  84.             //Imprime Resposta  
  85.             System.out.println("<= " + formatBuffer(  
  86.                     responseAPDU.getBytes(),  
  87.                     responseAPDU.getBytes().length));  
  88.   
  89.             //Bytes de Testes  
  90.             buffer =  
  91.                 new byte[]{0x0A0x0B0x0C0x0D0x0E0x0F};  
  92.   
  93.             //Monta APDU de Envio  
  94.             commandAPDU = new CommandAPDU(  
  95.                     0x00//CLA  
  96.                     0x00//INS  
  97.                     0x00//P1  
  98.                     0x00//P2  
  99.                     buffer); //Teste  
  100.   
  101.             //Imprime Comando  
  102.             System.out.println("\n[TESTE BYTES]");  
  103.             System.out.println("=> " + formatBuffer(  
  104.                     commandAPDU.getBytes(),  
  105.                     commandAPDU.getBytes().length));  
  106.   
  107.             //Trasnmite e Recebe  
  108.             responseAPDU = cardChannel.transmit(commandAPDU);  
  109.   
  110.             //Verifica Resposta  
  111.             if (responseAPDU.getSW() != 0x9000) {  
  112.                 throw new Exception("Falha ao Selecionar : "  
  113.                         + String.format("0x%04X",  
  114.                           responseAPDU.getSW()));  
  115.             }  
  116.   
  117.             //Imprime Resposta  
  118.             System.out.println("<= " + formatBuffer(  
  119.                     responseAPDU.getBytes(),  
  120.                     responseAPDU.getBytes().length));  
  121.   
  122.         } catch (Exception e) {  
  123.             e.printStackTrace(System.out);  
  124.         }  
  125.     }  
  126.   
  127.     /** 
  128.      * Converte Buffer bytes para String 
  129.      * @param buffer 
  130.      * @param length 
  131.      * @return String 
  132.      */  
  133.     public static String formatBuffer(byte[] buffer,int length)  
  134.     {  
  135.         StringBuilder strBuff = new StringBuilder("");  
  136.         for (int i = 0; i < length; i++) {  
  137.             strBuff.append(String.format("%02X", buffer[i]));  
  138.         }  
  139.         return strBuff.toString();  
  140.     }  
  141. }  
O Resultado ao executar deve ser similar a isso:
  1. Lista : [PC/SC terminal ACS ACR38U 0]  
  2. Terminal Selecionado: ACS ACR38U 0  
  3. card: PC/SC card in ACS ACR38U 0, protocol T=1, state OK  
  4. ATR : 3BF81300008131FE454A434F5076323431B7  
  5.   
  6. [SELECT COMMAND]  
  7. => 00A404000B0102030405060708090000  
  8. <= 9000  
  9.   
  10. [TESTE BYTES]  
  11. => 00000000060A0B0C0D0E0F  
  12. <= 00000000060A0B0C0D0E0F9000  
Vamos a analise do código: 
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