Модуль KJUR.asn1: базовые классы

KJUR.asn1 реализует набор классов для построения и сериализации ASN.1 структур в формате DER/BER, используемых в криптографии, сертификатах X.509, PKCS


Базовая модель ASN.1 объектов

Основой системы выступает абстрактный класс ASN.1 объекта, определяющий общий контракт для всех элементов структуры. Внутри KJUR.asn1 это реализуется через KJUR.asn1.ASN1Object.

Ключевая роль базового объекта заключается в унификации поведения:

  • генерация DER-кода
  • вложенность структур
  • сериализация в hex и byte массивы
  • ленивое построение (deferred encoding)

Каждый наследник реализует метод:

getEncodedHex()

который возвращает DER-представление объекта в виде hex-строки.


KJUR.asn1.ASN1Object

ASN1Object является корневым классом для всех ASN.1 типов. Он не представляет конкретного ASN.1 типа, но задаёт базовую логику кодирования.

Основные свойства:

  • isModified — флаг необходимости пересборки бинарного представления
  • hTLV — кешированное DER-значение
  • hT — тег
  • hL — длина
  • hV — значение

Методы:

  • getEncodedHex() — возвращает DER-код
  • getLengthHexFromValue() — вычисление длины значения
  • getEncodedHexValue() — кодирование только значения
  • tohex() — вспомогательное представление

Модель построена на принципе ленивого вычисления: бинарный результат пересоздаётся только при изменении структуры.


Примитивные ASN.1 типы

ASN1Integer

Представляет целые числа в ASN.1 формате INTEGER. Используется в ключах, сертификатах и цифровых подписях.

Особенности:

  • поддержка big integer через hex строку
  • автоматическое добавление знакового бита
  • нормализация ведущих нулей

Пример:

const int = new KJUR.asn1.DERInteger({int: 1234});
console.log(int.getEncodedHex());

ASN1Boolean

Используется для булевых значений TRUE/FALSE.

Правила кодирования:

  • TRUE = 0xff
  • FALSE = 0x00

ASN1OctetString

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

Особенности:

  • поддержка raw hex
  • возможность вложенного ASN.1 через encapsulation

ASN1BitString

Используется для битовых последовательностей, например, в public key structures.

Особенность DER-кодирования:

  • первый байт указывает число неиспользованных битов
  • последующие байты — данные

Структурные типы

ASN1Sequence

SEQUENCE — основной контейнер ASN.1, упорядоченная коллекция элементов.

Особенности реализации:

  • хранит массив asn1Array
  • поддерживает вложенные структуры любой глубины
  • кодируется как единый DER-объект

Пример:

const seq = new KJUR.asn1.DERSequence({
  array: [
    new KJUR.asn1.DERInteger({int: 1}),
    new KJUR.asn1.DERInteger({int: 2})
  ]
});

ASN1Set

SET аналогичен SEQUENCE, но не гарантирует порядок элементов.

В KJUR.asn1 порядок обычно сохраняется на уровне структуры, но семантически трактуется как неупорядоченный набор.


Идентификаторы и метаданные

ASN1ObjectIdentifier (OID)

Представляет идентификаторы алгоритмов и схем.

Пример:

  • RSA encryption
  • SHA-256
  • EC public key

Кодирование OID происходит через базу 40:

  • первые два компонента кодируются в один байт
  • последующие — base128 encoding

Пример:

const oid = new KJUR.asn1.DERObjectIdentifier({
  oid: "1.2.840.113549.1.1.11"
});

ASN1Null

Используется как маркер отсутствующего значения.

Кодирование всегда фиксировано:

  • tag + length = 0

Вспомогательные утилиты ASN.1

KJUR.asn1.ASN1Util

Утилитарный слой предоставляет функции для преобразования данных между форматами.

Основные возможности:

  • hex ↔︎ byte array
  • PEM ↔︎ DER
  • base64 ↔︎ hex
  • нормализация ASN.1 структур

Ключевые методы:

  • newObject(param) — создание ASN.1 объекта по описанию
  • getPEMStringFromHex(hex, label) — генерация PEM
  • hexToDER() — преобразование hex в DER-структуру
  • getTLVLength() — вычисление длины TLV

DER кодирование (TLV модель)

Все ASN.1 объекты в KJUR.asn1 кодируются по схеме TLV:

  • T (Tag) — тип элемента
  • L (Length) — длина значения
  • V (Value) — данные

Пример структуры:

30 ...      SEQUENCE
  02 ...    INTEGER
  04 ...    OCTET STRING

Кодирование длины поддерживает:

  • short form (0–127)
  • long form (128+)

Вложенные структуры и рекурсивность

Одной из ключевых особенностей является рекурсивная сборка ASN.1 дерева.

Каждый объект может содержать:

  • примитивные типы
  • другие ASN.1 структуры
  • массивы объектов

При вызове getEncodedHex() происходит:

  1. обход дерева
  2. вычисление значений снизу вверх
  3. формирование TLV на каждом уровне

Механизм ленивого обновления

ASN.1 объекты используют флаг isModified, который управляет пересборкой бинарного представления.

Логика:

  • при изменении внутреннего значения → isModified = true
  • при вызове getEncodedHex() → пересборка только при необходимости
  • кеширование результата в hTLV

Это критично для производительности при работе с большими сертификатами.


Представление PEM через ASN.1 слой

Хотя PEM относится к внешнему уровню, ASN.1 слой участвует в его формировании.

Процесс:

  1. DER структура кодируется в hex
  2. hex преобразуется в base64
  3. добавляются заголовки:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

Расширяемость базовых классов

Архитектура KJUR.asn1 позволяет создавать новые ASN.1 типы через наследование от ASN1Object.

Типичная схема расширения:

var MyASN1Type = function(params) {
  KJUR.asn1.ASN1Object.call(this);
  this.params = params;
};

MyASN1Type.prototype = Object.create(KJUR.asn1.ASN1Object.prototype);
MyASN1Type.prototype.constructor = MyASN1Type;

MyASN1Type.prototype.getEncodedHex = function() {
  // кастомная логика кодирования
};

Это позволяет внедрять нестандартные ASN.1 конструкции без изменения ядра библиотеки.


Обработка ошибок и валидация

Базовые классы минимизируют строгую валидацию, отдавая приоритет гибкости.

Основные проверки:

  • корректность hex строк
  • допустимость OID формата
  • валидность вложенных ASN.1 структур

Ошибки обычно проявляются на этапе DER кодирования, а не при создании объектов.


Производительность кодирования

Оптимизация достигается за счёт:

  • кеширования TLV
  • ленивой сборки дерева
  • минимизации преобразований строк
  • работы преимущественно с hex-форматом вместо byte array

На практике это позволяет эффективно кодировать большие структуры сертификатов и ключей даже в среде браузера.