代码之家  ›  专栏  ›  技术社区  ›  Andrew B.

如何在python中验证RSA SHA1签名?

  •  28
  • Andrew B.  · 技术社区  · 16 年前

    我有一个字符串、一个签名和一个公钥,我想验证字符串上的签名。钥匙如下:

    -----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 回复  |  直到 9 年前
        1
  •  24
  •   Community CDub    8 年前

    标记之间的数据是包含pkcs 1 rsapublickey的pkcs 8 publicKeyInfo的asn.1 der编码的base64编码。

    这是许多标准,您最好使用加密库对其进行解码(如m2crypto-as suggested by 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
  •  28
  •   joeforker    16 年前

    使用 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
    
        3
  •  2
  •   user64349    16 年前

    公钥包含一个模数(非常长的数字,可以是1024bit、2058bit、4096bit)和一个公钥指数(非常小的数字,通常等于一个大于二的幂)。您需要先了解如何将该公钥拆分为两个组件,然后才能对其执行任何操作。

    我对pycrypto不太了解,但要验证签名,请使用字符串的散列值。现在我们必须解密签名。读起来 modular exponentiation ;解密签名的公式是 message^public exponent % modulus . 最后一步是检查您所做的哈希和您得到的解密签名是否相同。

        4
  •  1
  •   Community CDub    8 年前

    我想 ezPyCrypto 可能会让这更容易些。高层次的方法 钥匙 类包含这两种方法,我希望它们可以解决您的问题:

    Rasmus 在评论中指出 verifyString 硬编码为使用MD5,在这种情况下,Ezpycryto无法帮助Andrew,除非他深入研究其代码。我推迟到 joeforker's answer 考虑 M2Crypto .

        5
  •  1
  •   Allen Geng    10 年前

    关于der解码的更多信息。

    DER编码始终遵循TLV三元组格式:(标记、长度、值)

    • 标记指定值的类型(即数据结构)
    • 长度指定此值字段所占的字节数
    • 值是可以是另一个三元组的实际值

    标记主要告诉如何解释值字段中的字节数据。ans.1有一个类型系统,例如,0x02表示整数,0x30表示序列(一个或多个其他类型实例的有序集合)

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

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

    例如,假设我要编码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
    

    这就解释了拉姆斯·费伯在回答中所说的话。

        6
  •  0
  •   David Z    16 年前

    也许这不是你要找的答案,但是如果你只需要把密钥转换成比特,它看起来像是base64编码的。看看 codecs 我想是标准python库中的模块。

        7
  •  0
  •   pmhoudry    10 年前

    使用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'
    

    这就是问题所在

        8
  •  -1
  •   panlilu    10 年前

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