簡體   English   中英

如何驗證Python中的RSA SHA1簽名?

[英]How do you verify an RSA SHA1 signature in Python?

我有一個字符串,一個簽名和一個公鑰,我想驗證字符串上的簽名。 關鍵看起來像這樣:

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

另外,如果我對任何術語感到困惑,請告訴我。

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

標記之間的數據是包含PKCS#1 RSAPublicKey的PKCS#8 PublicKeyInfo的ASN.1 DER編碼的base64編碼。

這是很多標准,你最好使用加密庫對其進行解碼(例如joeforker 建議的M2Crypto )。 將以下內容視為有關格式的一些有趣信息:

如果你願意,你可以像這樣解碼它:

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。

您可以在元組中將這兩個值用作ne來構造RSAobj。

公鑰包含模數(非常長的數字,可以是1024位,2058位,4096位)和公鑰指數(更小的數字,通常等於一個多於兩個到一些功率)。 您需要了解如何將該公鑰拆分為兩個組件,然后才能對其執行任何操作。

我不太了解pycrypto,但要驗證簽名,請接受字符串的哈希值。 現在我們必須解密簽名。 閱讀模冪運算 ; 解密簽名的公式是message^public exponent % modulus 最后一步是檢查您所做的散列和您獲得的解密簽名是否相同。

更多關於DER解碼。

DER編碼始終遵循TLV三元組格式:(標簽,長度,值)

  • Tag指定值的類型(即數據結構)
  • Length指定此值字段占用的字節數
  • 價值是實際價值,可能是另一個三元組

Tag基本上講述了如何解釋Value字段中的字節數據。 ANS.1確實有一個類型系統,例如0x02表示整數,0x30表示序列(一個或多個其他類型實例的有序集合)

長度表示有一個特殊的邏輯:

  • 如果長度<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

它解釋了Rasmus Faber在回復中的內容

我認為ezPyCrypto可能會讓這更容易一些。 密鑰類的高級方法包括這兩種方法,我希望這些方法可以解決您的問題:

Rasmus在評論中指出, verifyString被硬編碼為使用MD5,在這種情況下,ezPyCryto無法幫助Andrew,除非他趟入其代碼。 我按照joeforker的回答 :考慮M2Crypto

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

這就是它

也許這不是您正在尋找的答案,但如果您只需將密鑰轉換為位,則看起來它是Base64編碼的。 查看標准Python庫中的codecs模塊(我認為)。

我嘗試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"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM