你如何在Python中validationRSA SHA1签名?

我有一个string,一个签名和一个公钥,我想validationstring上的签名。 关键看起来像这样:

-----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); 

另外,如果我对任何术语感到困惑,请告诉我。

标记之间的数据是包含PKCS#1 RSAPublicKey的PKCS#8 PublicKeyInfo的ASN.1 DER编码的base64编码。

这是很多的标准,最好的方式是使用encryption库来解码它(例如joeforker build议的M2Crypto )。 把以下内容作为一些关于格式的有趣信息:

如果你愿意,你可以像这样解码:

Base64解码string:

 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。

你可以在元组中使用这两个值作为ne来构造一个RSAobj。

使用M2Crypto 。 以下是如何validationRSA和OpenSSL支持的其他algorithm:

 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 

一个公钥包含一个模数(很长的数字,可以是1024bit,2058bit,4096bit)和一个公钥指数(小得多的数字,通常等于一个比一个大一两比某个幂)。 你需要找出如何将这个公钥分成两个部分,然后才能做任何事情。

我不太了解pycrypto,但要validation签名,请使用string的散列。 现在我们必须解密签名。 阅读模块求幂 ; 解密签名的公式是message^public exponent % modulus 。 最后一步是检查你所做的散列和你得到的解密签名是否相同。

也许这不是你正在寻找的答案,但是如果你只需要把密钥变成比特,看起来就像是Base64编码。 看看标准Python库中的codecs模块(我认为)。

我认为ezPyCrypto可能会使这一点更容易。 关键类的高级方法包括这两个方法,我希望能解决你的问题:

  • verifyString – 根据签名validationstring
  • importKey – 导入公钥(也可能是私钥)

Rasmus在评论中指出, verifyString是硬编码使用MD5,在这种情况下ezPyCryto不能帮助安德鲁,除非他涉足其代码。 我按照joeforker的回答 :考虑M2Crypto 。

更多关于DER解码。

DER编码总是遵循TLV三元组格式:(标签,长度,值)

  • 标记指定值的types(即数据结构)
  • Length指定此值字段占用的字节数
  • 价值是可能是另一个三重的实际价值

Tag基本上告诉了如何解释Value字段中的字节数据。 ANS.1确实有一个types系统,例如0x02表示整数,0x30表示序列(一个或多个其他types实例的有序集合)

长度表示有一个特殊的逻辑:

  • 如果长度<127,则L字段只使用一个字节,并直接编码为长度数字值
  • 如果长度> 127,则在L字段的第一个字节中,第一个位必须是1,而其余的7个位表示用于指定Value字段长度的后续字节数。 值,值本身的实际字节数。

例如,假设我想编码长度为256个字节的数字,那么就是这样的

 02 82 01 00 1F 2F 3F 4F … DE AD BE EF 
  • 标记,0x02表示这是一个数字
  • 长度0x82,它的位显示是1000 0010,这意味着以下两个字节指定值的实际长度,其0x0100表示​​值字段的长度是256字节
  • 值,从1F到EF,实际是256个字节。

现在看看你的例子

 30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336 6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b 32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce 196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301 0001 

它解释为什么拉斯姆斯·法伯在他的答复

使用M2Crypto,上述答案不起作用。 这是一个经过testing的例子。

 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' 

这就是它

我尝试由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"