модуль 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 = “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
Константы
- 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;
} Если блок задан, он выводит каждый из встреченных элементов. Параметры блока (в указанном порядке):
-
глубина: Глубина рекурсии, плюс один при каждом встреченном конструктивном значении (Целое число)
-
смещение: Текущее смещение байта (Целое число)
-
длина заголовка: Объединенная длина в байтах заголовков тега и длины. (Целое число)
-
длина: Общая оставшаяся длина всех данных (Целое число)
-
конструктивная: Является ли это значение конструктивным или нет (Булево)
-
класс_тега: Текущий класс тега (Символ)
-
тег: Текущий номер тега (Целое число)
Пример
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.