2009-02-13 18 views
27

Mam ciąg, podpis i klucz publiczny, i chcę zweryfikować podpis na ciąg. Kluczem wygląda następująco:W jaki sposób weryfikujesz podpis RSA SHA1 w Pythonie?

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

Czytałem docs pycrypto przez jakiś czas, ale nie mogę dowiedzieć się, jak zrobić RSAobj z tego rodzaju klucza. Jeśli znasz PHP, Próbuję wykonać następujące czynności:

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

Ponadto, jeśli jestem mylić o jakiejkolwiek terminologii, proszę dać mi znać.

Odpowiedz

24

Dane między znacznikami to kodowanie base64 kodu ASN.1 DER w PKKS # 8 PublicKeyInfo zawierającego klucz RSAPublicK PKCS # 1.

To dużo standardów i najlepiej będzie, jeśli wykorzystasz bibliotekę kryptograficzną do jej odkodowania (np. M2Crypto jako suggested by joeforker). Leczeniu następujących jak zabawić informacji o formacie:

Jeśli chcesz, możesz rozszyfrować to tak:

Base64-dekodowania ciąg:

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

To DER-kodowanie od:

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 
      :   } 
      :  } 
      : } 

do 1024 bitowym kluczem RSA, można traktować jako stałą "30819f300d06092a864886f70d010101050003818d00308189028181" nagłówka, a następnie przez 00-bajtowego, a następnie 128 bajtów modułu RSA. Po tym 95% czasu dostaniesz 0203010001, który oznacza się RSA publiczny wykładnik 0x10001 = 65537.

można użyć tych dwóch wartości, jak n i e w krotce skonstruować RSAobj.

+1

Wielkie dzięki. Spędziłem sporo czasu ścigając dokumentację standardów, ale nigdy nie znalazłem wszystkich elementów. "Fajna informacja" jest zdecydowanie doceniana. –

0

Może to nie jest odpowiedź, której szukasz, ale jeśli wszystko, czego potrzebujesz, to zmienić klucz w bity, wygląda na to, że jest zakodowany w Base64. Spójrz na moduł codecs (jak sądzę) w standardowej bibliotece Pythona.

+0

Jedynym sposobem mogę znaleźć stworzyć RSAobj wymaga krotki jako wejście. –

2

Klucz publiczny zawiera zarówno moduł (bardzo długi numer, może być 1024bit, 2058bit, 4096bit), jak i wykładnik klucza publicznego (znacznie mniejsza liczba, zwykle równa się jeden więcej niż dwa do pewnej mocy). Musisz dowiedzieć się, jak podzielić ten klucz publiczny na dwa komponenty, zanim będziesz mógł coś z nim zrobić.

Nie wiem zbyt wiele na temat pycrypto, ale w celu zweryfikowania podpisu, weź hasz napisu. Teraz musimy odszyfrować podpis. Przeczytaj na modular exponentiation; formuła do odszyfrowania podpisu to message^public exponent % modulus. Ostatnim krokiem jest sprawdzenie, czy wprowadzony skrót i odszyfrowany podpis są takie same.

1

Myślę, że ezPyCrypto może to trochę ułatwić.Metody wysokim poziomie klucza klasy obejmuje tych dwóch metod, które mam nadzieję będzie rozwiązać problem:

  • verifyString - sprawdza ciąg przed podpisaniem
  • importKey - kluczowego import publicznych (i ewentualnie klucz prywatny zbyt)

Rasmus Zwraca uwagę w komentarzach, że verifyString jest zakodowany do używania MD5, w takim przypadku ezPyCryto nie może pomóc Andrew, chyba że wejdzie w swój kod. Odradzam joeforker's answer: rozważ M2Crypto.

+2

Przyjrzawszy się temu, wygląda na to, że verifyString() jest zakodowany na używaniu MD5 i ten importKey używa domowego, niestandardowego formatu. Tak więc, niestety, nie sądzę, że będzie w stanie z niego skorzystać. –

28

Użyj M2Crypto. Oto jak zweryfikować dla RSA i dowolnego innego algorytmu obsługiwanego przez OpenSSL:

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 openssl_verify wywołuje dokładnie takie same funkcje, jak powyższy kod Pythona. – joeforker

+0

Dzięki Joe. Gdybym mógł zaznaczyć dwie jako odpowiedź, również oznaczyłbym twoje. –

+1

Arg, Próbowałem uruchomić to na chwilę, ale wciąż otrzymuję -1 z pliku verify_final. Wartości, które zweryfikowałem poprawnie za pomocą PHP. –

1

Więcej o dekodowaniu DER.

kodowania DER zawsze następuje TLV formatu tryplet: (Tag, długość, wartość)

  • Tag określa typ (czyli struktury danych) wartości
  • Długość określa liczbę bajtów to pole wartości zajmuje
  • wartość jest rzeczywista wartość, która może być inna trójka

Tag w zasadzie mówi, jak interpretować dane bajtów w polu wartość. ANS.1 ma system typów, np. 0x02 oznacza liczbę całkowitą 0x30 oznacza sekwencję (uporządkowaną zbiór jednego lub więcej innych przypadkach Type)

trwania prezentacji ma szczególne logiki:

  • Jeżeli długość < 127, pole L wykorzystuje tylko jeden bajt i kodowana jako wartość długości bezpośrednio
  • Jeśli długość> 127, to w pierwszym bajcie L pole, pierwszy bit musi być 1, a pozostałe 7 bitów reprezentuje liczbę następujących po bajtów używanych do określenia długość pola Wartość. Wartość, w rzeczywistości bajtów samej wartości.

Załóżmy, że chcę zakodować liczba 256 bajtów, to byłoby jak ten

02 82 01 00 1F 2F 3F 4F … DE AD BE EF 
  • Tag, 0x02 oznacza, że ​​jest to liczba
  • Długość, 0x82, prezentacja bitowa to 1000 0010, co oznacza, że ​​ po dwóch bajtach określa faktyczną długość wartości, która jego 0x0100, co oznacza, że ​​pole wartości ma długość 256 bajtów
  • Wartość f rom 1F do EF, rzeczywista 256 bajtów.

Teraz patrząc na przykład

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

Interpretuje jak tylko to, co Rasmus Faber umieścić w swojej odpowiedzi

-1

staram kodu podanego przez joeforker ale to nie działa. Oto mój przykładowy kod i działa dobrze.

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

co to znaczy self.populateSignStr? –

0

Powyższe odpowiedzi nie działają na M2Crypto. Oto sprawdzony przykład.

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' 

I to wszystko