Как вы проверяете подпись RSA SHA1 в Python? - PullRequest
28 голосов
/ 13 февраля 2009

У меня есть строка, подпись и открытый ключ, и я хочу проверить подпись в строке. Ключ выглядит так:

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

Я уже некоторое время читаю документы на pycrypto, но не могу понять, как сделать RSAobj с этим ключом. Если вы знаете PHP, я пытаюсь сделать следующее:

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

Кроме того, если я запутался в какой-либо терминологии, пожалуйста, дайте мне знать.

Ответы [ 8 ]

28 голосов
/ 13 февраля 2009

Использование M2Crypto . Вот как проверить RSA и любой другой алгоритм, поддерживаемый 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
24 голосов
/ 13 февраля 2009

Данные между маркерами - это кодировка base64 DER-кодировки ASN.1 PKCS # 8 PublicKeyInfo, содержащая PKCS # 1 RSAPublicKey.

Это много стандартов, и вам лучше всего использовать криптобиблиотеку для ее декодирования (например, M2Crypto как , предложенный joeforker ). Относитесь к следующей забавной информации о формате:

Если вы хотите, вы можете расшифровать его так:

Base64-декодировать строку:

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301
0001

Это DER-кодировка:

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

Для 1024-битного ключа RSA вы можете трактовать "30819f300d06092a864886f70d010101050003818d00308189028181" как постоянный заголовок, за которым следует 00 байт, за которыми следуют 128 байтов модуля RSA. После этого в 95% случаев вы получите 0203010001, что означает открытый показатель RSA 0x10001 = 65537.

Вы можете использовать эти два значения как n и e в кортеже для создания RSAobj.

2 голосов
/ 13 февраля 2009

Открытый ключ содержит как модуль (очень длинное число, может быть 1024-битный, 2058-битный, 4096-битный), так и показатель открытого ключа (гораздо меньшее число, обычно равное одному больше, чем два в некоторой степени) Вам нужно выяснить, как разделить этот открытый ключ на два компонента, прежде чем что-либо делать с ним.

Я не знаю много о pycrypto, но чтобы проверить подпись, возьмите хеш строки. Теперь мы должны расшифровать подпись. Читайте о модульное возведение в степень ; формула для расшифровки подписи message^public exponent % modulus. Последний шаг - проверить, совпадают ли хэш, который вы сделали, и расшифрованная подпись, которую вы получили.

1 голос
/ 30 июля 2014

Подробнее о декодировании DER.

Кодировка DER всегда соответствует триплетному формату TLV: (тег, длина, значение)

  • Тег указывает тип (то есть структуру данных) значения
  • Длина указывает число байтов, которое занимает это значение
  • Значение - это фактическое значение, которое может быть другим триплетом

Тег в основном говорит о том, как интерпретировать байтовые данные в поле Значение. ANS.1 действительно имеет систему типов, например 0x02 означает целое число, 0x30 означает последовательность (упорядоченная коллекция одного или нескольких экземпляров других типов)

Длина представления имеет особую логику:

  • Если длина <127, поле L использует только один байт и кодируется как значение длины номера напрямую </li>
  • Если длина> 127, то в первом байте поля L первый бит должно быть 1, а остальные 7 битов представляют число следующих байты, используемые для указания длины поля значения. Значение, фактически байты самого значения.

Например, скажем, я хочу закодировать число длиной 256 байт, тогда это будет выглядеть так

02  82  01  00  1F  2F  3F  4F  …   DE  AD  BE  EF
  • Tag, 0x02 означает, что это число
  • Длина, 0x82, представление битов составляет 1000 0010, что означает следующие два байта указывают фактическую длину значения, которое его 0x0100 означает, что значение поля составляет 256 байт
  • Значение, от 1F до EF, фактические 256 байтов.

Теперь, глядя на ваш пример

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301
0001

Это интерпретирует как то, что Расмус Фабер добавил в своем ответе

1 голос
/ 13 февраля 2009

Я думаю, ezPyCrypto может сделать это немного проще. Методы высокого уровня класса key включают в себя следующие два метода, которые, я надеюсь, решат вашу проблему:

  • verifyString - проверить строку с подписью
  • importKey - импортировать открытый ключ (и, возможно, закрытый ключ)

Rasmus указывает в комментариях, что verifyString жестко запрограммирован для использования MD5, и в этом случае ezPyCryto не может помочь Эндрю, если он не вмешивается в его код. Я откладываю ответ Джофоркера : рассмотрим M2Crypto .

0 голосов
/ 27 марта 2015

Используя M2Crypto, приведенные выше ответы не работают. Вот проверенный пример.

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'

И это все

0 голосов
/ 08 августа 2014

Я пробую код, предоставленный joeforker, но он не работает. Вот мой пример кода, и он отлично работает.

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"
0 голосов
/ 13 февраля 2009

Возможно, это не тот ответ, который вы ищете, но если все, что вам нужно, это превратить ключ в биты, похоже, что он закодирован в Base64. Посмотрите на модуль codecs (я думаю) в стандартной библиотеке Python.

...