
Indispensable ASN.1 Engine
This Kotlin Multiplatform library provides the most sophisticated KMP ASN.1 engine in the known universe. kotlinx-* dependencies aside, it only depends only on KmmResult for extra-smooth iOS interop. It features:
- ASN.1 Parser and Encoder including a DSL to generate ASN.1 structures
- ObjectIdentifier Class with human-readable notation (e.g. 1.2.9.6245.3.72.13.4.7.6)
- ASN.1 Integer (variable length integer)
- 100% pure Kotlin BitSet
- Generic ASN.1 abstractions to operate on and create arbitrary ASN.1 Data
- Support for all targets but wasm/WASI (due to Kotest not supporting it)
This in short, you can work with arbitrary ASN.1 structures anywhere!
Tip
Do check out the full API docs here!
Using it in your Projects
This library was built for Kotlin Multiplatform. Currently, it supports all KMP targets.
Simply declare the desired dependency to get going:
Structure and Class Overview
As the name Indispensable ASN.1 implies, this module is indispensable, if you work with ASN.1 structures.
Package Organisation
The asn1 package contains a 100% pure Kotlin (read: no platform dependencies) ASN.1 engine and data types:
Asn1Elements.ktcontains all ASN.1 element typesAsn1Elementis an abstract, generic ASN.1 element. Has a tag and content. Can be DER-encodedAsn1Element.Tagrepresenting an ASN.1 tag. Contains user-friendly representations of:- Tag number
CONSTRUCTEDbit- Tag Class
- A set of predefined tag constants that are often encountered such as
INTEGER,OCTET STRING,BIT STRING, etc…
Asn1Primitiveis an ASN.1 element containing primitive data (string, byte strings, numbers, null, …)Asn1Structureis aCONSTRUCTEDASN.1 type, containing zero or more ASN.1 child elementsAsn1Sequencehas sequence semantics (order-preserving!)Asn1SequenceOfhas sequence semantics but allows only child nodes of the same tagAsn1Sethas set semantics, i.e. sorts all child nodes by tag in accordance with DERAsn1SetOfhas set semantics but allows only child nodes of the same tag
In addition, some convenience types are also present:
Asn1ExplicitlyTagged, which is essentially a sequence, but with a user-definedCONTEXT_SPECIFICtagAsn1BitString, wich is an ASN.1 primitive containing bit strings, which are not necessarily byte-aligned. Heavily relies on the includedBitSettype to work its magic.Asn1OctetString, wich is often encountered in one of two flavours:Asn1PrimitiveOctetStringcontaining raw bytesAsn1EncapsulatingOctetStringcontaining any number of children. This is a structure, without theCONSTRUCTEDbit set, using tag number4.
Asn1CustomStructurerepresenting a structure with a custom tag, that does not align with any predefined tag. Can be constructed to auto-sort children to conform with DER set semantics.ObjectIdentifierrepresents an ASN.1 OIDAsn1Stringcontains different String types (printable, UTF-8, numeric, …)Asn1Timemaps from/to kotlinx-datetimeInstants and supports both UTC time and generalized time
The asn1.encoding package contains the ASN.1 builder DSL, as well as encoding and decoding functions
-- both for whole ASN.1 elements, as well as for encoding/decoding primitive data types to/from DER-conforming byte arrays.
Most prominently, it comes with ASN.1 unsigned varint and minimum-length encoding of signed numbers.
ASN.1 Core
The ASN.1 engine allows decoding and encoding arbitrary structures from/to ByteArrays, as well as kotlinx.io Source and Sink.
Relevant Indispensable classes like CryptoPublicKey, X509Certificate, Pkcs10CertificationRequest, etc. all
implement Asn1Encodable and their respective companions implement Asn1Decodable.
This is an essential pattern, making the ASN.1 engine work the way it does.
We have opted against using kotlinx.serialization for maximum flexibility and more convenient debugging.
The following section provides more details on the various patterns used for ASN.1 encoding and decoding.
Generic Patterns
Recalling the classes in the asn1 package described before already hints how ASN.1 elements are constructed.
In effect, it is just a nesting of those classes.
This works well for parsing and encoding but lacks higher-level semantics (in contrast to X509Certificate from the Indispensable module, for example).
Decoding
Decoding functions come in two categories: high-level functions, wich are used to map ASN.1 elements to types with enriched semantics (such as certificates, public keys, etc.) and low-level ones, operating on the encoded values of TLV structures (i.e. decoding the V in TLV). Hence, a typical decoding pipeline looks as follows:
| Encoded Bytes | ––––→ | ASN.1 Element | ––––→ | Asn1Encodable rich type |
|---|---|---|---|---|
06052B81040022 |
Asn1Element.parse() |
Primitive(tag=6 (=06), length=5, overallLength=7) 2B81040022 |
ObjectIdentifier.decodeFromTlv() |
ObjectIdentifier("1.3.132.0.34") |
High-Level
Tip
Structured, iterator-based decoding of Asn1Structure has been introduced, by letting Asn1Structure implement Iterable<Asn1Element>.
The functions nextChild(), nextChildOrNull(), hasMoreChildren(), and peek() are now deprecated in favor of the new iterator approach.
Child access is now performed using the inner iterator, which provides the following methods: next(), nextOrNull(), hasNext(), peek(), and reversed().
The iterator also exposed the Asn1Structure it belongs to through the containingStructure property.
Asn1Decodable provides the following functions for decoding data:
doDecode(), which is the only function that needs to be implemented by high-level types implementingAsn1Encodable. To provide a concrete example: This function needs to contain all parsing/decoding logic to construct aCryptoPublicKeyfrom anAsn1Sequence, as demonstrated in the Indispensable source code.verifyTag()already implements optional tag assertion. The default implementation ofdecodeFromTlv()(see below) calls this before invokingdoDecode().decodeFromTlv()takes an ASN.1 element and optional tag to assert, and returns a high-level type. Throws!decodeFromTlvSafe()does not throw, but returns a KmmResult, encapsulating the result ofdecodeFromTlv()decodeFromTlvorNull()does not throw, but returns null when decoding failsdecodeFromDer()takes DER-encoded bytes, parses them into an ASN.1 element and callsdecodeFromTlv(). Throws!decodeFromDerSafe()takes DER-encoded bytes. Does not throw, but returns a KmmResult, encapsulating the result ofdecodeFromDer()decodeFromDerOrNull()takes DER-encoded bytes. Does not throw, but returns null on decoding errors.
In addition, the companion of Asn1Element exposes the following functions:
parse()parses a single ASN.1 element from the input and throws on error, or when additional input is left after parsing. This ensures that the input contains a single, top-level ASN.1 element.parseAll()consumes all input and returns a list of parsed ASN.1 elements. Throws on error.Source.readAsn1Element()decodes a single ASN.1 element (can be a structure or a primitive) from a kotlix.io Source.parseFirst()comes in two flavours, both of which parse only a single, top-level ASN.1 element from the passed input- Variant 1 takes a
Sourceand advances it until after the first parsed element. - Variant 2 takes a
ByteArrayand returns the first parses element, as well as the remaining bytes (asPair<Asn1Element, ByteArray>)
- Variant 1 takes a
decodeFromDerHexString()strips all whitespace before trying to decode an ASN.1 element from the provided hex string. This function throws various exceptions on illegal input. Has the same semantics asparse().
All of these return one or more Asn1Elements, which can then be passed to decodeFromTlv() if desired.
Low-level decoding functions deal with the actual decoding of payloads in TLV structures.
Low-Level
Some low-level decoding functions are implemented as extension functions in Asn1Primitive for convenience (since CONSTRUCTED elements contain child nodes, but no raw data).
The base decoding function is called decode() and has the following signature:
Tag instead of an Ulong. in both cases a tag to assert and a user-defined transformation function is expected, which operates on
the content of the ASN.1 primitive. Moreover, a non-throwing decodeOrNull variant is present.
In addition, the following self-describing shorthands are defined:
| Function | Description |
|---|---|
Asn1Primitive.decodeToBoolean() |
throws |
Asn1Primitive.decodeToBooleanOrNull() |
returns null on error |
Asn1Primitive.decodeToInt() |
throws |
Asn1Primitive.decodeToIntOrNull() |
returns null on error |
Asn1Primitive.decodeToLong() |
throws |
Asn1Primitive.decodeToLongOrNull() |
returns null on error |
Asn1Primitive.decodeToUInt() |
throws |
Asn1Primitive.decodeToUIntOrNull() |
returns null on error |
Asn1Primitive.decodeToULong() |
throws |
Asn1Primitive.decodeToULongOrNull() |
returns null on error |
Asn1Primitive.decodeToDouble() |
throws |
Asn1Primitive.decodeToDoubleOrNull() |
returns null on error |
Asn1Primitive.decodeToFloat() |
throws |
Asn1Primitive.decodeToFloatOrNull() |
returns null on error |
Asn1Primitive.decodeToAsn1Integer() |
throws |
Asn1Primitive.decodeToAsn1IntegerOrNull() |
returns null on error |
Asn1Primitive.decodeToAsn1Real() |
throws |
Asn1Primitive.decodeToAsn1RealOrNull() |
returns null on error |
Asn1Primitive.decodeToEnumOrdinal() |
throws |
Asn1Primitive.decodeToEnumOrdinalOrNull() |
returns null on error |
Asn1Primitive.decodeToEnum() |
throws |
Asn1Primitive.decodeToEnumOrNull() |
returns null on error |
Asn1Primitive.decodeToEnumOrdinal() |
throws |
Asn1Primitive.decodeToEnumOrdinalOrNull() |
returns null on error |
Asn1Primitive.decodeToString() |
throws |
Asn1Primitive.decodeToStringOrNull() |
returns null on error |
Asn1Primitive.decodeToInstant() |
throws |
Asn1Primitive.decodeToInstantOrNull() |
returns null on error |
Asn1Primitive.readNull() |
validates that the ASN.1 primitive is indeed an ASN.1 NULL. throws on error |
Asn1Primitive.readNullOrNull() |
validates that the ASN.1 primitive is indeed an ASN.1 NULL. returns null on error |
In addition, an asAsn1String() conversion function exists that checks an ASN.1 primitive's tag and returns the correct Asn1String subtype (UTF-8, NUMERIC, BMP, …).
Manually working on DER-encoded payloads is also supported through the following extensions (each taking a ByteArray as input):
Int.decodeFromAsn1ContentBytes()UInt.decodeFromAsn1ContentBytes()Long.decodeFromAsn1ContentBytes()ULong.decodeFromAsn1ContentBytes()Double.decodeFromAsn1ContentBytes()Float.decodeFromAsn1ContentBytes()Asn1Integer.decodeFromAsn1ContentBytes()Asn1Real.decodeFromAsn1ContentBytes()Boolean.decodeFromAsn1ContentBytes()String.decodeFromAsn1ContentBytes()Instant.decodeGeneralizedTimeFromAsn1ContentBytes()Instant.decodeUtcTimeFromAsn1ContentBytes()
All of these functions throw an Asn1Exception when decoding fails.
Moreover, a generic tag assertion function is present on Asn1Element, which throws an Asn1TagMisMatchException on error
and returns the tag-asserted element on success:
Asn1Element.assertTag()takes either anAsn1Element.Tagor anULongtag number
Encoding
Similarly to decoding function, encoding function also come as high-level and low-level ones.
The general idea is the same: Asn1Encodable should be implemented by any custom type that needs encoding to ASN.1,
while low-level encoding functions create the raw bytes contained in an Asn1Primitive.
Hence, a typical encoding pipeline looks as follows:
Asn1Encodable rich type |
––––→ | ASN.1 Element | ––––→ | Encoded Bytes |
|---|---|---|---|---|
ObjectIdentifier("1.3.132.0.34") |
oid.encodeToTlv() |
Primitive(tag=6 (=06), length=5, overallLength=7) 2B81040022 |
Asn1Element.derEncoded |
06052B81040022 |
High-Level
Asn1Encodable defines the following functions:
encodeToTlv()is the only function that need to be implemented. It defines how user-defined types are converted to an ASN.1 element. Throws on error.encodeToTlvOrNull()is a non-throwing variant of the above, returningnullon error.encodeToTlvOrSafe()encapsulates the encoding result into aKmmResult.encodeToDer()invokesencodeToTlv().derEncodedto produce aByteArrayconforming to DER. Throws on error.encodeToDerOrNull()is a non-throwing variant of the above, returningnullon error.encodeToDerSafe()encapsulates the encoding result into aKmmResult.
Asn1Element and its subclasses come with the lazily-evaluated property derEncoded which produces a ByteArray conforming to DER.
Low-Level
Low-level encoding functions come in two flavours:
On the one hand, functions exist to produce correctly tagged ASN.1 primitives exist, including tag, length, and the encoded value.
On the other hand, there are functions responsible for producing only the content bytes of an Asn1Primitive. The first kind of functions rely on this second kind to encode values.
Both kind of encoding functions follow a simple naming convention:
encodeToAsn1Primitive()produces an ASN.1 primitive corresponding to the input. This is implemented forInt,UInt,Long,ULong,Double,Float,Asn1Integer,Asn1Real,Boolean,EnumandStringencodeToAsn1ContentBytes()producing the content bytes of anAsn1Primitive. This is implemented forInt,UInt,Long,ULong,Double,Float,Asn1Integer,Asn1Real,Boolean, andEnum.- As for strings: An UTF-8 string is just its bytes.
In addition, some more specialized encoding functions exist for cases that are not as straight-forward:
ByteArray.encodeToAsn1OctetStringPrimitive()produces an ASN.1 OCTET STRING containing the source bytes.ByteArray.encodeToAsn1BitStringPrimitive()produces an ASN.1 BIT STRING, prepending the source bytes with a single0x00byte.ByteArray.encodeToAsn1BitStringContentBytes()produces aByteArraycontaining the source bytes, prepended with a single0x00byte.Instant.encodeToAsn1UtcTimePrimitive()produces an ASN.1 UTC TIME primitiveInstant.encodeToAsn1GeneralizedTimePrimitive()produces an ASN.1 GENERALIZED TIME primitive
Custom Tagging
This library comes with extensive tagging support and an expressive Asn1Element.Tag class.
ASN.1 knows EXPLICIT and IMPLICIT tags.
The former is simply a structure with SEQUENCE semantics and a user-defined CONSTRUCTED, CONTEXT_SPECIFIC tag, while the latter replaces an ASN.1 element's tag.
Explicit
To explicitly tag any number of elements, simply invoke Asn1.ExplicitlyTagged, set the desired tag and add the desired elements using the ASN.1 builder DSL:
To create an explicit tag (to compare it to a parsed, explicitly tagged element, for example), just pass tag number (and optionally) tag class to Asn1.ExplicitTag.
Implicit
Implicit tagging is implemented differently. Any element can be implicitly tagged, after it was constructed, by invoking the
withImplicitTag infix function on it. There's, of course, also an option to override the tag class.
Creating an implicitly tagged UTF-8 String using the ASN.1 builder DSL with a custom tag class works as follows:
It is also possible to unset the CONSTRUCTED bit from any ASN.1 structure or Tag by invoking the infix function without as follows:
Warning
It is perfectly possible to use abuse implicit tagging in ways that produces UNIVERSAL tags that are reserved for well-defined types. If you really think you must create a faux ASN.1 NULL from an X.509 certificate go ahead, we dare you! Just blame the mess you created only on yourself and nobody else!
Object Identifiers
Signum's Indispensable ASN.1 engine comes with an expressive, convenient, and efficient ASN.1 ObjectIdentifier class.
It can be constructed by either parsing a ByteArray containing ASN.1-encoded representation of an OID,
or constructing it from a humanly-readable string representation ("1.2.96", "1 2 96").
In addition, it is possible to pass OID node components as either UInt or decimal string representation to construct an OID:
ObjectIdentifier(1u, 3u, 6u, 1u), ObjectIdentifier("1.2.3.234567898765434567").
The OID class exposes a nodes property, corresponding to the individual components that make up an OID node for convenience,
as well as a bytes property, corresponding to its ASN.1-encoded ByteArray representation.
One peculiar characteristic of the ObjectIdentifier class is that both nodes and bytes properties are lazily evaluated.
This means that if the OID was constructed from raw bytes, accessing bytes is a NOOP, but operating on nodes is initially
quite expensive, since the bytes have yet to be parsed.
Conversely, if an OID was constructed from a string, accessing bytes is slow.
If, however, an OID was constructed from UInt components, those are eagerly encoded into bytes and the nodes property
is not immediately initialized.
Finally, it is possible to directly construct an OID from a Uuid, which directly constructs an OID in Subtree 2.35, which
takes the same path as evaluating a String, but with some shortcuts.
This lazy-evaluation behaviour boils down to performance: Only very rarely will you want to create an OID with components exceeding UInt.MAX_VALUE,
but you will almost certainly want to encode an OID you created to ASN.1.
On the other hand, parsing an OID from ASN.1-encoded bytes and re-encoding it are both close to a NOOP (object creation aside).
For debug purposes, it is possible to add descriptions to OIDs (or override existing ones) by associating an OID with its description.
For example, using KnownOIDs[theExpressionistsOid] = "Edvard Munch" will cause KnownOIDs[theExpressionistsOid] to yield "Edvard Munch".
ASN.1 Integer
The ASN.1 engine provides its own bigint-like class, Asn1Integer. It is capable of encoding arbitrary length signed integers
to write and read them from ASN.1 structures.
It natively supports encoding from/to a two's complement ByteArray, and sign + magnitude representation,
making it interoperable with Kotlin MP BigNum
and JVM's BigInteger.
ASN.1 Real
The ASN.1 engine provides its variable-precision floating-point class, Asn1Real. It is capable of encoding arbitrary
length signed floating point numbers to write and read them from ASN.1 structures.
It natively supports encoding from/to Kotlin's built-in Float and Double.
Encoding and Decoding a Kotlin double-precision floating point number will result in the same Double.
However, an ASN.1 REAL can use a higher precision than 64 bit. Hence, decoding arbitrary ASN.1 REAL numbers to Double
can result in a loss of precision.
When decoding to Float, this is even more likely to happen.
To avoid this, simply keep the Asn1Real as-is.
ASN.1 Builder DSL
So far, custom high-level types and manually constructing low-level types was discussed.
When actually constructing ASN.1 structures, a far more streamlined and intuitive approach exists.
Signum's Indispensable ASN.1 engine comes with a powerful, expressive ASN.1 builder DSL, including shorthand functions
covering CONSTRUCTED types and primitives.
Everything is grouped under a namespace object called Asn1. It not only streamlines the creation of complex ASN.1
structures, but also provides maximum flexibility. The following snippet showcases how it can be used in practice:
Asn1.Sequence {
+ExplicitlyTagged(1uL) {
+Asn1Primitive(Asn1Element.Tag.BOOL, byteArrayOf(0x00)) //or +Asn1.Bool(false)
}
+Asn1.Set {
+Asn1.Sequence {
+Asn1.SetOf {
+PrintableString("World")
+PrintableString("Hello")
}
+Asn1.Set {
+PrintableString("World")
+PrintableString("Hello")
+Utf8String("!!!")
}
}
}
+Asn1.Null()
+ObjectIdentifier("1.2.603.624.97")
+(Utf8String("Foo") withImplicitTag (0xCAFEuL withClass TagClass.PRIVATE))
+PrintableString("Bar")
// ↓ faux primitive ↓
+(Asn1.Sequence { +Asn1.Int(42) } withImplicitTag (0x5EUL without CONSTRUCTED))
+Asn1.Set {
+Asn1.Int(3)
+Asn1.Int(-65789876543L)
+Asn1.Bool(false)
+Asn1.Bool(true)
}
+Asn1.Sequence {
+Asn1.Null()
+Asn1String.Numeric("12345")
+UtcTime(Clock.System.now())
}
} withImplicitTag (1337uL withClass TagClass.APPLICATION)
This produces the following ASN.1 structure:
Application 1337 (9 elem)
[1] (1 elem)
BOOLEAN false
SET (1 elem)
SEQUENCE (2 elem)
SET (2 elem)
PrintableString World
PrintableString Hello
SET (3 elem)
UTF8String !!!
PrintableString World
PrintableString Hello
NULL
OBJECT IDENTIFIER 1.2.603.624.97
Private 51966 (3 byte) Foo
PrintableString Bar
[94] (3 byte) 02012A
SET (4 elem)
BOOLEAN false
BOOLEAN true
INTEGER 3
INTEGER (36 bit) -65789876543
SEQUENCE (3 elem)
NULL
NumericString 12345
UTCTime 2024-09-16 11:53:51 UTC
You can, of course, also create primitives, by directly invoking builder functions, like Asn1.Int() and use the resulting
ASN.1 primitive as-is.
Tip
The builder also takes any Asn1Encodable, so you can also add an X509Certificate, or a CryptoPublicKey using
the same concise syntax.
Do checkout the API docs for a full list of builder functions!
Debugging and Pretty-Printing
Signum's ASN.1 engine provides pretty-printing capabilities:
Asn1Encodable and Asn1Element feature a prettyPrint() function.
The output of this function is very verbose and aimed at humans debugging ASN.1 structures.
It also ties in with OID descriptions;
any OIDs described using KnownOIDs will be annotated.
You likely want to call KnownOIDs.describeAll() (a part of indispensable's ASN.1 addons) before pretty printing.
For tags that are well-known, pretty-printing will automatically decode contents on a best-effort basis and display them.
A pretty-printed certificate will look as follows:
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=305, overallLength=309)
{
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=216, overallLength=219)
{
Tagged(tag=CONTEXT_SPECIFIC 0 CONSTRUCTED (=A0), length=3, overallLength=5)
{
Primitive(tag=2 (=02) (INTEGER), length=1, overallLength=3) 2
}
Primitive(tag=2 (=02) (INTEGER), length=8, overallLength=10) Non-complaint content: 0x3546897BCA2ACCCB
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=10, overallLength=12)
{
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=8, overallLength=10) ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256) (1.2.840.10045.4.3.2)
}
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=31, overallLength=33)
{
Set(tag=17 CONSTRUCTED (=31) (SET), length=29, overallLength=31)
{
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=27, overallLength=29)
{
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=3, overallLength=5) commonName (X.520 DN component) (2.5.4.3)
Primitive(tag=12 (=0C) (UTF8 STRING), length=20, overallLength=22) DefaultCryptoService
}
}
}
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=30, overallLength=32)
{
Primitive(tag=23 (=17) (UTC TIME), length=13, overallLength=15) 2025-07-09T16:36:43Z
Primitive(tag=23 (=17) (UTC TIME), length=13, overallLength=15) 2025-08-08T16:36:43Z
}
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=31, overallLength=33)
{
Set(tag=17 CONSTRUCTED (=31) (SET), length=29, overallLength=31)
{
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=27, overallLength=29)
{
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=3, overallLength=5) commonName (X.520 DN component) (2.5.4.3)
Primitive(tag=12 (=0C) (UTF8 STRING), length=20, overallLength=22) DefaultCryptoService
}
}
}
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=89, overallLength=91)
{
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=19, overallLength=21)
{
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=7, overallLength=9) ecPublicKey (ANSI X9.62 public key type) (1.2.840.10045.2.1)
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=8, overallLength=10) prime256v1 (ANSI X9.62 named elliptic curve) (1.2.840.10045.3.1.7)
}
Primitive(tag=3 (=03) (BIT STRING), length=66, overallLength=68) 00041F2F8DE2979325CBB6E6244D2197BD911CE9EC14B64ADB308C4564CC6004D8DDAA7368B1D6E5D5C01D9D43D5777F2A63EA63565469CA050A9F5F0D1EFF0A59C6
}
}
Sequence(tag=16 CONSTRUCTED (=30) (SEQUENCE), length=10, overallLength=12)
{
Primitive(tag=6 (=06) (OBJECT IDENTIFIER), length=8, overallLength=10) ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256) (1.2.840.10045.4.3.2)
}
Primitive(tag=3 (=03) (BIT STRING), length=72, overallLength=74) 00304502202ACD359890E9532398B1761133124750B8DB5719B032160F0E31233383F9A2E8022100BAEC2C1FA464003A2E8FD2ED15E02E178CA58EB7B14CEA7560C30BAB08CDC3CB
}