ASN1HEX.parse и обход дерева

ASN.1 кодировка в JavaScript-библиотеке jsrsasign представляет собой один из ключевых механизмов работы с криптографическими структурами: сертификатами X.509, ключами, подписями и любыми DER-данными. В основе лежит необходимость разбирать бинарно-hex представление ASN.1 объектов в дерево, пригодное для программной обработки.

ASN.1 (Abstract Syntax Notation One) в DER-кодировке всегда имеет иерархическую структуру: последовательности (SEQUENCE) содержат вложенные элементы, которые, в свою очередь, могут быть простыми типами (INTEGER, OID, UTF8String) или снова контейнерами.

В jsrsasign эта структура при разборе hex-представления превращается в дерево узлов, где каждый узел содержит:

  • тег (tag) — тип ASN.1 элемента
  • позицию в hex-строке
  • длину значения
  • дочерние узлы (если элемент составной)

Такое представление позволяет выполнять навигацию по структуре без повторного декодирования байтов.

ASN1HEX.parse: базовый разбор hex-структуры

Функция ASN1HEX.parse предназначена для преобразования ASN.1 DER hex строки в структурированное дерево.

Типичный вызов:

var tree = ASN1HEX.parse(hex);

На выходе формируется массивоподобная структура, где каждый узел описывается набором параметров:

  • индекс начала узла
  • длина заголовка
  • длина содержимого
  • вложенные элементы

Внутренне структура напоминает AST (abstract syntax tree), где каждый элемент связан с подэлементами через индексы.

Пример логической структуры:

SEQUENCE
 ├── INTEGER
 ├── OID
 ├── SEQUENCE
 │    ├── UTF8String
 │    └── BIT STRING

Формат узлов ASN1HEX.parse

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

[nodeType, start, headerLength, valueLength, children]

где:

  • nodeType — ASN.1 тег (например, 0x30 для SEQUENCE)
  • start — позиция в hex-строке
  • headerLength — длина заголовка (tag + length bytes)
  • valueLength — длина полезных данных
  • children — массив дочерних узлов (если применимо)

Важно, что дерево строится лениво: вложенные элементы не всегда декодируются полностью сразу, а индексируются.

Навигация по ASN.1 дереву

После вызова ASN1HEX.parse основная работа заключается в обходе структуры. jsrsasign предоставляет несколько уровней доступа:

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

Получение дочерних узлов

Логика обхода основана на индексации:

var children = node[4];

Если элемент является SEQUENCE, children будет содержать массив вложенных узлов.

Рекурсивный обход дерева

Наиболее естественный способ анализа ASN.1 структуры — рекурсивный DFS (depth-first search).

function walk(node, depth) {
    var indent = "  ".repeat(depth);

    console.log(indent + "TAG: " + node[0]);
    console.log(indent + "POS: " + node[1]);

    if (node[4]) {
        for (var i = 0; i < node[4].length; i++) {
            walk(node[4][i], depth + 1);
        }
    }
}

Такой подход позволяет полностью развернуть ASN.1 структуру сертификата или ключа.

Итеративный обход через стек

Для больших структур предпочтителен обход без рекурсии:

function traverse(root) {
    var stack = [root];

    while (stack.length > 0) {
        var node = stack.pop();

        if (node[4]) {
            for (var i = 0; i < node[4].length; i++) {
                stack.push(node[4][i]);
            }
        }
    }
}

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

Извлечение значений узлов

ASN1HEX.parse сам по себе не возвращает интерпретированные значения. Для этого используется позиция и длина:

  • извлечение hex значения
  • декодирование строки
  • преобразование OID

Пример извлечения содержимого:

var valueHex = hex.substring(node[1] + node[2] * 2,
                             node[1] + (node[2] + node[3]) * 2);

Далее значение может быть преобразовано в читаемый формат:

  • INTEGER → число
  • OID → строка идентификатора
  • UTF8String → текст

Поиск конкретных структур в дереве

Частая задача — найти определённый OID или поле в сертификате.

Пример поиска OID:

function findOID(node, targetOID) {
    var results = [];

    function dfs(n) {
        if (n[0] === 0x06) { // OID tag
            var oid = ASN1HEX.oidHexToInt(n[5]);
            if (oid === targetOID) {
                results.push(n);
            }
        }

        if (n[4]) {
            for (var i = 0; i < n[4].length; i++) {
                dfs(n[4][i]);
            }
        }
    }

    dfs(node);
    return results;
}

Работа с сертификатами X.509 через ASN.1 дерево

ASN1HEX.parse особенно полезен при анализе X.509 сертификатов:

  • Subject
  • Issuer
  • Validity
  • Public Key Info
  • Extensions

Каждый из этих блоков является SEQUENCE внутри основного дерева.

Пример логики:

Certificate
 ├── TBSCertificate
 │    ├── Version
 │    ├── Serial Number
 │    ├── Signature
 │    ├── Issuer
 │    ├── Validity
 │    ├── Subject
 │    ├── SubjectPublicKeyInfo
 │    └── Extensions
 ├── SignatureAlgorithm
 └── SignatureValue

ASN1HEX.parse позволяет пройти эту структуру без использования высокоуровневых парсеров.

Оптимизация обхода

При работе с большими ASN.1 структурами важно учитывать:

  • минимизацию substring операций
  • кеширование результатов getChildIdx
  • отказ от повторного парсинга hex
  • использование итеративных обходов вместо рекурсии при глубокой вложенности

Особенно критично это при обработке цепочек сертификатов и CRL.

Типичные ошибки при работе с ASN1HEX.parse

  • неверная интерпретация индексов (hex vs byte offset)
  • попытка декодировать primitive как constructed
  • игнорирование длины заголовка
  • повторный парсинг одного и того же hex

ASN.1 структура чувствительна к смещению, и любая ошибка в расчётах индексов приводит к разрушению дерева.

Сопоставление узлов с криптографическими сущностями

Каждый ASN.1 узел можно связать с криптографической логикой:

  • SEQUENCE → структура сертификата или ключа
  • INTEGER → RSA modulus или serial number
  • OID → алгоритмы (RSA, SHA256, ECDSA)
  • BIT STRING → публичный ключ
  • UTC TIME → сроки действия

Обход дерева позволяет вручную реконструировать смысл сертификата без использования готовых API jsrsasign верхнего уровня.

Использование дерева для низкоуровневого анализа

ASN1HEX.parse применяется там, где требуется:

  • проверка нестандартных сертификатов
  • анализ повреждённых ASN.1 структур
  • извлечение скрытых расширений
  • аудит криптографических данных
  • отладка DER кодировок

Работа на уровне дерева даёт полный контроль над структурой, минуя абстракции библиотек высокого уровня