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