Cartões inteligentes. Parte 4. JavaCard

Olá Giktayms!

Hoje eu gostaria de falar sobre JavaCard. Este artigo abordará o conceito de JavaCard e uma visão geral de sua arquitetura. Se houver interesse neste tópico, eu poderia escrever uma série separada de artigos nos quais todos os aspectos do JavaCard serão discutidos em detalhes. Eu direi imediatamente que não vou ensiná-lo a escrever seu primeiro aplicativo em JavaCard, porque já existem muitos artigos na Internet sobre isso. Hoje vamos falar principalmente sobre como o JavaCard funciona.

Portanto, um cartão inteligente baseado em JavaCard é um cartão no qual os aplicativos são executados na JavaCard Virtual Machine (uma versão limitada da Java Virtual Machine adaptada para cartões inteligentes) no chamado JavaCard Runtime Environment (que tem muito pouco em comum com o Java Runtime Environment) .

Em termos de terminologia, os aplicativos são chamados de Applets e estão contidos em Pacotes. Os pacotes são distribuídos em arquivos CAP (em vez de arquivos Jar). Pacotes e aplicativos têm seu próprio AID (Application Identifier). Isso é necessário para que eles possam ser identificados exclusivamente em comandos como: SELECT, INSTALL, DELETE etc. (SELECT é descrito em ISO7816-4 e JavaCard e outros comandos são descritos na Plataforma Global).

O ciclo de vida dos Applets é um pouco diferente do ciclo de vida usual dos aplicativos de computador. Um applet é qualquer classe que herda da classe base do Applet. Ao instalar aplicativos, seu método de instalação estática é chamado. Este método deve criar um objeto da classe correspondente e chamar o método de registro nele. Posteriormente, o objeto será salvo no sistema e receberá seu próprio AID, que será utilizado para posterior comunicação com o aplicativo. O objeto e seus campos de dados são armazenados na NVM (memória não volátil). Cada objeto ou matriz criada pelo aplicativo usando o operador "novo" também estará no NVM. Isso significa que, diferentemente dos programas de computador tradicionais, o estado dos aplicativos JavaCard é constante e não é perdido, mesmo quando o cartão é desligado.

Comunicação com a aplicação


A qualquer momento, todo canal lógico aberto tem um aplicativo ativo. Um aplicativo ativo é aquele que recebe todos os APDUs enviados pelo terminal. Quando um APDU é recebido, o JavaCard chama o método de processo do aplicativo ativo, que leva o APDU recebido como o único parâmetro. Este método é o núcleo do Applet, porque processa solicitações do terminal. O APDU resultante também é usado para enviar respostas.

Um applet pode ser ativado de duas maneiras:

  • ao redefinir o cartão ou ao abrir um canal lógico, o sistema geralmente ativa o aplicativo marcado como Aplicativo Padrão
  • usando o comando SELECT com P1 = 0x04 e o AID (total ou parcial) do aplicativo em Data

Quando o aplicativo é ativado usando o comando SELECT, imediatamente após a ativação, o método de processo com o APDU que contém este comando será chamado. Portanto, o Applet pode enviar informações em resposta a um comando SELECT quando ativado. A classe Applet fornece o método selectionApplet () para determinar se o comando recebido fez com que o aplicativo fosse ativado.

O aplicativo também tem a capacidade de reescrever os métodos select () e desmarcar () para inicializar respectivamente durante a ativação e dessinicializar durante a desativação. Esses métodos serão chamados independentemente de como o aplicativo foi ativado.

Canais lógicos podem ser abertos e fechados usando o comando MANAGE CHANNEL. Por meio de canais lógicos abertos, você pode enviar qualquer comando, incluindo SELECT. São SELECT e MANAGE CHANNEL que são os únicos comandos processados ​​diretamente pelo sistema, e não pelo aplicativo ativo. Embora no caso do comando SELECT, possamos dizer que ele é processado pelo sistema e pelo aplicativo ativo.

As regras completas para chamar o aplicativo e processar o comando SELECT são bastante complexas e existem poucas contradições entre o JavaCard e a Plataforma Global. No entanto, abordarei esse tópico em um dos meus próximos artigos. Agora, vale a pena dizer que os cartões geralmente seguem as regras descritas na Plataforma Global.

Gerenciamento de memória


Como eu disse acima, objetos e matrizes são armazenados por padrão no NVM. Além do NVM, o JavaCard também permite criar matrizes na RAM usando vários métodos da classe JCSystem. Existem 2 tipos de memória temporária: Limpar ao redefinir e Limpar ao cancelar a seleção. A primeira é apagada quando o cartão é desligado ou reiniciado. O segundo é apagado quando o Applet deixa de estar ativo (ou seja, quando SELECIONA outro aplicativo, desligamento, redefinição etc.). Deve-se notar que, embora o conteúdo da matriz seja apagado (ou seja, todos os zeros ou falsos sejam gravados nela), a própria matriz permanece e pode, por exemplo, ser armazenada no campo de dados do objeto.

O que é salvo no NVM:
  • Todos os objetos e seus campos de dados.
  • Todas as matrizes.
  • O conteúdo das matrizes criadas pelo novo operador.

O que é armazenado na RAM (CLEAR_ON_RESET ou CLEAR_ON_DESELECT):
  • , JCSystem.makeTransient<>Array. , , , JCSystem.makeTransientObjectArray(), NVM. RAM.
  • Global Arrays: (APDU Install Parameters ), Global Arrays, JCSystem.makeGlobalArray().
  • . .


Um aplicativo, como regra, deve criar todos os objetos e matrizes necessários durante a instalação e não dinamicamente. A razão para isso é principalmente dois fatores:

1) O desejo de garantir que, após a instalação bem-sucedida do aplicativo, ele não pare de funcionar se outro aplicativo consumir toda a memória livre.
2) Coletor de lixo - uma função opcional (embora muito comum). Isso significa que, ao criar objetos dinamicamente, existe o risco de os objetos criados nunca serem excluídos. Com o tempo, isso levará à falta de memória livre. Mesmo se houver um coletor de lixo, esse procedimento não ocorrerá automaticamente, mas a pedido do aplicativo (JCSystem.requestObjectDeletion ()).

Posteriormente, o código do aplicativo raramente é obtido "limpo" em termos de programação orientada a objetos. Usar byte comum [] para muitas operações diferentes é uma prática muito comum. Classes consistindo apenas em métodos estáticos e executando no byte [] também não são incomuns. Além disso, a criação de classes e objetos é minimizada. Pouca memória e deve ser protegida a todo custo.

Também vale a pena notar o papel do objeto APDU. Seu buffer localizado na RAM é apagado antes de receber cada comando. É habitual usá-lo não apenas para formar uma resposta, mas também como um buffer temporário. Seu tamanho é pelo menos 256 bytes ou muito maior se o cartão suportar Comprimento estendido.

Firewall de applet


Um recurso importante do JavaCard que pode confundir os programadores é a presença do chamado Applet Firewall. O objetivo do Firewall é impedir que qualquer Applet acesse os dados de outro. Devo dizer imediatamente que o JavaCard permite a comunicação entre aplicativos usando interfaces compartilháveis ​​e também que pacotes são bibliotecas de classes e funções que podem ser usadas em diferentes aplicativos. É por isso que é necessário um firewall. Os princípios básicos do Applet Firewall são os seguintes:

  • Cada aplicativo pertence a um contexto. Todos os aplicativos do mesmo pacote pertencem ao mesmo contexto.
  • Cada objeto pertence a um aplicativo ou sistema.
  • Um pacote sem aplicativos (biblioteca) não tem contexto. Objetos de suas classes pertencem ao aplicativo que os criou.
  • Sempre há um contexto ativo no sistema.
  • (/ ) , .
  • .
  • . . , , firewall.
  • CLEAR_ON_DESELECT , .

Usando a Interface compartilhável, um programador pode permitir que certos métodos de um objeto estejam disponíveis para aplicativos de um contexto diferente. Quando um desses métodos é chamado, o sistema alterna o contexto para o contexto ao qual pertence o objeto que fornece a Interface compartilhável. Isso significa que esse método tem acesso apenas aos objetos que pertencem ao seu contexto e, por exemplo, se você passar esse método byte [] como parâmetro do contexto original, ocorrerá um erro durante o acesso. Após a conclusão da execução do método, o contexto volta ao original.

O aplicativo também tem acesso a determinados objetos pertencentes ao sistema. Esses objetos são chamados de pontos de entrada. Existem dois tipos de pontos de entrada: temporário e permanente. Pontos de entrada permanentes são simples, o acesso a eles é permitido a partir de qualquer contexto. Um exemplo disso são os objetos da classe AID. Os pontos de entrada temporários, por outro lado, têm uma restrição que impede que eles sejam armazenados nos campos de dados do objeto ou nos campos estáticos da classe. Um exemplo de um ponto de entrada temporário é um objeto APDU.

A partir do JavaCard 3.0.4, também é possível criar matrizes globais usando o método JCSystem.makeGlobalArray (). O comportamento deles é exatamente o mesmo dos pontos de entrada temporários. Eles são principalmente necessários como parâmetro para métodos chamados usando a técnica da Interface compartilhável.

Atomicidade e transações


O JavaCard garante operações atômicas ao alterar os campos de dados de objetos ou classes. A atomicidade também é fornecida por métodos que trabalham em matrizes (exceto aqueles que possuem o sufixo NonAtomic no nome). Isso significa que, por exemplo, o Util.arrayCopy copiará todos os bytes (se executado com êxito) ou deixará a matriz inalterada em caso de erro ou perda de energia.

Se você precisar fazer alterações em vários campos e matrizes constantes em uma operação atômica, o JavaCard também fornecerá a função JCSystem.beginTransaction () para iniciar a transação e JCSystem.commitTransaction () para concluí-la. Todas as alterações que ocorrem em matrizes e campos constantes entre chamadas para JCSystem.beginTransaction () e JCSystem.commitTransaction () farão automaticamente parte da transação. Se a transação for cancelada devido a um erro, perda de energia ou uma chamada para o método JCSystem.abortTransaction (), o sistema restaurará o estado original. Vale lembrar que a implementação técnica da transação utiliza um buffer de sistema adicional. Se o buffer estiver cheio, o sistema emitirá um erro TransactionException.

Rmi


O JavaCard suporta a tecnologia RMI (Remote Metod Invocation). Neste artigo, não vou dedicar esta tecnologia. Só posso dizer que essa funcionalidade não é comum e muitos cartões não a suportam.

API


A maior parte da API JavaCard é dedicada à criptografia. Existem classes para criptografar, criar e verificar uma assinatura digital, gerar chaves e números aleatórios, calcular somas de verificação e hashes e também para implementar esquemas de troca de chaves. Além da criptografia, o JavaCard também oferece aulas para armazenar e verificar PINs e até dados biométricos. As demais classes fornecem acesso à funcionalidade descrita em outros capítulos ou são auxiliares para trabalhar com cadeias, números, TLV, etc. Quanto à API, ela será interpretada em outra série de artigos.

Principais limitações do JavaCard em comparação com o Java


A linguagem de programação do aplicativo JavaCard é Java. Quase Java. Portanto, os tipos char, float, double, long e enum não são suportados. O tipo int é opcional para cartões (geralmente os cartões modernos o suportam) e seu uso, se não for ativado pela opção correspondente, levará a um erro ao converter o aplicativo em um arquivo CAP. Esqueça for, iterator e lambda. Genéricos, importação estática e anotações (apenas Runtime Invisible) são suportados pelo conversor desde a versão 3.0.0, como eles não afetam a execução do código.

Para compilar o código, um compilador JDK comum é usado. Erros de incompatibilidade serão perceptíveis apenas ao converter para um arquivo CAP ou ao usar o IDE inteligente para JavaCard.

O maior problema para os programadores, de fato, é a falta de um int por padrão. Geralmente, eles realmente não são necessários, então não quero ativá-los desnecessariamente. No entanto, o compilador Java tem o hábito de converter automaticamente o resultado de todas as operações aritméticas para int. Para evitar isso, você deve usar um tipo explícito de conversão para abreviar ou byte no código, o que torna o código menos legível.

Outra limitação ocorre na inicialização de campos estáticos, a saber, que eles podem ser inicializados apenas com constantes e matrizes contendo constantes, mas não com objetos.

Conclusão


Então essa foi minha introdução ao JavaCard. Se você achar este artigo interessante ou, pelo menos, despertou seu interesse neste tópico, poderá escrever nos comentários o que especificamente você gostaria de saber mais. Para que eu possa decidir em que ordem e em quantos detalhes escrever sobre uma funcionalidade e aplicação específica do JavaCard. De qualquer forma, o próximo artigo da Plataforma Global concluirá esta série.

Source: https://habr.com/ru/post/pt380543/


All Articles