Типы ASN.1: SEQUENCE, SET, CHOICE, ANY

SEQUENCE

В ASN.1 SEQUENCE представляет собой упорядоченную коллекцию элементов, где каждый компонент имеет фиксированную позицию. В терминах кодирования это структурный контейнер, в котором важен порядок полей и их типы. В криптографических протоколах SEQUENCE используется практически везде: сертификаты X.509, ключевые структуры PKCS

В Jsrsasign SEQUENCE реализуется через класс KJUR.asn1.DERSequence. Он позволяет объединять произвольные ASN.1-объекты в строгую последовательность.

Базовая структура SEQUENCE:

  • элементы упорядочены
  • типы элементов могут различаться
  • декодирование зависит от позиции, а не от имени поля

Простейший пример формирования SEQUENCE:

const { ASN1Object, DERInteger, DERUTF8String, DERSequence } = KJUR.asn1;

const seq = new DERSequence({
  array: [
    new DERInteger({ int: 123 }),
    new DERUTF8String({ str: "test" })
  ]
});

const hex = seq.getEncodedHex();

Внутри SEQUENCE могут находиться вложенные структуры:

const inner = new DERSequence({
  array: [
    new DERInteger({ int: 1 }),
    new DERInteger({ int: 2 })
  ]
});

const outer = new DERSequence({
  array: [
    inner,
    new DERUTF8String({ str: "nested" })
  ]
});

SEQUENCE активно применяется при описании алгоритмических параметров. Например, RSA публичный ключ кодируется как последовательность модуль/экспонента.


SET

SET является структурой, похожей на SEQUENCE, но с принципиальным отличием: порядок элементов не является значимым. В ASN.1 это неупорядоченное множество уникальных элементов.

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

  • порядок элементов не фиксирован
  • семантически важен только набор значений
  • при кодировании применяется каноническая сортировка DER

В Jsrsasign SET представлен классом KJUR.asn1.DERSet.

Пример создания SET:

const { DERInteger, DERUTF8String, DERSet } = KJUR.asn1;

const set = new DERSet({
  array: [
    new DERUTF8String({ str: "B" }),
    new DERUTF8String({ str: "A" })
  ]
});

const hex = set.getEncodedHex();

При DER-кодировании элементы SET автоматически упорядочиваются по байтовому представлению, что обеспечивает детерминированность результата.

SET часто используется в криптографических подписях, особенно в структурах типа Attributes в PKCS#7/CMS, где набор атрибутов не зависит от порядка.


CHOICE

CHOICE представляет собой конструкцию выбора одного варианта из нескольких возможных типов. В отличие от SEQUENCE и SET, CHOICE не содержит одновременно несколько полей — активным является только один вариант.

В ASN.1 это выражение альтернативности:

  • поле A или поле B или поле C
  • только одно значение допускается

В Jsrsasign CHOICE не имеет отдельного низкоуровневого класса. Он реализуется через механизм ASN1Object описаний и условной сборки структур.

Типичный подход через KJUR.asn1.ASN1Util.newObject:

const { ASN1Util } = KJUR.asn1;

const obj = ASN1Util.newObject({
  utf8str: { str: "text value" }
});

Другой вариант — ручное ветвление:

function buildChoice(type, value) {
  const { DERInteger, DERUTF8String } = KJUR.asn1;

  if (type === "int") {
    return new DERInteger({ int: value });
  }

  if (type === "str") {
    return new DERUTF8String({ str: value });
  }

  throw new Error("unsupported choice");
}

CHOICE широко используется в протоколах, где формат поля зависит от контекста: алгоритмы подписи, альтернативные идентификаторы, разные типы ключевых параметров.


ANY

ANY в ASN.1 обозначает неопределённый тип, который может принимать любое ASN.1-значение. Это наиболее гибкая, но и наименее строгая конструкция.

Характерные особенности:

  • тип определяется контекстом
  • может содержать любую ASN.1-структуру
  • используется для расширяемости схем

В Jsrsasign ANY напрямую не представлен как отдельный класс, поскольку ASN.1-библиотека ориентирована на конкретные типы. Вместо этого ANY реализуется через работу с ASN1Object и динамическую вставку уже закодированных структур.

Пример вставки произвольного ASN.1-объекта:

const { DERSequence, DERInteger, DERUTF8String } = KJUR.asn1;

const anyValue = new DERSequence({
  array: [
    new DERInteger({ int: 999 }),
    new DERUTF8String({ str: "any content" })
  ]
});

const container = new DERSequence({
  array: [
    anyValue
  ]
});

В некоторых случаях ANY кодируется как заранее сериализованный HEX:

const raw = KJUR.asn1.ASN1Util.newObject({
  seq: {
    array: [
      { int: { int: 1 } }
    ]
  }
}).getEncodedHex();

const wrapped = KJUR.asn1.ASN1Util.newObject({
  any: raw
});

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


Сравнение поведения конструкций

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

В Jsrsasign эти конструкции реализуются неравномерно: SEQUENCE и SET имеют прямые классы DERSequence и DERSet, CHOICE строится через условное создание объектов, ANY — через универсальные ASN1Object или предварительно закодированные данные.


Композиция сложных структур

ASN.1-модели редко существуют изолированно. На практике конструкции комбинируются:

const { DERSequence, DERSet, DERInteger, DERUTF8String } = KJUR.asn1;

const attributes = new DERSet({
  array: [
    new DERSequence({
      array: [
        new DERUTF8String({ str: "key" }),
        new DERInteger({ int: 1 })
      ]
    }),
    new DERSequence({
      array: [
        new DERUTF8String({ str: "role" }),
        new DERUTF8String({ str: "admin" })
      ]
    })
  ]
});

Такие структуры типичны для PKCS#7, CMS и расширений сертификатов, где вложенность достигает нескольких уровней, а семантика определяется комбинацией SEQUENCE и SET внутри гибких контейнеров ANY и CHOICE.


Каноническое кодирование DER

Все перечисленные типы в контексте Jsrsasign чаще всего кодируются по правилам DER (Distinguished Encoding Rules), что накладывает дополнительные ограничения:

  • SEQUENCE и SET всегда имеют строго определённый бинарный формат
  • SET требует сортировки элементов
  • CHOICE влияет только на выбранный вариант, но не на кодирование
  • ANY обязан быть уже корректно закодирован

DER-выход можно получить через:

const hex = sequence.getEncodedHex();

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