2017-01-23 9 views
3

erstellen Ich versuche derzeit, ein RSACryptoServiceProvider Objekt nur aus einer decodierten PEM-Datei zu erstellen. Nach einigen Tagen der Suche habe ich es geschafft, eine funktionierende Lösung zu finden, aber sie ist nicht produktionsbereit.Korrekt RSACryptoServiceProvider aus öffentlichen Schlüssel

Auf den Punkt gebracht, um ein RSACryptoServiceProvider Objekt aus den Bytes zu erstellen, die den öffentlichen Schlüssel in einer PEM-Datei machen, muss ich das Objekt unter Angabe der keysize (derzeit 2048 unter Verwendung von SHA256, speziell) erstellen und dann eine RSAParameters importieren Objekt mit dem Exponent und Modulus Set. Ich mache das so;

byte[] publicKeyBytes = Convert.FromBase64String(deserializedPublicKey.Replace("-----BEGIN PUBLIC KEY-----", "") 
                     .Replace("-----END PUBLIC KEY-----", "")); 

// extract the modulus and exponent based on the key data 
byte[] exponentData = new byte[3]; 
byte[] modulusData = new byte[256]; 
Array.Copy(publicKeyBytes, publicKeyBytes.Length - exponentData.Length, exponentData, 0, exponentData.Length); 
Array.Copy(publicKeyBytes, 9, modulusData, 0, modulusData.Length); 


// import the public key data (base RSA - works) 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(dwKeySize: 2048); 
RSAParameters rsaParam = rsa.ExportParameters(false); 
rsaParam.Modulus = modulusData; 
rsaParam.Exponent = exponentData; 
rsa.ImportParameters(rsaParam); 

Während dies funktioniert, ist es nicht sinnvoll anzunehmen, dass die deserializedPublicKey genau 270 Bytes sein wird und dass das Modul Ich brauche 9 an Position gefunden wird und immer 256 Bytes lang sein.

Wie kann ich dies ändern, um den Modulus bei einer Menge öffentlicher Schlüsselbytes korrekt auszuwählen? Ich habe versucht, den ANS-Standard zu verstehen, aber mit wenig Glück finde ich, was ich davon brauche. Ich brauche nicht (oder habe Zeit), um die gesamte öffentliche Schlüsselstruktur zu dekodieren, sondern konsistent die Modulus-Bytes. Der Exponent ist einfacher, da es immer die letzten paar Bytes ist, obwohl seine Länge auch variieren kann.

Jede Hilfe wird geschätzt.

Antwort

8

Sie müssen die vorhandenen Parameter nicht exportieren und sie dann erneut importieren. Dies zwingt Ihren Computer, einen RSA-Schlüssel zu generieren und ihn dann wegzuwerfen. Es ist also unerheblich, eine Schlüsselgröße für den Konstruktor anzugeben (wenn Sie den Schlüssel nicht verwenden, wird er normalerweise nicht generiert).

Die öffentliche Schlüsseldatei ist ein DER-codierter Blob.

Wenn Sie den Inhalt in die PEM-Rüstung aufnehmen, handelt es sich um ein Base64-codiertes Byte-Array.

30 81 A0 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 
05 00 03 81 8E 00 30 81 8A 02 81 82 00 BC AC B1 
A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB 15 EB F9 00 
EC B3 29 BF 1F 75 71 7A 00 B2 19 9C 8A 18 D7 91 
B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63 5F 05 95 75 
3D FF 7B A7 C9 87 2D BF 7E 32 26 DE F4 4A 07 CA 
56 8D 10 17 99 2C 2B 41 BF E5 EC 35 70 82 4C F1 
F4 B1 59 19 FE D5 13 FD A5 62 04 AF 20 34 A2 D0 
8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F A3 2F CC D3 
48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B 55 09 02 03 
01 00 01 

ITU-T X.690 definiert, wie die Dinge lesen codiert unter Basic Encoding Rules (BER), Canonical Encoding Rules (CER, die ich noch nie explizit verwendet gesehen), und Distinguished Encoding Rules (DER). In den meisten Fällen schränkt CER BER und DER die CER ein, wodurch DER am einfachsten zu lesen ist. (ITU-T X.680 beschreibt Abstract Syntax Notation One (ASN.1), die die Grammatik ist, dass DER ist eine binäre Codierung für)

Wir haben jetzt ein bisschen Parsen tun können:

30 

Dieses eine Sequenz identifiziert (0x10) mit dem CONSTRUCTED-Bit (0x20), was bedeutet, dass es andere DER/getaggte Werte enthält. (SEQUENCE ist immer in DER KONSTRUIERT)

Dieser nächste Teil ist eine Länge. Da das High-Bit gesetzt ist (> 0x7F), ist das erste Byte ein "Länge-Länge" -Wert. Es zeigt an, dass die wahre Länge in den nächsten 1 Byte (s) codiert ist (lengthLength & 0x7F). Daher sind die Inhalte dieser SEQUENZ insgesamt 160 Bytes. (In diesem Fall "der Rest der Daten", aber die SEQUENCE könnte in etwas anderem enthalten sein).Also lassen Sie sich den Inhalt lesen:

30 0D 

Wir sehen unsere konstruierten Folge wieder (0x30) mit einem Längenwert von 0x0D, so haben wir eine 13-Byte-Nutzlast.

06 09 2A 86 48 86 F7 0D 01 01 01 05 00 

Die 06 ist OBJECT IDENTIFIER, mit einer 0x09 Byte Nutzlast. OID hat eine leicht nicht intuitive Kodierung, aber diese entspricht der Textdarstellung 1.2.840.113549.1.1.1, die id-rsaEncryption (http://www.oid-info.com/get/1.2.840.113549.1.1.1) ist.

Dies lässt uns immer noch mit zwei Bytes (05 00), die wir sehen, ist ein NULL (mit einer Nutzlast von 0 Byte, denn, nun, es ist NULL).

So weit, so haben wir

SEQUENCE 
    SEQUENCE 
    OID 1.2.840.113549.1.1.1 
    NULL 
    143 more bytes. 

Weiter auf:

03 81 8E 00 

Die 03 bedeutet BIT STRING. BIT STRING ist codiert als [Tag] [Länge] [Anzahl nicht verwendeter Bits]. Die unbenutzten Bits sind im Wesentlichen immer Null. Das ist also eine Folge von Bits, 0x8E Bytes lang, und alle von ihnen werden verwendet.

Technisch sollten wir dort aufhören, weil CONSTRUCTED nicht eingestellt wurde. Aber da wir das Format dieser Struktur wissen, passieren wir den Wert behandeln, als ob das GEBAUT Bit sowieso gesetzt wurde:

30 81 8A 

Hier ist unser Freund wieder GEBAUT FOLGE, 0x8A Nutz-Bytes, die bequem zu „alles entspricht, ist links".

02 81 82 

02 identifiziert eine ganze Zahl und dieser hat 0x82 Nutzdatenbytes:

00 BC AC B1 A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB 
15 EB F9 00 EC B3 29 BF 1F 75 71 7A 00 B2 19 9C 
8A 18 D7 91 B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63 
5F 05 95 75 3D FF 7B A7 C9 87 2D BF 7E 32 26 DE 
F4 4A 07 CA 56 8D 10 17 99 2C 2B 41 BF E5 EC 35 
70 82 4C F1 F4 B1 59 19 FE D5 13 FD A5 62 04 AF 
20 34 A2 D0 8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F 
A3 2F CC D3 48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B 
55 09 

Der führende 0x00 eine Verletzung DER wäre, mit der Ausnahme das nächste Byte das hohe Bit gesetzt hat. Dies bedeutet, dass 0x00 vorhanden war, um das Vorzeichenbit nicht gesetzt zu halten, was dies zu einer positiven Zahl macht.

02 03 01 00 01 

andere INTEGER, 3 Bytes, Wert 01 00 01. Und wir sind fertig.

SEQUENCE 
    SEQUENCE 
    OID 1.2.840.113549.1.1.1 
    NULL 
    BIT STRING 
    SEQUENCE 
     INTEGER 00 BC AC ... 0B 55 09 
     INTEGER 01 00 01 

Ernte https://tools.ietf.org/html/rfc5280 wir sehen, dass dies viel wie ein SubjectPublicKeyInfo Struktur aussieht:

SubjectPublicKeyInfo ::= SEQUENCE { 
    algorithm   AlgorithmIdentifier, 
    subjectPublicKey  BIT STRING } 

AlgorithmIdentifier ::= SEQUENCE { 
    algorithm    OBJECT IDENTIFIER, 
    parameters    ANY DEFINED BY algorithm OPTIONAL } 
          -- contains a value of the type 
          -- registered for use with the 
          -- algorithm object identifier value 

Natürlich ist es nicht weiß, was das RSA-Public-Key-Format ist. Aber die oid-Seite der Unterkunft sagte uns RFC 2313 zu überprüfen, wo wir

An RSA public key shall have ASN.1 type RSAPublicKey: 

RSAPublicKey ::= SEQUENCE { 
    modulus INTEGER, -- n 
    publicExponent INTEGER -- e } 

So sehen die besagt, dass die erste INTEGER wir lesen, ist der Modulwert, und das zweite ist (öffentlich) Exponent.

Die DER-Codierung ist Big-Endian, die auch die RSAParameters-Codierung ist, aber für RSAParameters müssen führende 0x00 Werte aus Modulus entfernt werden.

Während das ist nicht so einfach wie Ihnen den Code zu geben, es zu tun, sollte es ziemlich einfach sein, einen Parser für RSA-Schlüssel mit diesen Informationen zu schreiben. Ich würde empfehlen, dass Sie es als internal static RSAParameters ReadRsaPublicKey(...) schreiben, und dann brauchen Sie nur

RSAParameters rsaParameters = ReadRsaPublicKey(...); 

using (RSA rsa = RSA.Create()) 
{ 
    rsa.ImportParameters(rsaParameters); 
    // things you want to do with the key go here 
} 
+0

das ist eine sehr beeindruckende Antwort! Ich werde versuchen, basierend auf den bereitgestellten Informationen zu implementieren, danke! – DiskJunky

-1

PEM-Dateien sind nur eine Serie von base64-kodierten DER-Dateien und .net erlaubt den direkten Import von DER-Dateien, also können Sie so etwas tun (ich nehme an, Sie verwenden nur den öffentlichen Schlüssel, wie Sie angeben):

byte[] certBytes = Convert.FromBase64String(deserializedPublicKey 
    .Replace("-----BEGIN PUBLIC KEY-----", "") 
    .Replace("-----END PUBLIC KEY-----", "")); 

X509Certificate2 cert = new X509Certificate2(certBytes); 
RSACryptoServiceProvider publicKeyProvider = 
(RSACryptoServiceProvider)cert.PublicKey.Key; 
+0

zu tun, wenn es nur so einfach :-) Wenn ich den öffentlichen Schlüssel in den Konstruktor übergeben, es ergibt sich eine 'CryptographicException' von 'Kann das gewünschte Objekt nicht finden' – DiskJunky

+0

Es sollte so einfach sein, aber es scheint, dass X509Certificate2 erfordert, dass die DER-Datei den privaten Schlüssel enthält ... – Gusman

+0

Als Alternative, Bouncy Castle verwenden, hat es native Unterstützung, PEM-Dateien – Gusman

Verwandte Themen