views:

2560

answers:

5

I've got a string, a signature, and a public key, and I want to verify the signature on the string. The key looks like this:

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

I've been reading the pycrypto docs for a while, but I can't figure out how to make an RSAobj with this kind of key. If you know PHP, I'm trying to do the following:

openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA1);

Also, if I'm confused about any terminology, please let me know.

+1  A: 

Maybe this isn't the answer you're looking for, but if all you need is to turn the key into bits, it looks like it's Base64 encoded. Look at the codecs module (I think) in the standard Python library.

David Zaslavsky
The only way I can find to create an RSAobj requires a tuple as input.
Andrew B.
+1  A: 

A public key contains both a modulus(very long number, can be 1024bit, 2058bit, 4096bit) and a public key exponent(much smaller number, usually equals one more than a two to some power). You need to find out how to split up that public key into the two components before you can do anything with it.

I don't know much about pycrypto but to verify a signature, take the hash of the string. Now we must decrypt the signature. Read up on modular exponentiation; the formula to decrypt a signature is message^public exponent % modulus. The last step is to check if the hash you made and the decrypted signature you got are the same.

+13  A: 

The data between the markers is the base64 encoding of the ASN.1 DER-encoding of a PKCS#8 PublicKeyInfo containing an PKCS#1 RSAPublicKey.

That is a lot of standards, and you will be best served with using a crypto-library to decode it (such as M2Crypto as suggested by joeforker). Treat the following as some fun info about the format:

If you want to, you can decode it like this:

Base64-decode the string:

30819f300d06092a864886f70d010101050003818d0030818902818100df1b822e14eda1fcb74336
6a27c06370e6cad69d4116ce806b3d117534cf0baa938c0f8e4500fb59d4d98fb471a8d01012d54b
32244197c7434f27c1b0d73fa1b8bae55e70155f907879ce9c25f28a9a92ff97de1684fdaff05dce
196ae76845f598b328c5ed76e0f71f6a6b7448f08691e6a556f5f0d773cb20d13f629b6391020301
0001

This is the DER-encoding of:

   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
            :         }
            :       }
            :   }

For a 1024 bit RSA key, you can treat "30819f300d06092a864886f70d010101050003818d00308189028181" as a constant header, followed by a 00-byte, followed by the 128 bytes of the RSA modulus. After that 95% of the time you will get 0203010001, which signifies a RSA public exponent of 0x10001 = 65537.

You can use those two values as n and e in a tuple to construct a RSAobj.

Rasmus Faber
Thanks a lot. I spent quite some some time chasing standards docs but never found all the pieces. The "fun info" is definitely appreciated.
Andrew B.
+2  A: 

I think ezPyCrypto might make this a little easier. The high-level methods of the key class includes these two methods which I hope will solve your problem:

  • verifyString - verify a string against a signature
  • importKey - import public key (and possibly private key too)

Rasmus points out in the comments that verifyString is hard-coded to use MD5, in which case ezPyCryto can't help Andrew unless he wades into its code. I defer to joeforker's answer: consider M2Crypto.

Garth T Kidd
Taking a quick look at this, it seems like verifyString() is hardcoded to use MD5 and that importKey uses a homemade, non-standard format. So unfortunately, I don't think he will be able to use this.
Rasmus Faber
+8  A: 

Use M2Crypto. Here's how to verify for RSA and any other algorithm supported by 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
joeforker
PHP's openssl_verify calls exactly the same functions as the above Python code.
joeforker
Thanks joe. If I could mark two as the answer I'd mark yours too.
Andrew B.
Arg, I've been trying to get this to work for a while, but I keep just getting -1 from the verify_final. The values I have verify correctly using PHP.
Andrew B.
If you corrupt your message do you get a different return code from verify_final? Does your message have any special characters that might be encoded differently in Python? Are you using the latest M2Crypto? See also http://svn.osafoundation.org/m2crypto/trunk/tests/
joeforker
No, no, and "whatever apt gets". I'll try those tests before checking for a newer version. Here are the PHP and Python versions, in case I'm doing anything obviously wrong: http://pastebin.com/f58e6809a http://pastebin.com/f137244e1 . Thanks again.
Andrew B.
@Andrew So you know which files to look for, the example above mostly uses the EVP API (test_evp.py). You could also try making RSA-specific "verify" calls (test_rsa.py). If you get desperate, compare with http://cvs.php.net/viewvc.cgi/php-src/ext/openssl/openssl.c?view=markup
joeforker