2009-02-13 8 views
27

Ich habe eine Zeichenfolge, eine Signatur und einen öffentlichen Schlüssel bekommt, und ich mag die Unterschrift auf der Zeichenfolge überprüfen. Der Schlüssel sieht wie folgt aus:Wie verifizieren Sie eine RSA SHA1-Signatur in Python?

-----BEGIN PUBLIC KEY----- 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfG4IuFO2h/LdDNmonwGNw5srW 
nUEWzoBrPRF1NM8LqpOMD45FAPtZ1NmPtHGo0BAS1UsyJEGXx0NPJ8Gw1z+huLrl 
XnAVX5B4ec6cJfKKmpL/l94WhP2v8F3OGWrnaEX1mLMoxe124Pcfamt0SPCGkeal 
VvXw13PLINE/YptjkQIDAQAB 
-----END PUBLIC KEY----- 

Ich habe eine Zeit lang die PyCrypto docs lesen, aber ich kann nicht herausfinden, wie man mit dieser Art von Schlüssel ein RSAobj zu machen. Wenn Sie PHP wissen, ich versuche folgendes zu tun:

openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA1); 

Auch, wenn ich über jede Terminologie verwirrt bin, lass es mich wissen.

Antwort

24

die Daten zwischen den Markierungen ist die Base64-Kodierung der ASN.1 DER-Codierung eines PKCS # 8 PublicKeyInfo einen PKCS # 1 RSAPublicKey enthält.

Das sind viele Standards, und Sie werden am besten bedient mit einer Krypto-Bibliothek zu entschlüsseln (wie M2Crypto als suggested by joeforker). Behandeln Sie die folgenden als etwas Spaß Informationen über das Format:

Wenn Sie möchten, können Sie es so dekodieren kann:

Base64-decode die Zeichenfolge:

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

Dies ist die DER-Codierung von:

0 30 159: SEQUENCE { 
    3 30 13: SEQUENCE { 
    5 06 9:  OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) 
    16 05 0:  NULL 
      :  } 
    18 03 141: BIT STRING 0 unused bits, encapsulates { 
    22 30 137:  SEQUENCE { 
    25 02 129:   INTEGER 
      :   00 DF 1B 82 2E 14 ED A1 FC B7 43 36 6A 27 C0 63 
      :   70 E6 CA D6 9D 41 16 CE 80 6B 3D 11 75 34 CF 0B 
      :   AA 93 8C 0F 8E 45 00 FB 59 D4 D9 8F B4 71 A8 D0 
      :   10 12 D5 4B 32 24 41 97 C7 43 4F 27 C1 B0 D7 3F 
      :   A1 B8 BA E5 5E 70 15 5F 90 78 79 CE 9C 25 F2 8A 
      :   9A 92 FF 97 DE 16 84 FD AF F0 5D CE 19 6A E7 68 
      :   45 F5 98 B3 28 C5 ED 76 E0 F7 1F 6A 6B 74 48 F0 
      :   86 91 E6 A5 56 F5 F0 D7 73 CB 20 D1 3F 62 9B 63 
      :   91 
157 02 3:   INTEGER 65537 
      :   } 
      :  } 
      : } 

Für einen 1024-Bit-RSA-Schlüssel können Sie "30819f300d06092a864886f70d010101050003818d00308189028181" als konstante Header, gefolgt von einem 00-Byte, gefolgt von dem 128 Bytes des RSA-Modul behandeln. Nach diesem 95% der Zeit werden Sie 0203010001 erhalten, die einen öffentlichen RSA-Exponenten von 0x10001 bedeutet = 65537.

Sie diese beiden Werte als n und e in einem Tupel verwenden, um eine RSAobj zu konstruieren.

+1

Vielen Dank. Ich habe einige Zeit damit verbracht, Standarddokumenten zu jagen, habe aber nie alle Teile gefunden. Die "lustige Info" wird definitiv geschätzt. –

0

Vielleicht ist dies nicht die Antwort, die Sie suchen, aber wenn alles, was Sie brauchen, ist den Schlüssel in Bits zu drehen, sieht es aus wie es codiert Base64. Schauen Sie sich die codecs Modul (glaube ich) in der Standard-Python-Bibliothek.

+0

Die einzige Art, wie ich einen RSAobj erstellen finden erfordert ein Tupel als Eingabe. –

2

Ein öffentlicher Schlüssel enthält sowohl einen Modulus (sehr lange Zahl, kann 1024 Bit, 2058 Bit, 4096 Bit) und einen öffentlichen Schlüssel Exponent (viel kleinere Zahl, in der Regel gleich eins mehr als zwei zu etwas Macht). Sie müssen herausfinden, wie Sie diesen öffentlichen Schlüssel in die zwei Komponenten aufteilen, bevor Sie damit etwas anfangen können.

Ich weiß nicht viel über PyCrypto aber eine Signatur, nehmen Sie die Hash der Zeichenfolge zu überprüfen. Jetzt müssen wir die Signatur entschlüsseln. Lesen Sie weiter auf modular exponentiation; Die Formel zum Entschlüsseln einer Signatur lautet message^public exponent % modulus. Der letzte Schritt besteht darin, zu überprüfen, ob der von Ihnen erstellte Hash und die entschlüsselte Signatur identisch sind.

1

Ich denke, ezPyCrypto könnte dies ein wenig leichter machen.Die High-Level-Methoden der Schlüssel Klasse umfassen diese beiden Methoden der hoffentlich Ihr Problem lösen:

  • verifyString - prüfen eine Zeichenfolge gegen eine Unterschrift
  • importKey - Import Öffentliche Schlüssel (und möglicherweise auch private Schlüssel)

Rasmus weist darauf hin, in den Kommentaren, dass verifyString ist hartcodiert um MD5 zu verwenden, in welchem ​​Fall ezPyCryto Andrew nicht helfen kann, wenn er nicht in seinen Code hineinwagt. Ich verzichte auf joeforker's answer: erwäge M2Crypto.

+2

Wenn man sich das kurz anschaut, sieht es so aus, als ob verifyString() hardcoded ist, um MD5 zu verwenden, und dass importKey ein hausgemachtes, nicht standardisiertes Format verwendet. Also, ich glaube leider nicht, dass er das nutzen kann. –

28

Verwenden Sie M2Crypto. Hier ist, wie für RSA und andere von OpenSSL unterstützt Algorithmus zu überprüfen:

pem = """-----BEGIN PUBLIC KEY----- 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfG4IuFO2h/LdDNmonwGNw5srW 
nUEWzoBrPRF1NM8LqpOMD45FAPtZ1NmPtHGo0BAS1UsyJEGXx0NPJ8Gw1z+huLrl 
XnAVX5B4ec6cJfKKmpL/l94WhP2v8F3OGWrnaEX1mLMoxe124Pcfamt0SPCGkeal 
VvXw13PLINE/YptjkQIDAQAB 
-----END PUBLIC KEY-----""" # your example key 

from M2Crypto import BIO, RSA, EVP 
bio = BIO.MemoryBuffer(pem) 
rsa = RSA.load_pub_key_bio(bio) 
pubkey = EVP.PKey() 
pubkey.assign_rsa(rsa) 

# if you need a different digest than the default 'sha1': 
pubkey.reset_context(md='sha1') 
pubkey.verify_init() 
pubkey.verify_update('test message') 
assert pubkey.verify_final(signature) == 1 
+1

PHP's openssl_verify ruft genau dieselben Funktionen auf wie der obige Python-Code. – joeforker

+0

Danke Joe. Wenn ich zwei als die Antwort markieren könnte, würde ich auch deine markieren. –

+1

Arg, ich habe versucht, dies für eine Weile zu funktionieren, aber ich bekomme nur -1 von der verify_final. Die Werte, die ich habe, überprüfen korrekt mit PHP. –

1

Mehr über die DER-Decodierung.

DER-Codierung folgt immer ein TLV Triplett Format: (Tag, Länge, Wert)

  • Tag gibt den Typ (dh Datenstruktur) des Wertes
  • Länge, um die Anzahl von Byte-Feld Dieser Wert gibt nimmt
  • Wert der tatsächliche Wert, der im Grunde sagt ein anderes Triplett sein könnte

Tag wie die Bytes Daten in dem Feld Wert zu interpretieren. ANS.1 hat ein Typensystem, z.B. 0x02 bedeutet, ganze Zahl ist, bedeutet, 0x30 Sequenz (eine geordnete Sammlung von einem oder mehreren anderen Typ Instanzen)

Länge Darstellung hat eine spezielle Logik:

  • Wenn die Länge < 127, nur das L-Feld ein Byte verwendet, und codiert als der Zahlenwert Länge
  • direkt, wenn die Länge> 127, dann in dem ersten Byte von L-Feld muß der erste Bit 1 sein, und die übrigen 7 Bits repräsentieren die Anzahl der folgenden Bytes verwendet, die angibt Länge des Felds Wert. Wert, der tatsächlich Bytes des Werts selbst.

Zum Beispiel sagen, dass ich eine Anzahl von 256 Byte lang kodieren will, dann wäre es wie dieser

  • Tag

    02 82 01 00 1F 2F 3F 4F … DE AD BE EF 
    
    sein, 0x02 bedeutet es eine Reihe
  • Länge ist, 0x82, Bit-Darstellung davon ist 1000 0010, dh die folgenden zwei Bytes gibt die tatsächliche Länge des Wertes, die sein 0x0100 Bedeutung das Wert-Feld ist 256 Bytes lang
  • Wert, f von 1F nach EF, die tatsächlichen 256 Bytes.Jetzt

bei Ihrem Beispiel sucht

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

Er interpretiert als genau das, was Rasmus Faber in seiner Antwort stellte

-1

Ich versuche, den durch joeforker bestimmten Code, aber es funktioniert nicht. Hier ist mein Beispielcode und es funktioniert gut.

from Crypto.Signature import PKCS1_v1_5 
from Crypto.PublicKey import RSA 
from Crypto.Hash import SHA 

pem = """-----BEGIN PUBLIC KEY----- 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfG4IuFO2h/LdDNmonwGNw5srW 
nUEWzoBrPRF1NM8LqpOMD45FAPtZ1NmPtHGo0BAS1UsyJEGXx0NPJ8Gw1z+huLrl 
XnAVX5B4ec6cJfKKmpL/l94WhP2v8F3OGWrnaEX1mLMoxe124Pcfamt0SPCGkeal 
VvXw13PLINE/YptjkQIDAQAB 
-----END PUBLIC KEY-----""" # your example key 

key = RSA.importKey(pem) 
h = SHA.new(self.populateSignStr(params)) 
verifier = PKCS1_v1_5.new(key) 
if verifier.verify(h, signature): 
    print "verified" 
else: 
    print "not verified" 
+1

Was bedeutet es self.populateSignStr? –

0

Mit M2Crypto funktionieren die obigen Antworten nicht. Hier ist ein getestetes Beispiel.

import base64 
import hashlib 
import M2Crypto as m2 

# detach the signature from the message if it's required in it (useful for url encoded data) 
message_without_sign = message.split("&SIGN=")[0] 
# decode base64 the signature 
binary_signature = base64.b64decode(signature) 
# create a pubkey object with the public key stored in a separate file 
pubkey = m2.RSA.load_pub_key(os.path.join(os.path.dirname(__file__), 'pubkey.pem')) 
# verify the key 
assert pubkey.check_key(), 'Key Verification Failed' 
# digest the message 
sha1_hash = hashlib.sha1(message_without_sign).digest() 
# and verify the signature 
assert pubkey.verify(data=sha1_hash, signature=binary_signature), 'Certificate Verification Failed' 

Und das ist es