module OpenSSL::ASN1
Абстрактная синтаксическая нотация один (или ASN.1) — это нотация для описания структур данных, определённая в ITU-T X.680. Сам стандарт ASN.1 не устанавливает правила кодирования или разбора, однако структуры данных ASN.1 обычно кодируются с использованием правил Distinguished Encoding (DER) или, реже, правил Basic Encoding (BER), описанных в ITU-T X.690. Кодировки DER и BER — это двоичные кодировки Tag-Length-Value (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 предоставляет атрибуты для чтения и установки tag, tag_class и, наконец, value конкретного элемента 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, если значение было помечено неявно или явно. Подробную информацию о подклассах и соответствиях между типами данных ASN.1 и объектами Ruby см. в документации Primitive.
Допустимые значения 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) -
header length: суммарная длина заголовков Tag и Length в байтах. (
Integer) -
length: общая оставшаяся длина всех данных (
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.