¿Cómo verifica una firma RSA SHA1 en Python?

Tengo una cadena, una firma y una clave pública, y quiero verificar la firma en la cadena. La clave se ve así:

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

He estado leyendo los documentos de pycrypto por un tiempo, pero no puedo averiguar cómo hacer un RSAobj con este tipo de clave. Si sabes PHP, estoy tratando de hacer lo siguiente:

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

Además, si estoy confundido acerca de cualquier terminología, hágamelo saber.

Los datos entre los marcadores son la encoding base64 de la encoding ASN.1 DER de una PublicKeyInfo PKCS # 8 que contiene una RSAPublicKey PKCS # 1.

Eso es un montón de estándares, y será mejor que te sirvan utilizar una crypto-library para decodificarla (como M2Crypto como sugiere Joeforker ). Considere lo siguiente como información divertida sobre el formato:

Si quieres, puedes decodificarlo así:

Base64-decodifica la cadena:

 30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 0001 

Esta es la encoding DER de:

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

Para una clave RSA de 1024 bits, puede tratar "30819f300d06092a864886f70d010101050003818d00308189028181" como un encabezado constante, seguido de un 00-byte, seguido de los 128 bytes del módulo RSA. Después de ese 95% del tiempo, obtendrá 0203010001 , lo que significa un exponente público RSA de 0x10001 = 65537.

Puede usar esos dos valores como n y e en una tupla para construir un RSAobj.

Utilice M2Crypto . Aquí le mostramos cómo verificar RSA y cualquier otro algoritmo compatible con 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 

Una clave pública contiene un módulo (un número muy largo, puede ser de 1024 bits, 2058 bits, 4096 bits) y un exponente de clave pública (un número mucho más pequeño, por lo general es igual a uno más de uno a una cierta potencia). Debe descubrir cómo dividir esa clave pública en los dos componentes antes de poder hacer algo con ella.

No sé mucho sobre pycrypto pero para verificar una firma, tome el hash de la cadena. Ahora debemos descifrar la firma. Leer sobre la exponenciación modular ; La fórmula para descifrar una firma es el message^public exponent % modulus . El último paso es verificar si el hash que hizo y la firma desencriptada que obtuvo son iguales.

Creo que ezPyCrypto podría hacer esto un poco más fácil. Los métodos de alto nivel de la clase clave incluyen estos dos métodos que espero que resuelvan tu problema:

  • verifyString – verifica una cadena contra una firma
  • importKey – importar clave pública (y posiblemente también clave privada)

Rasmus señala en los comentarios que verifyString está codificado para usar MD5, en cuyo caso ezPyCryto no puede ayudar a Andrew a menos que ingrese su código. Me refiero a la respuesta de Joeforker : considere M2Crypto .

Más sobre la deencoding de DER.

La encoding DER siempre sigue un formato de triplete TLV: (Etiqueta, Longitud, Valor)

  • La etiqueta especifica el tipo (es decir, la estructura de datos) del valor
  • Longitud especifica el número de bytes que ocupa este campo de valor
  • El valor es el valor real que podría ser otro triplete.

La etiqueta básicamente indica cómo interpretar los datos de bytes en el campo Valor. ANS.1 tiene un sistema de tipos, por ejemplo, 0x02 significa entero, 0x30 significa secuencia (una colección ordenada de una o más instancias de otro tipo)

La presentación de longitud tiene una lógica especial:

  • Si la longitud <127, el campo L solo usa un byte y se codifica como el valor del número de longitud directamente
  • Si la longitud> 127, entonces en el primer byte del campo L, el primer bit debe ser 1, y los 7 bits restantes representan el número de bytes siguientes utilizados para especificar la longitud del campo Valor. Valor, en realidad los bytes del propio valor.

Por ejemplo, digamos que quiero codificar un número de 256 bytes de longitud, entonces sería así

 02 82 01 00 1F 2F 3F 4F … DE AD BE EF 
  • Etiqueta, 0x02 significa que es un número
  • Longitud, 0x82, la presentación de bits es 1000 0010, lo que significa que los siguientes dos bytes especifican la longitud real del valor, cuyo 0x0100 significa que el campo de valor tiene 256 bytes de longitud
  • Valor, de 1F a EF, los 256 bytes reales.

Ahora mirando tu ejemplo

 30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 0001 

Se interpreta como lo que Rasmus Faber puso en su respuesta.

Tal vez esta no sea la respuesta que está buscando, pero si todo lo que necesita es convertir la clave en bits, parece que está codificado en Base64. Mire el módulo de codecs (creo) en la biblioteca estándar de Python.

Usando M2Crypto, las respuestas anteriores no funcionan. Aquí hay un ejemplo probado.

 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' 

Y eso es todo

Intento el código dado por joeforker pero no funciona. Aquí está mi código de ejemplo y funciona bien.

 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"