модуль OpenSSL::ASN1
Абстрактная синтаксическая нотация один (или ASN.1) — это синтаксис для описания структур данных и определяется в ITU-T X.680. Сам ASN.1 не предписывает никаких правил кодирования или разбора, но обычно структуры данных ASN.1 кодируются с использованием правил Различительных Encoding (DER) или реже правил Основных Encoding (BER), описанных в ITU-T X.690. Кодирования DER и BER — это двоичные кодирования Тег-Длина-Значение (TLV), которые довольно компактны по сравнению с другими популярными форматами описания данных, такими как XML, JSON и т.д. Структуры данных ASN.1 очень распространены в криптографических приложениях, например, сертификаты открытого ключа X.509 или списки отозвания сертификатов (CRL) определяются в ASN.1 и кодируются в DER. ASN.1, DER и BER — это строительные блоки прикладной криптографии. Модуль ASN1 предоставляет необходимые классы для генерации структур данных ASN.1 и методы для их кодирования с использованием кодирования DER. Метод decode позволяет анализировать произвольные данные, закодированные в BER/DER, в объект Ruby, который затем можно изменять и перекодировать по своему усмотрению.
Иерархия классов ASN.1
Базовым классом, представляющим структуры ASN.1, является ASN1Data. ASN1Data предлагает атрибуты для чтения и установки тега, класса тега и, наконец, значения конкретного элемента ASN.1. При разборе любые помеченные значения (явные или неявные) будут представлены экземплярами ASN1Data, поскольку их «реальный тип» можно определить только с помощью информации вне контекста из объявления типа ASN.1. Поскольку эта информация обычно известна при кодировании типа, все подклассы ASN1Data предлагают дополнительный атрибут tagging, который позволяет кодировать значение неявно (:IMPLICIT) или явно (:EXPLICIT).
Constructive
Constructive — это, как следует из его названия, базовый класс для всех конструктивных кодирований, т.е. тех, которые состоят из нескольких значений, в отличие от «примитивных» кодирований с одним единственным значением. Значение Constructive всегда является Array.
ASN1::Set и ASN1::Sequence
Наиболее распространенными конструктивными кодированиями являются множества (SET) и последовательности (SEQUENCE), поэтому существуют два подкласса Constructive, представляющие каждый из них.
Primitive
Это суперкласс всех примитивных значений. Primitive сам по себе не используется при разборе данных ASN.1, все значения являются либо экземплярами соответствующего подкласса Primitive, либо экземплярами ASN1Data, если значение было помечено неявно или явно. См. документацию по Primitive для получения подробностей о подклассах и их соответствиях типов данных ASN.1 объектам Ruby.
Возможные значения для tagging
При построении объекта ASN1Data определение типа ASN.1 может потребовать, чтобы некоторые элементы были помечены неявно или явно. Это можно сделать, вручную установив атрибут tagging для подклассов ASN1Data. Используйте символ :IMPLICIT для неявной маркировки и :EXPLICIT , если элемент требует явной маркировки.
Возможные значения для tag_class
Можно создать произвольные объекты ASN1Data, которые также поддерживают класс тега PRIVATE или APPLICATION. Возможные значения для атрибута tag_class:
-
:UNIVERSAL(по умолчанию для значений без тегов) -
:CONTEXT_SPECIFIC(по умолчанию для помеченных значений) -
:APPLICATION -
:PRIVATE
Константы тегов
Для каждого универсального тега определена константа:
-
OpenSSL::ASN1::EOC (0)
-
OpenSSL::ASN1::BOOLEAN (1)
-
OpenSSL::ASN1::INTEGER (2)
-
OpenSSL::ASN1::BIT_STRING (3)
-
OpenSSL::ASN1::OCTET_STRING (4)
-
OpenSSL::ASN1::NULL (5)
-
OpenSSL::ASN1::OBJECT (6)
-
OpenSSL::ASN1::ENUMERATED (10)
-
OpenSSL::ASN1::UTF8STRING (12)
-
OpenSSL::ASN1::SEQUENCE (16)
-
OpenSSL::ASN1::SET (17)
-
OpenSSL::ASN1::NUMERICSTRING (18)
-
OpenSSL::ASN1::PRINTABLESTRING (19)
-
OpenSSL::ASN1::T61STRING (20)
-
OpenSSL::ASN1::VIDEOTEXSTRING (21)
-
OpenSSL::ASN1::IA5STRING (22)
-
OpenSSL::ASN1::UTCTIME (23)
-
OpenSSL::ASN1::GENERALIZEDTIME (24)
-
OpenSSL::ASN1::GRAPHICSTRING (25)
-
OpenSSL::ASN1::ISO64STRING (26)
-
OpenSSL::ASN1::GENERALSTRING (27)
-
OpenSSL::ASN1::UNIVERSALSTRING (28)
-
OpenSSL::ASN1::BMPSTRING (30)
UNIVERSAL_TAG_NAME константа
Array, хранящая имя заданного номера тега. Эти имена совпадают с именами констант тегов, которые также определены, например, UNIVERSAL_TAG_NAME[2] = "INTEGER" и OpenSSL::ASN1::INTEGER = 2.
Пример использования
Декодирование и просмотр файла, закодированного в DER
require 'openssl'
require 'pp'
der = File.binread('data.der')
asn1 = OpenSSL::ASN1.decode(der)
pp der
Создание структуры ASN.1 и кодирование ее в DER
require 'openssl'
version = OpenSSL::ASN1::Integer.new(1)
# Explicitly 0-tagged implies context-specific tag class
serial = OpenSSL::ASN1::Integer.new(12345, 0, :EXPLICIT, :CONTEXT_SPECIFIC)
name = OpenSSL::ASN1::PrintableString.new('Data 1')
sequence = OpenSSL::ASN1::Sequence.new( [ version, serial, name ] )
der = sequence.to_der
Константы
- CLASS_TAG_MAP
- UNIVERSAL_TAG_NAME
-
Array, хранящая имена тегов по их индексу.
Открытые методы класса
Исходный код
static VALUE
ossl_asn1_decode(VALUE self, VALUE obj)
{
VALUE ret;
unsigned char *p;
VALUE tmp;
long len, read = 0, offset = 0;
obj = ossl_to_der_if_possible(obj);
tmp = rb_str_new4(StringValue(obj));
p = (unsigned char *)RSTRING_PTR(tmp);
len = RSTRING_LEN(tmp);
ret = ossl_asn1_decode0(&p, len, &offset, 0, 0, &read);
RB_GC_GUARD(tmp);
int_ossl_decode_sanity_check(len, read, offset);
return ret;
} Декодирует значение, закодированное в BER или DER, и создает экземпляр ASN1Data. der может быть String или любым объектом, имеющим метод .to_der, преобразующий его в строку BER/DER.
Пример
der = File.binread('asn1data')
asn1 = OpenSSL::ASN1.decode(der)
Исходный код
static VALUE
ossl_asn1_decode_all(VALUE self, VALUE obj)
{
VALUE ary, val;
unsigned char *p;
long len, tmp_len = 0, read = 0, offset = 0;
VALUE tmp;
obj = ossl_to_der_if_possible(obj);
tmp = rb_str_new4(StringValue(obj));
p = (unsigned char *)RSTRING_PTR(tmp);
len = RSTRING_LEN(tmp);
tmp_len = len;
ary = rb_ary_new();
while (tmp_len > 0) {
long tmp_read = 0;
val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read);
rb_ary_push(ary, val);
read += tmp_read;
tmp_len -= tmp_read;
}
RB_GC_GUARD(tmp);
int_ossl_decode_sanity_check(len, read, offset);
return ary;
} Аналогично методу decode, но decode ожидает одно значение, представленное в der. В то время как decode_all, наоборот, декодирует последовательность последовательных значений BER/DER, выстроенных в der, и возвращает их как массив.
Пример
ders = File.binread('asn1data_seq')
asn1_ary = OpenSSL::ASN1.decode_all(ders)
Исходный код
static VALUE
ossl_asn1_traverse(VALUE self, VALUE obj)
{
unsigned char *p;
VALUE tmp;
long len, read = 0, offset = 0;
obj = ossl_to_der_if_possible(obj);
tmp = rb_str_new4(StringValue(obj));
p = (unsigned char *)RSTRING_PTR(tmp);
len = RSTRING_LEN(tmp);
ossl_asn1_decode0(&p, len, &offset, 0, 1, &read);
RB_GC_GUARD(tmp);
int_ossl_decode_sanity_check(len, read, offset);
return Qnil;
} Если задан блок, он выводит каждый из встреченных элементов. Параметры блока (в порядке следования):
-
depth: Глубина рекурсии, плюс один при каждом встреченном конструктивном значении (
Integer) -
offset: Текущий байтовый смещение (
Integer) -
длина заголовка: Объединенная длина заголовков тега и длины в байтах. (
Integer) -
длина: Общая оставшаяся длина всех данных (
Integer) -
constructed: Является ли это значение составным или нет (
Boolean) -
tag_class: Текущий класс тега (
Symbol) -
tag: Текущий номер тега (
Integer)
Пример
der = File.binread('asn1data.der')
OpenSSL::ASN1.traverse(der) do | depth, offset, header_len, length, constructed, tag_class, tag|
puts "Depth: #{depth} Offset: #{offset} Length: #{length}"
puts "Header length: #{header_len} Tag: #{tag} Tag class: #{tag_class} Constructed: #{constructed}"
end
Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.