ASN.1 в jsrsasign реализуется через набор классов пространства
KJUR.asn1, где каждая структура соответствует отдельному
типу ASN.1-узла. Построение произвольных структур сводится к композиции
объектов и строгому контролю тегирования, типов и правил
DER-энкодинга.
В jsrsasign все элементы ASN.1 наследуются от базового конструктора
ASN1Object. Основные строительные блоки:
DERIntegerDEROctetStringDERUTF8StringDERNullDERObjectIdentifierDERSequenceDERSetDERTaggedObjectКаждый объект формирует собственный DER-фрагмент, который затем может быть вложен в другие структуры.
Простейший пример композиции:
var int = new KJUR.asn1.DERInteger({int: 42});
var str = new KJUR.asn1.DERUTF8String({str: "test"});
var seq = new KJUR.asn1.DERSequence({
array: [int, str]
});
Каждый объект инкапсулирует:
DER-правила требуют однозначной канонической формы, поэтому порядок байтов и структура фиксированы.
ASN.1 почти всегда используется как дерево. В jsrsasign это
отражается через вложенные array или set
параметры.
Пример вложенности:
var innerSeq = new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERInteger({int: 1}),
new KJUR.asn1.DERUTF8String({str: "inner"})
]
});
var outerSeq = new KJUR.asn1.DERSequence({
array: [
innerSeq,
new KJUR.asn1.DERBoolean({bool: true})
]
});
В результате формируется дерево, где каждый узел сериализуется в DER независимо, затем объединяется в родительском контейнере.
Для построения произвольных ASN.1-типов часто требуется использование
контекстно-специфических тегов. В jsrsasign это реализуется через
DERTaggedObject.
Контекстный тег позволяет:
var tagged = new KJUR.asn1.DERTaggedObject({
tag: "a0",
explicit: true,
obj: new KJUR.asn1.DERUTF8String({str: "context value"})
});
Здесь "a0" обозначает [0] constructed context-specific
tag.
Различают два режима:
Объект оборачивается в дополнительный слой:
Используется в большинстве X.509 расширений.
Тип исходного объекта заменяется:
var implicitTagged = new KJUR.asn1.DERTaggedObject({
tag: "a1",
explicit: false,
obj: new KJUR.asn1.DERInteger({int: 99})
});
В этом случае Integer перестаёт быть INTEGER на уровне ASN.1-разметки и интерпретируется как контекстный тип.
DERSequence — основной контейнер для большинства
ASN.1-структур (сертификаты, ключи, запросы).
Пример сложной произвольной схемы:
var asn1 = new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERInteger({int: 10}),
new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERObjectIdentifier({oid: "1.2.840.113549.1.1.5"}),
new KJUR.asn1.DERNull()
]
}),
new KJUR.asn1.DEROctetString({hex: "deadbeef"})
]
});
Здесь демонстрируется комбинация:
DERSet отличается от DERSequence тем, что
порядок элементов не фиксируется логически, но в DER он всегда
сортируется по байтовому представлению.
var set = new KJUR.asn1.DERSet({
array: [
new KJUR.asn1.DERUTF8String({str: "b"}),
new KJUR.asn1.DERUTF8String({str: "a"})
]
});
На уровне DER результат будет отсортирован, несмотря на порядок добавления.
OID является ключевым элементом большинства ASN.1-структур криптографии:
var oid = new KJUR.asn1.DERObjectIdentifier({
oid: "2.5.4.3"
});
OID часто комбинируется с NULL:
var algId = new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERObjectIdentifier({oid: "1.2.840.113549.1.1.1"}),
new KJUR.asn1.DERNull()
]
});
Это классический формат идентификатора алгоритма RSA.
Произвольные ASN.1-структуры часто используются для имитации частей сертификатов:
var tbs = new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERInteger({int: 2}),
algId,
new KJUR.asn1.DERUTF8String({str: "subject"})
]
});
Дальнейшее вложение позволяет формировать полный сертификатный контейнер.
Для низкоуровневых структур используется
DEROctetString:
var bin = new KJUR.asn1.DEROctetString({
hex: "0102030405"
});
Либо передача массива байтов через HEX-представление обеспечивает строгую совместимость с DER.
При построении сложных протокольных структур часто требуется комбинировать:
Пример:
var complex = new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERTaggedObject({
tag: "a0",
explicit: true,
obj: new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERInteger({int: 1}),
new KJUR.asn1.DERUTF8String({str: "nested"})
]
})
}),
new KJUR.asn1.DERSet({
array: [
new KJUR.asn1.DERObjectIdentifier({oid: "1.2.3.4"}),
new KJUR.asn1.DERNull()
]
})
]
});
jsrsasign автоматически применяет DER-кодирование, включая:
Любая произвольная структура проходит сериализацию через
.getEncodedHex():
var hex = complex.getEncodedHex();
Этот метод возвращает конечное DER-представление.
При построении произвольных структур часто возникают ошибки:
DERSequence вместо DERSet в
протоколах, где важен порядок сортировкиКорректная модель всегда опирается на строгую композицию объектов, а не на ручную сборку байтов.
Для динамических структур удобно формировать фабрики узлов:
function makeAttr(typeOid, value) {
return new KJUR.asn1.DERSequence({
array: [
new KJUR.asn1.DERObjectIdentifier({oid: typeOid}),
new KJUR.asn1.DERUTF8String({str: value})
]
});
}
Такая модель позволяет собирать произвольные ASN.1-графы, сохраняя читаемость и управляемость структуры.
ASN.1 не накладывает ограничений на глубину дерева, что позволяет строить многослойные контейнеры:
jsrsasign корректно обрабатывает такие структуры, рекурсивно сериализуя каждый узел в DER-представление.