2009-02-13 31 views
27

Tengo una cadena, una firma y una clave pública, y quiero verificar la firma en la cadena. La clave es el siguiente:¿Cómo se verifica una firma RSA SHA1 en Python?

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

He estado leyendo los documentos PyCrypto por un tiempo, pero no puedo encontrar la manera de hacer una RSAobj con este tipo de llave. Si conoces 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, por favor hágamelo saber.

Respuesta

24

La información entre los marcadores es la codificación base64 de la codificación DER ASN.1 de una PKCS # 8 PublicKeyInfo que contiene un PKCS # 1 RSAPublicKey.

Esa es una gran cantidad de estándares, y lo mejor será que use una biblioteca de cifrado para decodificarla (como M2Crypto como suggested by joeforker). El tratamiento de los siguientes como algo de información divertida sobre el formato:

Si desea, puede decodificar así:

base 64-decodificación de la cadena:

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

Ésta es la DER-codificación 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, se puede tratar como una cabecera "30819f300d06092a864886f70d010101050003818d00308189028181" constante, seguido de un 00 bytes, seguido por los 128 bytes del módulo RSA. Después de que el 95% de las veces 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.

+1

Muchas gracias. Pasé bastante tiempo persiguiendo documentos estándar, pero nunca encontré todas las piezas. La "información divertida" es definitivamente apreciada. –

0

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

+0

La única manera en que puedo encontrar para crear una RSAobj requiere una tupla como entrada. –

2

Una clave pública contiene un módulo (número muy largo, puede ser 1024bit, 2058bit, 4096bit) y un exponente de clave pública (número mucho más pequeño, generalmente igual a uno más que dos a cierta potencia). Debe averiguar 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. Lea en modular exponentiation; la fórmula para descifrar una firma es message^public exponent % modulus. El último paso es comprobar si el hash que has creado y la firma descifrada que obtuviste son iguales.

1

Creo que ezPyCrypto puede hacer esto un poco más fácil.Los métodos de alto nivel de la clave de clase incluye estos dos métodos que espero va a resolver su problema:

  • verifyString - verificar una cadena contra una firma
  • importKey - importación de claves públicas (y posiblemente la clave privada también)

Rasmus puntos en los comentarios que verifyString es modificable para usar MD5, en cuyo caso ezPyCryto no puede ayudar a Andrew a menos que ingrese en su código. Aplazo a joeforker's answer: considere M2Crypto.

+2

Echando un vistazo rápido a esto, parece que verifyString() está codificado para usar MD5 y que importKey usa un formato casero no estándar. Desafortunadamente, no creo que él pueda usar esto. –

28

Utilice M2Crypto. Aquí es cómo comprobar para RSA y cualquier otro algoritmo apoyado por 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

El openssl_verify de PHP llama exactamente a las mismas funciones que el código Python anterior. – joeforker

+0

Gracias Joe. Si pudiera marcar dos como la respuesta, también marcaría la tuya. –

+1

Arg, he intentado hacer que esto funcione por un tiempo, pero sigo obteniendo -1 del verify_final. Los valores que tengo verificar correctamente utilizando PHP. –

1

Más sobre decodificación de DER.

DER codificación siempre sigue un formato TLV triplete: (Tag, Longitud, Valor)

  • Tag especifica el (es decir, estructura de datos) tipo del valor
  • longitud especifica el número de bytes de este campo de valor ocupa
  • valor es el valor real que podría ser otro triplete

Tag básicamente le dice cómo interpretar los datos de bytes en el campo valor. ANS.1 tiene un sistema de tipo, p. 0x02 significa número entero, 0x30 significa secuencia (una colección ordenada de uno o más instancias de tipo)

presentación Longitud tiene una lógica especial:

  • Si la longitud < 127, el campo L sólo utiliza un byte y codificado como el valor del número longitud directamente
  • Si la longitud> 127, entonces en el primer byte del campo L, el primer bit debe ser 1, y el resto 7 bits representa el número de siguiente bytes utilizados para especifica el longitud del campo Valor Valor, el realmente bytes del valor en sí.

Por ejemplo, decir que quiero para codificar un número de 256 bytes de longitud, entonces sería como esto

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

Mira ahora en tu ejemplo

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 
0001 

Se interpreta como justo lo Rasmus Faber puso en su respuesta

-1

trato dado por el código 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" 
+1

¿Qué significa self.populateSignStr? –

0

Al usar 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

Cuestiones relacionadas