我有一个字符串,一个签名和一个公钥,我想验证字符串上的签名。关键看起来像这样:
-----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);
另外,如果我对任何术语感到困惑,请告诉我。
答案 0 :(得分:28)
使用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
答案 1 :(得分:24)
标记之间的数据是包含PKCS#1 RSAPublicKey的PKCS#8 PublicKeyInfo的ASN.1 DER编码的base64编码。
这是很多标准,最好使用加密库对其进行解码(例如M2Crypto为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 :(得分:2)
公钥包含模数(非常长的数字,可以是1024位,2058位,4096位)和公钥指数(更小的数字,通常等于一个多于两个到一些功率)。您需要了解如何将该公钥拆分为两个组件,然后才能对其执行任何操作。
我不太了解pycrypto,但要验证签名,请接受字符串的哈希值。现在我们必须解密签名。阅读modular exponentiation;解密签名的公式是message^public exponent % modulus
。最后一步是检查你所做的哈希和你获得的解密签名是否相同。
答案 3 :(得分:1)
我认为ezPyCrypto可能会让这更容易一些。 键类的高级方法包括这两种方法,希望能解决您的问题:
Rasmus在评论中指出verifyString
被硬编码为使用MD5,在这种情况下,ezPyCryto无法帮助Andrew,除非他趟入其代码。我推荐joeforker's answer:考虑M2Crypto。
答案 4 :(得分:1)
有关DER解码的更多信息。
DER编码始终遵循TLV三元组格式:(标签,长度,值)
Tag基本上讲述了如何解释Value字段中的字节数据。 ANS.1确实有一个类型系统,例如0x02表示整数,0x30表示序列(一个或多个其他类型实例的有序集合)
长度表示有一个特殊的逻辑:
例如,假设我要编码长度为256个字节的数字,那么它就像这样
02 82 01 00 1F 2F 3F 4F … DE AD BE EF
现在看看你的例子
30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301
0001
它解释了Rasmus Faber在回复中提出的内容
答案 5 :(得分:0)
也许这不是您正在寻找的答案,但如果您只需要将密钥转换为位,则看起来它是Base64编码的。查看标准Python库中的codecs
模块(我认为)。
答案 6 :(得分:0)
使用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'
关于它的问题
答案 7 :(得分:-1)
我尝试了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"