2010

segunda-feira, janeiro 11, 2010

Olá

Ano novo, vida nova. Será?  Acho que já vi esta frase muitas e muitas vezes…

Promessas de Ano Novo? Mudanças?

Ok, agora que não tenho mais faculdade (Sim, eu me formei! Sou Bacharel em Computação Científica agora!! =p) vou ter tempo livre para fazer várias coisas…

Vou poder ocupar meu tempo programando (sim, continuar estudando…), desenvolvendo projetos pessoais, jogando XBOX, fazendo academia (sim, último ano me fez engordar BEM) e outras coisas especiais que podem ser resumidas com três pontinhos… Ah, e talvez tenha tempo para escrever por aqui também ;)

Ao contrário do que imaginava, meu post sobre SmartCard + Java foi bem acessado neste [1 ano e meio] em que esteve parado. Quem sabe não apareça mais posts sobre isso? :)

Hora de dormir… 2:28 e o sono está começando a me alcançar…


SmartCard + Java

quinta-feira, junho 26, 2008

Comecei a trabalhar com Java e a utilizar SmartCards no final de 2006, no mesmo período que comecei a utilizar Gnu/Linux efetivamente. Sim, emprego novo. =p

Mas… o que é um SmartCard?
Bom, basicamente é um cartão de plástico com um chip “embedado”.

Ah, então eu já vi um desses! É aqueles cartões bancários certo?
Isso, também. Mas há outros tipos, não apenas bancários.
Você tem celular? É GSM? Sabe aquele chipzinho que você insere nele?

Então, é um SmartCard também =]

A diferença entre eles é a estrutura criada internamente. Tem também a história das chaves de autenticação e outros detalhes que complicam bastante, dependendo do que precisa ser feito.

Como conheço um pouco sobre isso e já percebi que algumas pessoas ainda têm dúvidas, resolvi criar um pequeno tutorial com códigos em java para acesso ao SmartCard, para envio de alguns comando simples, apenas para começar a brincadeira.

Aqui considero que você já possui uma leitora com os drivers instalados, possui o JDK 1.6 (java) instalado (utilizaremos a classe SmartCardIO, que está disponível a partir dessa versão) e corretamente configurado, além de conhecimento sobre o funcionamento e comunicação com SmartCards (informações podem ser obtidas aqui e também aqui).

A comunicação com o cartão se dá através de comandos APDU (definida na ISO 7816-4).
Em java, antes de enviar o comando é necessário alguns passos importantes:

Criar uma “fábrica” de leitoras:

factory = TerminalFactory.getDefault();

Preencher uma lista com as leitoras conectadas e iniciadas:

terminals = factory.terminals().list();

Realizar a conexão com o cartão:

card = terminal.connect("t=0");

Abrir um canal para comunicação com o cartão:

channel = card.getBasicChannel();

Após esses passos é possível recuperar o ATR do cartão, que é sua resposta quando o mesmo é energizado:

atr = card.getATR();

Antes de enviar o comando APDU é desejável que seja feita a verificação para garantir que há um cartão na leitora e evitar uma exception

if(!terminal.isCardPresent()) {
}

Após a verificação, podemos enviar o comando desejado através do canal criado, recebendo a resposta do cartão:

lastResponse = channel.transmit(new CommandAPDU(myCommand));

Certo, esse é o básico. Mas temos um problema: A comunicação é feita através de array de bytes, e vai dar muito trabalho para converter toda hora que for preciso enviar um comando ou receber resposta e exibí-la. Basta criar uma classe para fazer a conversão =D Lembrando, valores HEXADECIMAIS.

A seguir uma classe simples que criei para demonstrar os passos citados e sua utilização.

import java.util.List;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;

/**
 * Classe para comunicação com SmartCards
 * @author Luiz Gustavo Silva Marcondes
 * http://lgmarcondes.wordpress.com
 */
public class MySmartCardTest {

private TerminalFactory factory;
private List<CardTerminal> terminals;
private CardTerminal terminal;
private Card card;
private CardChannel channel;
private ResponseAPDU lastResponse;

public MySmartCardTest() {
    try {
        if(initialization("t=0")) {
            //Se conseguiu inicializar, vamos
            //recuperar informações do ATR
            //(Answer to Response) do cartão
            System.out.println(getAtr());
        }
    } catch (CardException ex) {
        ex.printStackTrace();
    }
}

/**
 * Inicializa a seção, criando um canal para se
 * comunicar com o SmartCard.
 * @param protocol o procoloco a ser utilizado
 * ("T=0", "T=1", or "T=CL"), ou "*" para se
 * conectar usando qualquer protocolo disponível
 * @return true em caso de inicialização correta.
 */
public boolean initialization(String protocol)
        throws CardException {
    //Criar uma "Fábrica" de Terminais (leitoras)
    factory = TerminalFactory.getDefault();
    //Preencher a lista de Terminais (leitoras)
    terminals = factory.terminals().list();

    //Exibe mensagem e retorna se não achar leitoras
    if(terminals.size() == 0) {
        JOptionPane.showMessageDialog(null, "Erro,"+
                " nenhuma leitora foi encontrada.");
        return false;
    }

    //Box contendo a lista das leitoras
    JComboBox readersList;
    readersList = new JComboBox(terminals.toArray());

    int opcao = JOptionPane.showConfirmDialog(null,
            readersList, "Escolha a leitora: ",
            JOptionPane.OK_CANCEL_OPTION);

    if (opcao == JOptionPane.OK_OPTION) {
        terminal = terminals.
                get(readersList.getSelectedIndex());
    } else {
        return false;
    }

    //Ligação com o cartão utilizando protocolo de
    //comunicação T=0
    card = terminal.connect("t=0");

    //Abre um canal com um cartão
    channel = card.getBasicChannel();

    return true;
}

/**
 * Retorna o ATR - Answer to Reset - Resposta do
 * cartão ao reset
 * @return o ATR
 */
public String getAtr() {

    //Armazena o ATR do cartão em um array de bytes
    byte[] atr = card.getATR().getBytes();
    String response = "";

    //Converte a resposta de array de bytes para
    //strings hexadecimais em letras maiúsculas
    for (int i = 0; i < atr.length; i++) {
        if (atr[i] < 0) {
            response += Integer.toHexString(256 +
                    atr[i]).toUpperCase();
        } else {
            if (atr[i] < 16)
                response += "0";
            response += Integer.toHexString(atr[i])
                    .toUpperCase();
        }
    }

    return response;
}

/**
 * Envia o comando APDU para o SmartCard e retorna a
 * APDU de resposta.
 * @param apdu uma string que contém o comando APDU.
 * @return uma string que contém a APDU de resposta.
 * @throws CardException
 */
public String sendApdu(String apdu)
        throws CardException {
    //Se não houver cartão na leitora retorna
    if(!terminal.isCardPresent())
        return "";

    //Exibe o comando a ser enviado
    System.out.println("Command APDU: " + apdu);

    //Converte a string contendo o comando para um
    //array de bytes
    byte[] buffer = strToHexByte(apdu);

    //Envia a APDU e guarda resposta em lastResponse
    lastResponse = channel.transmit(
            new CommandAPDU(buffer));

    String response = "";
    //Armazena a resposta (SW) em forma de string
    response = Integer.toHexString(
            lastResponse.getSW());

    response += " ";

    byte[] data = lastResponse.getData();

    for (int i = 0; i < data.length; i++) {
        if (data[i] < 0) {
            response += Integer.toHexString(256 +
                    data[i]).toUpperCase();
        } else {
            if (data[i] < 16)
                response += "0";
            response += Integer.toHexString(data[i])
                    .toUpperCase();
        }
    }

    return response.toUpperCase();
}

/**
 * Converte uma string hexadecimal em array de bytes
 * @param str a string hexadecimal a ser convertida.
 * @return um array de bytes com a strign convertida.
 */
public byte[] strToHexByte(String string) {
    byte[] b = new byte[string.length() / 2];

    try {
        String s1;

        int j=0;
        for(int i = 0; i < string.length(); i += 2) {
            s1 = string.substring(i, i + 2);
            b[j] = (byte)Integer.parseInt(s1, 16);
            j++;
        }
    } catch(NumberFormatException ex) {
        ex.printStackTrace();
    } catch(StringIndexOutOfBoundsException ex) {
        ex.printStackTrace();
    }

    return b;
}

public static void main(String args[]) {
        MySmartCardTest myTest;
        myTest = new MySmartCardTest();

        try {
            String myAPDU, myAnswer;
            myAPDU = "00A40400023F00";
            myAnswer = myTest.sendApdu(myAPDU);
            System.out.println("Resp: " + myAnswer);
        } catch(CardException ex) {
            ex.printStackTrace();
        }
}
}

Bom, como o Corinthians apenas empatou ontem, por hoje é só! =p