PyDERASN: Als ich Big-Data-Unterstützung hinzufügte

Ich setze den letzten Artikel über PyDERASN fort - den kostenlosen ASN.1 DER / CER / BER-Codec in Python. Im letzten Jahr, ab dem Zeitpunkt des Schreibens, erschien in dieser Bibliothek neben all den kleinen Dingen, kleinen Korrekturen und noch strengeren Datenüberprüfungen (obwohl es zuvor bereits der strengste mir bekannte kostenlose Codec war) eine Funktion zum Arbeiten mit großen Datenmengen - nicht in RAM kriechen. Ich möchte in diesem Artikel darüber sprechen.

ASN.1 Browser

Probleme / Aufgaben


  • CRL:
    , (CA) X.509 . , CRL (certificate revocation list). CRL CACert.org, 8.72 MiB, PyDERASN- Python 3.6 ( asn1crypto pyasn1 ). 416 . . , . .
  • CMS:
    CMS (Cryptographic Message Syntax)
    // . ,
    SignedData , ,
    , - EnvelopedData
    .

    CMS. , CMS detached data, - , . 10 GiB 20 GiB : 10 GiB . .


Welche ASN.1-Objekte können in der Praxis ressourcenintensiv sein und viel Speicherplatz beanspruchen? Nur SEQUENCE OF / SET OF enthält zahlreiche Objekte (Hunderttausende im Fall von CACert.org) und alle Arten von * STRINGs (wie im zweiten problematischen Fall). Da eine der Tugenden eines Programmierers die Faulheit ist (laut Larry Wall), werden wir versuchen, die Probleme des Ressourcenverbrauchs mit einer minimalen Änderung des Bibliothekscodes zu überwinden.

* STRING- Objekte unterscheiden sich aus Sicht des Codecs fast nicht voneinander und werden von einer gemeinsamen Klasse in der Bibliothek implementiert. Was ist die Codierung in OCTET STRING in DER?

return tag + len_encode(len(value)) + value

Offensichtlich muss der Wert nicht die ganze Zeit im RAM sein. Es muss ein byteähnliches Objekt sein, anhand dessen Sie die Länge ermitteln können.

Memoryview erfüllt diese Bedingungen. Und doch memoryview kann erfolgen mmap auf jede temporäre Datei , die bereits im Speicher reduziert wird 20 GiB 10 GiB Anforderungen an die CMS - Datenbank kopiert in Hälfte: es genügt, wenn der Wert von angeben OCTET STRING s (oder jede andere * STRING s) ähnlich wie Memoryview . Um es zu erstellen, hat PyDERASN eine Hilfsfunktion:

from pyderasn import file_mmaped
with open("dump.sql.zst", "rb") as fd:
    ... = OctetString(file_mmaped(fd))

Wenn wir eine große Datenmenge dekodieren möchten, kann die Speicheransicht auch zum Dekodieren verwendet werden:

from pyderasn import file_mmaped
with open("dump.sql.zst", "rb") as fd:
    obj = Schema.decode(file_mmaped(fd))

Wir betrachten das Problem mit * STRING als teilweise gelöst. Zurück zum Erstellen einer riesigen CRL. Wie ist seine Struktur?

CertificateList SEQUENCE
. tbsCertList: TBSCertList SEQUENCE
. . version: Version INTEGER v2 (01) OPTIONAL
. . signature: AlgorithmIdentifier SEQUENCE
. . issuer: Name CHOICE rdnSequence
. . thisUpdate: Time CHOICE utcTime
. . nextUpdate: Time CHOICE utcTime OPTIONAL
. . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
. . . 0: RevokedCertificate SEQUENCE
. . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
. . . . revocationDate: Time CHOICE utcTime UTCTime 2003-04-01T14:25:08
. . . 1: RevokedCertificate SEQUENCE
. . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
. . . . revocationDate: Time CHOICE utcTime UTCTime 2002-10-01T02:18:01

                                 [...]

. . . 415753: RevokedCertificate SEQUENCE
. . . . userCertificate: CertificateSerialNumber INTEGER 1341859 (14:79:A3)
. . . . revocationDate: Time CHOICE utcTime UTCTime 2020-02-08T06:51:56
. . . 415754: RevokedCertificate SEQUENCE
. . . . userCertificate: CertificateSerialNumber INTEGER 1341860 (14:79:A4)
. . . . revocationDate: Time CHOICE utcTime UTCTime 2020-02-08T06:53:01
. . . 415755: RevokedCertificate SEQUENCE
. . . . userCertificate: CertificateSerialNumber INTEGER 1341861 (14:79:A5)
. . . . revocationDate: Time CHOICE utcTime UTCTime 2020-02-08T07:25:06
. signatureAlgorithm: AlgorithmIdentifier SEQUENCE
. signatureValue: BIT STRING 4096 bits

Eine lange Liste kleiner RevokedCertificate- Strukturen. In diesem Fall enthält es nur die Seriennummer des Zertifikats und den Zeitpunkt seines Widerrufs. Was ist die DER-Codierung?

value = b"".join(revCert.encode() for revCert in revCerts)
return tag + len_encode(len(value)) + value

Nur Verkettung von DER-Darstellungen jedes einzelnen Elements dieser Liste. Müssen wir all diese Hunderttausende von Objekten im Voraus haben, während wir nur in 15 Bytes DER-Darstellung serialisiert werden? Offensichtlich nicht. Daher ersetzen wir die Liste der Objekte durch einen Iterator / Generator. Die Erstellung der CRL basiert höchstwahrscheinlich auf Daten aus dem DBMS:

def revCertsGenerator(...):
    for row in db.cursor:
        yield RevokedCertificate((
            ("userCertificate", CertificateSerialNumber(row.serial)),
            ("revocationDate", Time(("utcTime", UTCTime(row.revdate)))),
        ))

crl["tbsCertList"]["revokedCertificates"] = revCertsGenerator()

Jetzt verbrauchen wir beim Erstellen einer solchen CRL fast keinen Speicher mehr - wir brauchen nur einen Ort, an dem wir mit der DER-Darstellung arbeiten können: In diesem Fall handelt es sich um ein paar zehn Megabyte (im Gegensatz zu einer halben Gigabyte-Liste von RevokedCertificate-Objekten). Es gibt jedoch einen Unterschied im Verhalten: Die Größenprüfung von SEQUENCE OF / SET OF erfolgt erst, nachdem der Iterator erschöpft ist, und nicht zum Zeitpunkt der Zuweisung eines Werts.

DER-Streaming-Codierung


Es ist unmöglich. Immerhin ist dies DER - die Längen aller TLV-Elemente (Tag + Länge + Wert) müssen im Voraus bekannt sein!

Aber ich würde so gerne! Immerhin benötigen wir noch 10 GiB Speicher, um die DER-Darstellung der Datenbankkopie zu speichern: raw = cms.encode () ! Idealerweise möchte ich einem bestimmten Schriftsteller vermitteln, wo die serialisierte Darstellung geschrieben werden soll. Übertragen Sie möglicherweise den Dateideskriptor, lassen Sie Platzhalter in der Datei an der Stelle der Längen und füllen Sie sie dann durch Suchen aus? Leider ist auch die Längenlänge (bzw. der Platzhalter) nicht im Voraus bekannt.

PyDERASN hat die Möglichkeit einer DER-Codierung mit zwei Durchgängen erhalten. Im ersten Durchgang wird Wissen über die Länge von Objekten gesammelt, wodurch ein temporärer Zustand entsteht. Der zweite ist StreamingDER-Codierung in einen bestimmten Writer , aufgrund der Kenntnis der Längen bereits möglich. Die Implementierung war einfach und fügte jedem der ASN.1-Basistypen nur ein paar Methoden mit zwei Durchgängen hinzu. Da das Durchlaufen von Objekten streng bestimmt ist (D - unterschieden!), Wird zum Speichern der erforderlichen Längen eine einfache Liste geführt, zu der beim Durchlaufen des gesamten Objektbaums Längenwerte hinzugefügt werden. Bei einigen Typen ist die Länge festgelegt. Für einige wird es nur für die Meldung an einen vorgelagerten Container benötigt ( SEQUENCE / SET , SEQUENCE OF / SET OF , EXPLICIT TAG ). Der Längenstatus für eine Liste von zwei widerrufenen Zertifikaten sieht beispielsweise folgendermaßen aus:

revCert = RevokedCertificate((
    ("userCertificate", CertificateSerialNumber(123)),
    ("revocationDate", Time(("utcTime", UTCTime(datetime.utcnow())))),
))
revs = RevokedCertificates([revCert, revCert])

(42, [40, 18, 18])

In diesem Fall müssen wir die Länge des Werts nur für jedes RevokedCertificate und die gesamte Liste als Ganzes kennen. Die Länge von Ganzzahlen und die codierte Zeit (in DER ist es eine feste Länge) im Zustand werden nicht als unnötig gespeichert. Für unsere CACert.org-CRL benötigt eine solche Liste daher etwas mehr als 3,5 MiB, und für das riesige CMS, bei dem fast das gesamte Gewicht auf ein einzelnes Feld mit einer Kopie der Datenbank fällt, sind etwa 0,5 KiB erforderlich.

Die Codierung in zwei Durchgängen erfolgt durch zwei Aufrufe:

fulllen, state = obj.encode1st()
with open("result", "wb") as fd:
    obj.encode2nd(fd.write, iter(state))

Der erste Durchgang gibt auch die gesamte Länge der Daten an, mit denen der freie Speicherplatz überprüft oder durch einen Aufruf von posix_fallocate zugewiesen werden kann .

Mit der Hilfsfunktion encode2pass (obj) können Sie eine Zwei-Pass-Codierung in den Speicher durchführen. Wozu? Der Speicherverbrauch kann erheblich wirtschaftlicher sein, da im Fall von CACert.org CRL keine 416k + kleinen Binärleitungen gespeichert werden, die durch einen b "". Join () -Aufruf verbunden sind . Dies erfordert jedoch mehr Prozessorzeit, da alle Objekte zweimal durchlaufen werden müssen.

Jetzt können wir ein beliebig großes CMS in DER praktisch ohne Speicherverbrauch codieren. Im Fall von CRL haben wir jedoch den Zertifikatsperrgenerator verwendet, der nach dem Ende des ersten Durchgangs abläuft. Was zu tun ist? Einfach neu initialisieren!

_, state = crl.encode1st()
crl["tbsCertList"]["revokedCertificates"] = revCertsGenerator()
crl.encode2nd(writer, iter(state))

Natürlich wir sind verpflichtet , dass das Ergebnis des Iterators sicherzustellen , wird genau das gleiche, sonst werden wir ein gebrochenes DER erhalten. Wenn Daten aus dem DBMS-Cursor entnommen werden, vergessen Sie nicht die Transaktionsisolationsstufe und -sortierung von REPEATABLE READ .

Real Stream Encoding: CER


Wie Sie wissen, ist DER (Distinguished Coding Rules) eine Teilmenge von BER (Basic Coding Rules), die die Codierungsregeln auf eine und nur eine Weise streng regelt. Dies ermöglicht die Verwendung in kryptografischen Aufgaben. Es gibt jedoch noch eine weitere bemerkenswerte Teilmenge von BER: CER (Canonical Coding Rules). Wie DER gibt es nur eine mögliche Darstellung der Daten. CER unterscheidet sich von DER in mehreren Details, aber Sie können damit eine echte Streaming-Codierung von Daten durchführen. Leider wurde CER nicht so populär wie DER.

Ohne so auffällige Unterschiede (wie das Sortieren von Tags in SET) weist CER zwei grundlegende Unterschiede zu DER auf:

  • (constructed, ) indefinite (LENINDEF PyDERASN). , SEQUENCE/SET, SEQUENCE OF/SET OF, EXPLICIT TAG- :

    TAG_CONSTRUCTED || LEN(VALUE) || VALUE
    

    LENINDEF (0x80) EOC ( , ) :

    TAG_CONSTRUCTED || 80 || VALUE || 00 00
    

  • *STRING-, 1000-, chunk- 1000-. , , DER. , 512 DER:

    TAG_PRIMITIVE || LEN(512) || 512B
    

    2048 :

    TAG_CONSTRUCTED || 80 ||
        TAG_PRIMITIVE || LEN(1000) || 1000B ||
        TAG_PRIMITIVE || LEN(1000) || 1000B ||
        TAG_PRIMITIVE || LEN(48) || 48B || 00 00
    

All dies (plus ein paar Kleinigkeiten) ermöglicht das Streaming (mit 1000 Bytes Puffer für Zeichenfolgen), um Objekte zu codieren. Ebenso können Sie mmap und Iteratoren verwenden. Die CER-Codierung erfolgt einfach durch Aufrufen der Methode .encode_cer (writer) . Leider kann PyDERASN die Gültigkeit von CER während der Dekodierung noch nicht überprüfen, sodass wir gezwungen sind, die Daten als BER zu dekodieren.

Der CMS-Standard erfordert übrigens die Codierung in BER (sowohl DER als auch CER, automatisch, sind BER). Daher können wir unsere riesige Kopie der Datenbank ohne einen DER mit zwei Durchgängen in CER CMS codieren. Jedoch , ist SignedData erforderlich , um ein haben SignedAttributes Element DER codiert, da das X.509 ist CertificateZertifikate. Mit PyDERASN können Sie die Verwendung von DER in bestimmten Strukturen erzwingen, indem Sie einfach das Attribut der_forced = True hinzufügen .

Stream-Dekodierung: Evgen-Modus


Wir haben das Codieren gelernt, aber nur mmap hilft beim Decodieren . Ein "echter" Stream-Decoder mit Steuerknöpfen in Form von "Gib mir mehr Daten", "Hier bist du", einer Art Zustand - würde eine radikale Änderung von PyDERASN erfordern. Und ich persönlich sehe es nicht als bequemer an als die aktuelle Lösung.

Und die aktuelle Lösung ist extrem einfach. Während des Dekodierungsprozesses haben wir verschiedene dekodierte primitive Objekte in unseren Händen, aus denen überlegene Komponenten (konstruiert) zusammengesetzt sind, von denen andere Komponenten usw. ... Wir sammeln Objekte an, um sie zusammenzusetzen und ganz nach oben zu gelangen gib uns ein großes Objekt. Warum nicht sofort alle Arten von entschlüsselten "herausgeben"Objekte, sobald sie auf unseren Händen erscheinen? Das heißt, nicht ein endgültiges Objekt zurückzugeben, sondern einen Generator, der viele dekodierte Objekte generiert. Tatsächlich sind in PyDERASN jetzt alle Decodierungsmethoden zu Generatoren solcher "Ereignisse" geworden (Ereignisgenerierung, evgen).

Wenn wir den evgen-Decodierungsmodus unserer riesigen CACert.org-CRL aktivieren, sehen wir das folgende Bild:

$ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
[][T,L,  V len]
     10   [1,1,      1]   . . version: Version INTEGER v2 (01) OPTIONAL
     15   [1,1,      9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
     26   [0,0,      2]   . . . parameters: [UNIV 5] ANY OPTIONAL
     13   [1,1,     13]   . . signature: AlgorithmIdentifier SEQUENCE
     34   [1,1,      3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
     39   [0,0,      9]   . . . . . . value: [UNIV 19] AttributeValue ANY
     32   [1,1,     14]   . . . . . 0: AttributeTypeAndValue SEQUENCE
     30   [1,1,     16]   . . . . 0: RelativeDistinguishedName SET OF

                                 [...]

    188   [1,1,      1]   . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
    191   [1,1,     13]   . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
    191   [0,0,     15]   . . . . revocationDate: Time CHOICE utcTime
    191   [1,1,     13]   . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
    186   [1,1,     18]   . . . 0: RevokedCertificate SEQUENCE
    208   [1,1,      1]   . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
    211   [1,1,     13]   . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
    211   [0,0,     15]   . . . . revocationDate: Time CHOICE utcTime
    206   [1,1,     18]   . . . 1: RevokedCertificate SEQUENCE

                                 [...]

9144992   [0,0,     15]   . . . . revocationDate: Time CHOICE utcTime
9144985   [1,1,     20]   . . . 415755: RevokedCertificate SEQUENCE
    181   [1,4,9144821]   . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
      5   [1,4,9144997]   . tbsCertList: TBSCertList SEQUENCE
9145009   [1,1,      9]   . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
9145020   [0,0,      2]   . . parameters: [UNIV 5] ANY OPTIONAL
9145007   [1,1,     13]   . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
9145022   [1,3,    513]   . signatureValue: BIT STRING 4096 bits
      0   [1,4,9145534]  CertificateList SEQUENCE

  • Zu Beginn der Dekodierung sahen wir das CertificateList SEQUENCE- Tag, die Datenlänge , aber das Objekt ist noch nicht bekannt, ob es bis zum Ende dekodiert werden kann. Bisher arbeiten wir nur daran.
  • SEQUENCE: version, INTEGER. . , . ( 10)
  • signature, SEQUENCE- : algorithm parameters. OBJECT IDENTIFIER ANY . ( 15, 26)
  • , , signature SEQUENCE , , : . ( 13)
  • , RevokedCertificate . ( 186, 206, ..)
  • tbsCertList . ( 5)
  • CertificateList, SEQUENCE, , , . ( 0)

Natürlich haben alle * STRING und Listen ( * OF ) keine wirkliche Bedeutung. Wenn DER .offset und .vlen kennt, können Sie den Wert einer Zeile aus einer Datei lesen (ein Stück Speicher?). Sequenzobjekte können nach Bedarf gesammelt und aggregiert werden, während alle Ereignisse empfangen werden.

Dekodierter Pfad und evgen_mode_upto


Wie kann man verstehen, welche Art von Objekt, welche Art von INTEGER wir zur Hand haben? Jedes Objekt hat einen eigenen sogenannten Dekodierungspfad, der ein bestimmtes Objekt in der Struktur eindeutig identifiziert. Zum Beispiel für CACert.org CRL-Dekodierungspfadereignisse:

tbsCertList:version
tbsCertList:signature:algorithm
tbsCertList:signature:parameters
tbsCertList:signature
tbsCertList:issuer:rdnSequence:0:0:type
                                 [...]
tbsCertList:issuer:rdnSequence
tbsCertList:issuer
                                 [...]
tbsCertList:revokedCertificates:0:userCertificate
tbsCertList:revokedCertificates:0:revocationDate:utcTime
tbsCertList:revokedCertificates:0:revocationDate
tbsCertList:revokedCertificates:0
tbsCertList:revokedCertificates:1:userCertificate
tbsCertList:revokedCertificates:1:revocationDate:utcTime
tbsCertList:revokedCertificates:1:revocationDate
tbsCertList:revokedCertificates:1
                                 [...]
tbsCertList:revokedCertificates:415755:userCertificate
tbsCertList:revokedCertificates:415755:revocationDate:utcTime
tbsCertList:revokedCertificates:415755:revocationDate
tbsCertList:revokedCertificates:415755
tbsCertList:revokedCertificates
tbsCertList
signatureAlgorithm:algorithm
signatureAlgorithm:parameters
signatureAlgorithm
signatureValue

So können wir die Liste der Seriennummern für die Sperrung von Zertifikaten aus dieser CRL drucken:

raw = file_mmaped(open("....crl", "rb"))
for decode_path, obj, tail in CertificateList().decode_evgen(raw):
    if (len(decode_path) == 5) and (decode_path[-1] == "userCertificate"):
        print(int(obj))

Die RevokedCertificate- Struktur kann viele Informationen enthalten, einschließlich verschiedener Erweiterungen. Rein technisch erhalten wir alle Daten über das widerrufene Zertifikat im evgen-Modus, aber das Aggregieren von Ereignissen, die sich auf ein revokedCertificates- Element beziehen , ist nicht sehr praktisch. Da jedes endgültige RevokedCertificate in der Praxis nicht viel Platz beansprucht , wäre es großartig, wenn es alle gleich wäre und nicht alle Objekte so gründlich in Ereignisse „sortiert“ werden. Mit PyDERASN kann die Liste Dekodierungspfade angeben, bei denen der evgen-Modus deaktiviert ist. So können wir den Dekodierungspfad festlegen (jedes Element aus der Liste ("tbsCertList", "revokedCertificates") ), für das das vollständige RevokedCertificate- Objekt empfangen werden soll :

for decode_path, obj, _ in CertificateList().decode_evgen(raw, ctx={
    "evgen_mode_upto": (
        (("tbsCertList", "revokedCertificates", any), True),
    ),
}):
    if (len(decode_path) == 3) and (decode_path[1] == "revokedCertificates"):
        print(int(obj["userCertificate"]))

Aggregierte Zeichenfolgen: agg_octet_string


Jetzt haben wir keine Probleme mehr, Objekte jeder Größe zu dekodieren. Es gibt auch kein Problem beim Decodieren eines DER-codierten CMS mit einer Kopie der Datenbank: Wir warten auf ein Ereignis mit einem Decodierungspfad, der auf die signierten / verschlüsselten Daten im CMS verweist, und verarbeiten die Daten aus der Datei mit Offset + Vlen. Was aber, wenn das CMS im CER-Format vorliegt? Dann hilft Offset + Vlen nicht weiter, da alle unsere 10 GiBs in Stücke von 1000 Bytes unterteilt sind, zwischen denen sich der DER-Header befindet. Aber was ist, wenn wir eine BER haben, in der das Verschachteln von * STRINGs alles sein kann?

SOME STRING[CONSTRUCTED]
    OCTET STRING[CONSTRUCTED]
        OCTET STRING[PRIMITIVE]
            DATA CHUNK
        OCTET STRING[PRIMITIVE]
            DATA CHUNK
        OCTET STRING[PRIMITIVE]
            DATA CHUNK
    OCTET STRING[PRIMITIVE]
        DATA CHUNK
    OCTET STRING[CONSTRUCTED]
        OCTET STRING[PRIMITIVE]
            DATA CHUNK
        OCTET STRING[PRIMITIVE]
            DATA CHUNK
    OCTET STRING[CONSTRUCTED]
        OCTET STRING[CONSTRUCTED]
            OCTET STRING[PRIMITIVE]
                DATA CHUNK

Beim Dekodieren im evgen-Modus erhalten wir für jedes Stück das entsprechende Ereignis und es reicht aus, nur primitive (nicht konstruierte) * STRING- Ereignisse zu erfassen , bei denen Offset + Vlen echte Daten enthalten. PyDERASN verfügt über eine praktische Hilfsfunktion agg_octet_string , die dies ausführt. Es reicht aus, einen Ereignisgenerator zu übergeben, dessen Dekodierungspfad „darunter“ eine Zeichenfolge, Binärdaten (oder Speicheransicht ) und einen Writer aggregieren muss - eine Funktion, die mit jedem empfangenen Datenelement aufgerufen wird. Wir möchten den SHA512-Hash berechnen und gleichzeitig den Inhalt von encapContentInfo.eContent CMS speichern . Suchen Sie den Speicherort des Inhaltsfelds (in dem sich SignedData befindet) und dekodieren dann den CER-Inhalt, schreiben gleichzeitig in den FS und hashen:

fdIn = open("data.p7m", "rb")
raw = file_mmaped(fdIn)
for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx={"bered": True}):
    if decode_path == ("content",):
        content = obj
        break
hasher_state = sha512()
fdOut = open("dump.sql.zst", "wb")
def hash_n_save(data):
    write_full(fdOut, data)
    hasher_state.update(data)
    return len(data)
evgens = SignedData().decode_evgen(
    raw[content.offset:],
    offset=content.offset,
    ctx={"bered": True},
)
agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hash_n_save)

Hier wird ein anderes Dienstprogramm write_full verwendet , das den Writer aufruft, bis alle Daten geschrieben sind, da das Betriebssystem beim Schreiben in eine Datei im Allgemeinen nicht alle übertragenen Daten verarbeiten muss und Sie den Vorgang fortsetzen müssen, bis sie vollständig geschrieben sind.

Ein paar liebevolle über SET OF


Rein technisch gesehen kann SET OF in DER- und CER-Codierungen nicht im laufenden Betrieb codiert werden, da codierte Darstellungen aller Elemente sortiert werden müssen. Weder CER noch ein DER mit zwei Durchgängen helfen hier. Der moderne ASN.1-Standard empfiehlt daher nicht die Verwendung von SET (das eine ähnliche Sortierung in DER erfordert) und SET OF .

Und was ist dieses Bild am Anfang des Artikels?


In PyDERASN erschien ein interaktiver, unprätentiöser ASN.1-Browser , der mir persönlich mehrmals half, Handler für komplexe Strukturen zu schreiben. Ermöglicht es Ihnen, die gesamte dekodierte Struktur zu durchlaufen und alle Details zu jedem Objekt, seine Position in Binärdaten und den Dekodierungspfad anzuzeigen. Darüber hinaus kann jedes Element in einer separaten Datei gespeichert werden, z. B. Zertifikate oder CRLs, die im CMS enthalten sind.

Sergey Matveev , Chiffrierpunk , Python / Go-Entwickler, Chefspezialist des FSUE „Scientific and Technical Center“ Atlas “.

All Articles