Смарт-Карты. Часть 4. Javaкарта

Привет, Гиктаймс! Сегодня я хотел бы поговорить о JavaCard. В этой статье основное внимание будет уделено концепции JavaCard и обзору ее архитектуры.

Если есть интерес к этой теме, то я мог бы написать отдельную серию статей, в которой подробно будут рассмотрены все аспекты JavaCard. Скажу сразу, я не буду вас учить писать свое первое приложение на JavaCard, потому что… В Интернете уже слишком много статей по этому поводу.

Сегодня мы поговорим в основном о принципах работы JavaCard. Итак, смарт-карта на основе JavaCard — это карта, на которой приложения запускаются на виртуальной машине JavaCard (ограниченной версии виртуальной машины Java, адаптированной для смарт-карт) в так называемой среде выполнения JavaCard (которая имеет очень мало общего с со средой выполнения Java).

С точки зрения терминологии приложения называются апплетами и содержатся в пакетах.

Пакеты распространяются в CAP-файлах (вместо Jar-файлов).

Пакеты и приложения имеют свой собственный AID (идентификатор приложения).

Это необходимо для того, чтобы их можно было четко идентифицировать в таких командах, как: SELECT, INSTALL, DELETE и т. д. (SELECT описан в ISO7816-4, а JavaCard и другие команды описаны в Глобальной платформе).

Жизненный цикл апплетов несколько отличается от типичного жизненного цикла настольных приложений.

Апплет — это любой класс, который наследуется от базового класса «Апплет».

При установке приложения вызывается его статический метод установки.

Этот метод должен создать объект соответствующего класса и вызвать на нем метод Register. В дальнейшем объект сохранится в системе и получит собственный AID, который будет использоваться для дальнейшей связи с приложением.

Объект и его поля данных хранятся в NVM (энергонезависимой памяти).

Каждый объект или массив, созданный приложением с помощью оператора «new», также будет находиться в NVM. Это означает, что в отличие от традиционных компьютерных программ состояние приложений JavaCard постоянно и не теряется даже при выключении карты.



Связь с приложением

В каждый момент времени на каждом открытом логическом канале находится одно активное приложение.

Активное приложение — это приложение, которое получает все APDU, отправленные терминалом.

При получении APDU JavaCard вызывает метод процесса активного приложения, который принимает полученный APDU в качестве единственного параметра.

Этот метод является ядром Applet, поскольку именно там обрабатываются запросы от терминала.

Полученный объект APDU также используется для отправки ответов.

Апплет можно активировать двумя способами:

  • при сбросе карты или открытии логического канала система, как правило, активирует приложение, помеченное как Default Application.
  • с помощью команды SELECT с P1 = 0x04 и AID (полным или частичным) приложения в Data
Когда приложение активируется с помощью команды SELECT, метод процесса будет вызван сразу после активации с помощью APDU, содержащего эту команду.

Это позволяет апплету отправлять информацию в ответ на команду SELECT при активации.

Класс Applet предоставляет метод ChooseApplet(), позволяющий определить, вызвала ли полученная команда активацию приложения.

Приложение также имеет возможность перезаписать методы select() и deselect() для выполнения соответственно инициализации при активации и деинициализации при деактивации.

Эти методы будут вызываться независимо от того, как было активировано приложение.

Логические каналы можно открывать и закрывать с помощью команды MANAGE CHANNEL. По открытым логическим каналам можно отправлять любые команды, в том числе SELECT. SELECT и MANAGE CHANNEL — единственные команды, которые обрабатываются непосредственно системой, а не активным приложением.

Хотя в случае с командой SELECT можно сказать, что она обрабатывается и системой, и активным приложением.

Полные правила активации приложения и обработки команды SELECT довольно сложны, и между JavaCard и Глобальной платформой есть небольшие несоответствия.

Впрочем, этой темы я затрону в одной из следующих статей.

Теперь стоит сказать, что карты часто следуют правилам, описанным в Глобальной платформе.



Управление памятью

Как я уже говорил выше, объекты и массивы по умолчанию сохраняются в NVM. Помимо NVM, JavaCard также предоставляет возможность создавать массивы в оперативной памяти с помощью ряда методов класса JCSystem. В этом случае существует 2 типа временной памяти: Очистка при сбросе и Очистка при отмене выбора.

Первый стирается при выключении или сбросе карты.

Второй стирается, когда Апплет перестает быть активным (т.е.

: при ВЫБОРЕ другого приложения, выключении, перезагрузке и т.п.

).

Стоит отметить, что хотя содержимое массива и стирается (то есть туда записываются все нули или ложь), сам массив остаётся, и его можно, например, хранить в поле данных объекта.

Что хранится в NVM:

  • Все объекты и их поля данных.

  • Все массивы.

  • Содержимое массивов, созданных новым оператором.

Что хранится в оперативной памяти (CLEAR_ON_RESET или CLEAR_ON_DESELECT):
  • Содержимое массивов, созданных JCSystem.makeTransient Функции массива.

    Важно отметить, что объекты, найденные в массиве, созданном JCSystem.makeTransientObjectArray(), по-прежнему будут храниться в NVM. Только ссылка на них будет в оперативной памяти.

  • Глобальные массивы: системные (буфер APDU и буфер параметров установки), а также глобальные массивы, созданные с помощью JCSystem.makeGlobalArray().

  • Буфер транзакций.

    Однако к этому буферу нельзя получить прямой доступ.

Обычно приложение должно создавать все необходимые ему объекты и массивы при установке, а не динамически.

Причиной этого являются в основном два фактора: 1) Желание быть уверенным, что в случае успешной установки приложения оно не перестанет работать, если какое-то другое приложение заберет всю свободную память.

2) Сборщик мусора — необязательная (хотя и очень распространенная) функция.

Это означает, что при динамическом создании объектов существует риск того, что созданные объекты никогда не будут удалены.

Со временем это приведет к нехватке свободной памяти.

Даже если сборщик мусора присутствует, эта процедура происходит не автоматически, а по запросу приложения (JCSystem.requestObjectDeletion()).

Впоследствии код приложения редко бывает «чистым» по параметрам объектно-ориентированного программирования.

Использование общего byte[] для множества различных операций — очень распространенная практика.

Классы, состоящие исключительно из статических методов и работающие на byte[], также не редкость.

Кроме того, создание классов и объектов сведено к минимуму.

Память ограничена и должна быть защищена любой ценой.

Также стоит отметить роль объекта APDU. Его буфер, расположенный в оперативной памяти, стирается перед получением каждой команды.

Его принято использовать не только для генерации ответа, но зачастую даже как временный буфер.

Его размер составляет не менее 256 байт или намного больше, если карта поддерживает расширенную длину.



Аплет Брандмауэр

Важной особенностью JavaCard, которая может сбить с толку программистов, является наличие так называемого Applet Firewall. Целью брандмауэра является предотвращение доступа любого апплета к чужим данным.

Скажу сразу, что JavaCard позволяет осуществлять связь между приложениями с помощью Shareable Interfaces, а также что пакеты — это библиотеки классов и функций, которые можно использовать в разных приложениях.

Вот почему возникает необходимость в брандмауэре.

Основные принципы Applet Firewall заключаются в следующем:

  • Каждое приложение принадлежит одному контексту.

    Все приложения из одного пакета принадлежат одному контексту.

  • Каждый объект принадлежит одному приложению или системе.

  • Пакет без приложений (библиотеки) не имеет контекста.

    Объекты его классов принадлежат приложению, которое их создало.

  • В системе всегда есть один активный контекст.
  • Доступ (чтение/запись в случае массива и вызов методов в случае объекта) разрешен только к объектам, принадлежащим активному контексту.

  • Система имеет доступ ко всем объектам.

  • Вызовы статических методов всегда разрешаются и выполняются в активном контексте.

    Также разрешен доступ к полям статических данных.

    Однако объекты, на которые ссылаются статические поля, подчиняются общим правилам брандмауэра.

  • Временные массивы типа CLEAR_ON_DESELECT доступны только в том случае, если активный контекст соответствует активному контексту приложения.

Используя разделяемый интерфейс, программист может разрешить доступ к определенным методам одного объекта приложениям из другого контекста.

При вызове одного из этих методов система переключает контекст на контекст, которому принадлежит объект, предоставляющий общий интерфейс.

Это означает, что этот метод имеет доступ только к объектам, принадлежащим его контексту, и если, например, передать этому методу byte[] в качестве параметра из исходного контекста, то при доступе возникнет ошибка.

После завершения выполнения метода контекст переключается обратно на исходный.

Приложение также имеет доступ к определенным объектам, принадлежащим системе.

Такие объекты называются точками входа.

Существует два типа точек входа: временные и постоянные.

Постоянные точки входа просты и доступны из любого контекста.

Примером этого являются объекты класса AID. С другой стороны, временные точки входа имеют ограничение, которое не позволяет хранить ссылки на них в полях данных объекта или полях статического класса.

Примером временной точки входа является объект APDU. Начиная с JavaCard 3.0.4, глобальные массивы также можно создавать с помощью метода JCSystem.makeGlobalArray().

Их поведение точно такое же, как и у временных Точек Входа.

Они в первую очередь необходимы в качестве параметра для методов, вызываемых с использованием техники Shareable Interface.

Атомарность и транзакции

JavaCard гарантирует атомарные операции при изменении полей данных объектов или классов.

Атомарность также обеспечивается методами, работающими с массивами (за исключением тех, которые имеют суффикс NonAtomic в названии).

Это означает, что, например, Util.arrayCopy либо скопирует все байты (в случае успеха), либо оставит массив без изменений в случае ошибки или потери энергии.

Если вам нужно внести изменения в несколько постоянных полей и массивов за одну атомарную операцию, JavaCard также предоставляет функции JCSystem.beginTransaction() для запуска транзакции и JCSystem.commitTransaction() для ее завершения.

Все изменения, которые происходят с массивами и постоянными полями между вызовами JCSystem.beginTransaction() и JCSystem.commitTransaction(), автоматически становятся частью транзакции.

Если транзакция прервана из-за ошибки, потери энергии или вызова метода JCSystem.abortTransaction(), система восстановит исходное состояние.

Стоит помнить, что техническая реализация транзакции использует дополнительный системный буфер.

Если буфер полностью заполнен, система выдает ошибку TransactionException.

РМИ

JavaCard поддерживает технологию RMI (удаленный вызов метода).

В этой статье я не буду обсуждать эту технологию.

Скажу лишь, что эта функциональность не распространена и многие карты ее не поддерживают.

API

Большая часть API JavaCard посвящена криптографии.

Существуют классы для шифрования, создания и проверки цифровых подписей, генерации ключей и случайных чисел, расчета контрольных сумм и хешей, а также реализации схем обмена ключами.

Помимо криптографии, JavaCard также предоставляет классы для хранения и проверки PIN-кодов и даже биометрических данных.

Остальные классы предоставляют доступ к функциональности, описанной в других главах, или являются вспомогательными для работы со строками, числами, TLV и т. д. Что касается API, то о нем речь пойдет в другой серии статей.



Основные ограничения JavaCard по сравнению с Java

Языком программирования приложений JavaCard является Java. Почти Ява.

Таким образом, типы char, float, double, long и enum не поддерживаются.

Тип int является необязательным для карт (обычно современные карты его поддерживают) и его использование, если оно не включено соответствующей опцией, приведет к ошибке при конвертации приложения в CAP-файл.

Забудьте об for, итераторе и лямбде.

Обобщенные шаблоны, статический импорт и аннотации (только Runtime Invisible) поддерживаются конвертером, начиная с версии 3.0.0, поскольку они не влияют на выполнение кода.

Для компиляции кода используется обычный компилятор из JDK. Ошибки несовместимости будут заметны только при преобразовании в файл CAP или при использовании интеллектуальной среды разработки JavaCard. Самая большая проблема для программистов — это отсутствие int по умолчанию.

Обычно они действительно не нужны, поэтому активировать их без надобности не хочется.

Однако компилятор Java имеет привычку автоматически приводить результат всех арифметических операций к типу int. Чтобы избежать этого, код должен использовать явное приведение типа к short или byte, что делает код менее читаемым.

Другое ограничение возникает при инициализации статических полей, а именно: их можно инициализировать только константами и массивами, содержащими константы, но не объектами.



Заключение

Итак, это было мое знакомство с JavaCard. Если эта статья показалась вам интересной или хотя бы пробудила у вас интерес к данной теме, то вы можете написать в комментариях, о чем именно вы хотели бы узнать больше.

Таким образом я могу решить, в каком порядке и подробно писать о том или ином функционале и применении JavaCard. В любом случае следующая статья о Глобальной платформе станет завершающей в этой серии.

Теги: #Сотовая связь #смарт-карты #смарт-карта #смарт-карта #javacard

Вместе с данным постом часто просматривают: