Module: Krypt::ASN1
- Defined in:
- ext/krypt/core/krypt_asn1.c,
ext/krypt/core/krypt_asn1.c,
ext/krypt/core/krypt_asn1_parser.c,
ext/krypt/core/krypt_asn1_in_adapter.c
Overview
Abstract Syntax Notation One (or ASN.1) is a notation syntax to describe data structures and is defined in ITU-T X.680. ASN.1 itself does not mandate any encoding or parsing rules, but usually ASN.1 data structures are encoded using the Distinguished Encoding Rules (DER) or less often the Basic Encoding Rules (BER) described in ITU-T X.690. DER and BER encodings are binary Tag-Length-Value (TLV) encodings that are quite concise compared to other popular data description formats such as XML, JSON etc. ASN.1 data structures are very common in cryptographic applications, e.g. X.509 public key certificates or certificate revocation lists (CRLs) are all defined in ASN.1 and DER-encoded. ASN.1, DER and BER are the building blocks of applied cryptography. The ASN1 module provides the necessary classes that allow generation of ASN.1 data structures and the methods to encode them using a DER encoding. The decode method allows parsing arbitrary BER-/DER-encoded data to a Ruby object that can then be modified and re-encoded at will.
BER encodings of a parsed value are preserved when re-encoding them in order to avoid breaking digital signatures that were computed over these encodings. Once a parsed value is replaced by another manually, the new value will be encoded in DER format, regardless of the previous encoding of the old value.
ASN.1 class hierarchy
The base class representing ASN.1 structures is ASN1Data. ASN1Data offers attributes to read and set the tag
, the tag_class
and finally the value
of a particular ASN.1 item. Upon parsing, any tagged values (implicit or explicit) will be represented by ASN1Data instances because their “real type” can only be determined using out-of-band information from the ASN.1 type declaration.
Constructive
Constructive is, as its name implies, the base class for all constructed encodings, i.e. those that consist of several values, opposed to “primitive” encodings with just one single value. Primitive values that are encoded with “infinite length” are typically constructed (their values come in multiple chunks) and are therefore represented by instances of Constructive. The value of a parsed Constructive is always an Array.
ASN1::Set and ASN1::Sequence
The most common constructive encodings are SETs and SEQUENCEs, which is why there are two sub-classes of Constructive representing each of them.
Primitive
This is the super class of all primitive values. Primitive itself is not used when parsing ASN.1 data, all values are either instances of a corresponding sub-class of Primitive or they are instances of ASN1Data if the value was tagged implicitly or explicitly. Please cf. Primitive documentation for details on sub-classes and their respective mappings of ASN.1 data types to Ruby objects.
Possible values for tag_class
It is possible to create arbitrary ASN1Data objects that also support a PRIVATE or APPLICATION tag class. Possible values for the tag_class
attribute are:
-
:UNIVERSAL
(the default for untagged values) -
:CONTEXT_SPECIFIC
(the default for tagged values) -
:APPLICATION
-
:PRIVATE
Additionally the following two may be used:
-
:IMPLICIT
-
:EXPLICIT
where :IMPLICIT
is simply a synonym for :CONTEXT_SPECIFIC
, and exists mostly for convenience reasons to match real ASN.1 definitions more closely. :EXPLICIT
on the other hand can be thought of as a hint for encoding an ASN1Data from scratch. Neither :IMPLICIT
nor :EXPLICIT
will ever be assigned during parsing. Both translate to :CONTEXT_SPECIFIC
eventually when being encoded. The difference is that :EXPLICIT
will force the corresponding value to be encoded with explicit tagging, whereas :IMPLICIT
, you guessed right, enforces implicit tagging, in the same way that :CONTEXT_SPECIFIC
does.
Tag constants
There is a constant defined for each universal tag:
-
Krypt::ASN1::EOC (0)
-
Krypt::ASN1::BOOLEAN (1)
-
Krypt::ASN1::INTEGER (2)
-
Krypt::ASN1::BIT_STRING (3)
-
Krypt::ASN1::OCTET_STRING (4)
-
Krypt::ASN1::NULL (5)
-
Krypt::ASN1::OBJECT (6)
-
Krypt::ASN1::ENUMERATED (10)
-
Krypt::ASN1::UTF8STRING (12)
-
Krypt::ASN1::SEQUENCE (16)
-
Krypt::ASN1::SET (17)
-
Krypt::ASN1::NUMERICSTRING (18)
-
Krypt::ASN1::PRINTABLESTRING (19)
-
Krypt::ASN1::T61STRING (20)
-
Krypt::ASN1::VIDEOTEXSTRING (21)
-
Krypt::ASN1::IA5STRING (22)
-
Krypt::ASN1::UTCTIME (23)
-
Krypt::ASN1::GENERALIZEDTIME (24)
-
Krypt::ASN1::GRAPHICSTRING (25)
-
Krypt::ASN1::ISO64STRING (26)
-
Krypt::ASN1::GENERALSTRING (27)
-
Krypt::ASN1::UNIVERSALSTRING (28)
-
Krypt::ASN1::BMPSTRING (30)
UNIVERSAL_TAG_NAME constant
An Array that stores the name of a given tag number. These names are the same as the name of the tag constant that is additionally defined, e.g. UNIVERSAL_TAG_NAME = “INTEGER” and Krypt::ASN1::INTEGER = 2.
Example usage
Decoding and viewing a DER-encoded file
require 'krypt'
require 'pp'
File.open('data.der', 'rb') do |f|
pp Krypt::ASN1.decode(f)
end
Creating an ASN.1 structure and DER-encoding it
require 'krypt'
version = Krypt::ASN1::Integer.new(1)
# 0-tagged with context-specific tag class
serial = Krypt::ASN1::Integer.new(12345, 0, :CONTEXT_SPECIFIC)
name = Krypt::ASN1::PrintableString.new('Data 1')
sequence = Krypt::ASN1::Sequence.new( [ version, serial, name ] )
der = sequence.to_der
Defined Under Namespace
Modules: Template Classes: ASN1Data, ASN1Error, Constructive, Header, Instream, ParseError, Parser, Primitive, SerializeError
Constant Summary collapse
- UNIVERSAL_TAG_NAME =
Array storing tag names at the tag’s index.
ary
Class Method Summary collapse
-
.decode(src) ⇒ ASN1Data
-
src
: May either be aString
containing a DER-/PEM-encoded value, an IO-like object supporting IO#read and IO#seek or any arbitrary object that supports either ato_der
or ato_pem
method transforming it into a DER-/BER-encoded or PEM-encodedString
.
-
-
.decode_der(der) ⇒ ASN1Data
-
der
: May either be aString
containing a DER-encoded value, an IO-like object supporting IO#read and IO#seek or any arbitrary object that supports ato_der
method transforming it into a DER-/BER-encodedString
.
-
-
.decode_pem(pem) ⇒ ASN1Data
-
pem
: May either be aString
containing a PEM-encoded value, an IO-like object supporting IO#read and IO#seek or any arbitrary object that supports ato_pem
method transforming it into a PEM-encodedString
.
-
Class Method Details
.decode(src) ⇒ ASN1Data
-
src
: May either be aString
containing a DER-/PEM-encoded value, anIO-like object supporting IO#read and IO#seek or any arbitrary object that supports either a +to_der+ or a +to_pem+ method transforming it into a DER-/BER-encoded or PEM-encoded +String+.
Decodes arbitrary DER- or PEM-encoded ASN.1 objects and returns an instance (or a subclass) of ASN1Data.
Examples
io = File.open("my.der", "rb")
asn1 = Krypt::ASN1.decode(io)
io.close
str = #some PEM-encoded string
asn1 = Krypt::ASN1.decode(str)
tagged = Krypt::ASN1::Integer.new(1, 0, :CONTEXT_SPECIFIC)
tagged.tag = Krypt::ASN1::INTEGER
tagged.tag_class = :UNIVERSAL
int = Krypt::ASN1.decode(tagged)
puts int.tag # => 2
puts int.tag_class # => :UNIVERSAL
puts int.value # => 1
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 |
# File 'ext/krypt/core/krypt_asn1.c', line 1531
static VALUE
krypt_asn1_decode(VALUE self, VALUE obj)
{
binyo_instream *in;
binyo_instream *cache;
binyo_instream *pem;
VALUE ret;
/* Try PEM first, if it fails, try as DER */
in = krypt_instream_new_value_der(obj);
cache = binyo_instream_new_cache(in);
pem = krypt_instream_new_pem(cache);
if (krypt_asn1_decode_stream(pem, &ret) != KRYPT_OK) {
krypt_instream_pem_free_wrapper(pem);
return int_asn1_fallback_decode(in, cache);
}
binyo_instream_free(pem); /* also frees in */
return ret;
}
|
.decode_der(der) ⇒ ASN1Data
-
der
: May either be aString
containing a DER-encoded value, anIO-like object supporting IO#read and IO#seek or any arbitrary object that supports a +to_der+ method transforming it into a DER-/BER-encoded +String+.
Decodes a DER-encoded ASN.1 object and returns an instance (or a subclass) of ASN1Data. Can be used in the same way as ASN1Data#decode, except that decode_der
explicitly assumes a DER-encoded source.
1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 |
# File 'ext/krypt/core/krypt_asn1.c', line 1564
static VALUE
krypt_asn1_decode_der(VALUE self, VALUE obj)
{
VALUE ret;
int result;
binyo_instream *in = krypt_instream_new_value_der(obj);
result = krypt_asn1_decode_stream(in, &ret);
binyo_instream_free(in);
if (result != KRYPT_OK)
krypt_error_raise(eKryptASN1Error, "Error while DER-decoding value");
return ret;
}
|
.decode_pem(pem) ⇒ ASN1Data
-
pem
: May either be aString
containing a PEM-encoded value, anIO-like object supporting IO#read and IO#seek or any arbitrary object that supports a +to_pem+ method transforming it into a PEM-encoded +String+.
Decodes a PEM-encoded ASN.1 object and returns an instance (or a subclass) of ASN1Data. Can be used in the same way as ASN1Data#decode, except that decode_pem
explicitly assumes a PEM-encoded source.
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 |
# File 'ext/krypt/core/krypt_asn1.c', line 1591
static VALUE
krypt_asn1_decode_pem(VALUE self, VALUE obj)
{
VALUE ret;
int result;
binyo_instream *pem;
pem = krypt_instream_new_pem(krypt_instream_new_value_pem(obj));
result = krypt_asn1_decode_stream(pem, &ret);
binyo_instream_free(pem);
if (result != KRYPT_OK)
krypt_error_raise(eKryptASN1Error, "Error while PEM-decoding value");
return ret;
}
|