Python ECDSA failing to verify signature - python

I am trying to verify a Bitcoin signature using ECDSA in python but finding it very hard, many attempts failed already.
The params:
address: 33ELcBdg6W7parjGxNYUz5uHVHwPqopNjE
message: hzpPiNlB
signature(base64): I2fKdGNtR5owOWVoWchMVWwC/gf4qyYZ8G+kvuR7CaBhU/0SO149a3/ylBaiVWzfUoXI5HlgYPjkrptk0HfW1NQ=
Note: I have converted the signature from base64 to hexstring as that is required by ECDSA. Whenever I try to verify it, it says:
Expected 64 byte signature (128 hexstring), provided 65 byte signature
(130 hexstring)
I had a look at many stackoverflow questions about ECDSA but none of the answers were 100% relevant to my qs. Your help is appreciated guys.
Update: I have used Bitcoin Python package. Have done this first to get the public key & then verify:
pip install bitcoin
>>> message = 'hzpPiNlB'
>>> signature = 'I2fKdGNtR5owOWVoWchMVWwC/gf4qyYZ8G+kvuR7CaBhU/0SO149a3/ylBaiVWzfUoXI5HlgYPjkrptk0HfW1NQ='
>>> import bitcoin
>>> recover_key = bitcoin.ecdsa_recover(message, signature)
>>> print(recover_key)
04bbdd00bafea40bf7b268baff4ec7635a0b12e94542067cf4077369be938f7b733c731248b88bb0f8b14783247705e568effd54e57643fc827852cf77d0ed8313
>>> verify = bitcoin.ecdsa_verify(message, signature, recover_key)
>>> print(verify)
True
Although the recovered pubkey is wrong up its somehow passing True. When using the correct pubkey which I have extracted from wallet I am getting False as result of verifying the signature.
>>> message = 'hzpPiNlB'
>>> signature = 'I2fKdGNtR5owOWVoWchMVWwC/gf4qyYZ8G+kvuR7CaBhU/0SO149a3/ylBaiVWzfUoXI5HlgYPjkrptk0HfW1NQ='
>>> pub_key = '0352ab1e8ef8553fb307ae8dcafd2395fd06e5ca882f0e27143cb15cf495cc435e'
>>> import bitcoin
>>> verify = bitcoin.ecdsa_verify(message, signature, pub_key)
>>> print(verify)
False

After extracting the pubkey by using the correct path, I can confirm that:
verify = bitcoin.ecdsa_verify(message, signature, pub_key).
is returning True.

Related

Gnupg not out-puting encrypted file

Im trying to import a public key, read a csv file, encrypt that file and store that encrypted file in a folder/ directory. The program runs but nothing seems to be generated, created or outputed after I run the script. Any suggestions.
import gnupg
gpg = gnupg.GPG(gnupghome='./gnupghome')
key_data = open('./datafiles/public_key.txt').read()
import_result = gpg.import_keys(key_data)
encrypted_ascii_data = gpg.encrypt('./datafiles/myFile.csv', key_data, output="./datafiles/myFile.csv.gpg")
The second parameter is a list of recipients. You're passing it the key_data. If you check the the result of your call to gpg.encrypt(...), you'll see that you have:
>>> encrypted_ascii_data.status
'invalid recipient'
You need to either specify an explicit recipient (by fingerprint, email address, etc), or extract a recipient from your imported key, like this:
>>> encrypted_ascii_data = gpg.encrypt('./datafiles/myFile.csv',
... import_result.fingerprints[0],
... output="./datafiles/myFile.csv.gpg")
But this will still probably fail with:
>>> encrypted_ascii_data.stderr
'[GNUPG:] KEY_CONSIDERED ... 0\ngpg: 426D9382DFD6A7A9: There is no assurance this key belongs to the named user\n[GNUPG:] INV_RECP 10 ...\n[GNUPG:] FAILURE encrypt 53\ngpg: [stdin]: encryption failed: Unusable public key\n'
It looks like you need to set up a trust for that key. Before attempting to use the key:
gpg.trust_keys(import_result.fingerprints, 'TRUST_ULTIMATE')
Once you've done this:
>>> encrypted_ascii_data = gpg.encrypt('./datafiles/myFile.csv',
... import_result.fingerprints[0],
... output="./datafiles/myFile.csv.gpg")
>>> encrypted_ascii_data.status
'encryption ok'

Matching Signing between Python and Ruby

I have been trying for a few days to validate some message signed with a private key in python. Note that the message has been signed using Ruby.
When I sign the same message in python I can verify it no problem. Note that I have already validated that the hash are the same.
Python code:
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = SHA.new()
sha1.update(string_to_encrypt)
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, sha1.digest(), 'RSA-SHA1')
b64_ssl = base64.b64encode(sign_ssl)
Ruby:
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = Digest::SHA1.digest(string_to_encrypt)
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.private_encrypt(sha1)
#update the license string with it
x = Base64.strict_encode64(signed_key)
I would expect b64_ssl and x to contain the same value and they don't. Could someone explain to me what I missing there?
Neither of these code snippets is actually producing the correct signature.
In the Ruby OpenSSL library you want to be using the sign method, not the private_encrypt method, which is a low level operation that doesn’t do everything required to produce a valid signature.
In both libraries the sign operation performs the hashing for you, you don’t need to do this beforehand. In fact your Python code is actually hashing the data twice.
Try the following Python code:
import OpenSSL
import base64
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, string_to_encrypt, 'SHA1')
b64_ssl = base64.b64encode(sign_ssl)
print(b64_ssl.decode())
which produces the same output as this Ruby code:
require 'openssl'
require 'base64'
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.sign('sha1', string_to_encrypt)
#update the license string with it
x = Base64.strict_encode64(signed_key)
puts x

Verify signature on X509 certificate in Python

I have an RSA public key and a signed X509 certificate. How can I check that the key signed the certificate? (My example happens to be a self-signed certificate.)
Here's what I'm doing now:
Generate self-signed cert and convert to DER encoding with openssl cli:
(I'm expecting DER in my real application)
openssl req -x509 -newkey rsa:2048 -keyout selfsigned.key -nodes -out selfsigned.cert -sha256 -days 1000
openssl x509 -outform der -in selfsigned.cert -out self.der
Decode it to a Crypto.Util.asn1.DerSequence instance.
>>> from Crypto.Util.asn1 import DerSequence
>>> from Crypto.PublicKey import RSA
>>> der = open('self.der').read()
>>> cert = DerSequence()
>>> cert.decode(der)
905L
>>> # according to RFC5280, this is a 3-length sequence:
>>> # tbsCertificate, signatureAlgorithm, signatureValue
>>> # "tbs" == "to be signed"
>>> len(cert)
3
Then I pull the RSA public key out:
>>> tbscert = DerSequence()
>>> tbscert.decode(cert[0])
>>> subjectPublicKeyInfo = tbscert[6]
>>> rsa_key = RSA.importKey(subjectPublicKeyInfo)
>>> >>> rsa_key
<_RSAobj #0x7fb27287d128 n(2048),e>
Then I pull the signature out
This is very annoying. I'm using another library to decode the DER again because this one gives me a slightly more convenient representation of the "bit string" encoding of the signature value. For now, I copy and paste the base-2 string representation of the value into int() to get a long (what the RSA.verify() method expects).
>>> from pyasn1_modules import rfc2437,rfc2459
>>> from pyasn1.codec.der import decoder
>>> cert2,rest = decoder.decode(der, asn1Spec=rfc2459.Certificate())
>>> sig_bits = cert2.getComponentByName("signatureValue")
>>> sig_bits
BitString("'10101110101100111010100000111001000110111111101001110000100111110111011111111110011010110101001001110110111011011001110001010000111001001001110111001110011101000000100000001100001101000111000110010100110101000110111101110001011011001001100011110011010101011100000001010111101110111001110011010011110101101001110101110100011111011111110001110001110100000110110100010000001010111010000100101110101001110000111001100010011110110100101001010000101001110101101111100111001111000010111001110000000101000000011110011110000110000101101101110110101101110101011110101111111000101011001000010000110100111111001011111100110011011011001001111110000000110100000001101000011010010100001100110100001001000111001011111100000011000101001100001010101001111000001010100101010101000100001000100011101111100101010010001111001110100001011110101100010111001010100010001011100000100101110001011101100010010111100010111110010010110111111100100100101010010000000010001011111110011000011000101000001000000000000011111101110010101100100000010111111110101000110010101111100101011101000010010110101000101101110001001101101000110101110011101011111100010000101001111011100100100010101001011011110001110000000000010011011111011110100000111001100010100000100001101111110010000111100110110110000001010010110011010111100101110101000001111001010011101001101101101101011000100010011110000101010110111011100010100011110101100110101011010111000011111000001111111111000101101101011110010111100101011100010000111011101101010101001011101101111011001001110000010011001111010001011110000011001011000110100011100000100100111000111100000010000001001001010001100000010011110100000111010010100001101001111111001111110111010001101010110100001100111010101000010000101000000111100001001001000100011100110010110101001110111101000101101011011100000010010000100111001100001110010101000100000010010111110001100011110010000100001000101100011000011000110010110011100010100010111011011111111010000001001100000100011010000000110111100111010101001110101000011111011000100010111101100110100010101111000110111110'B")
>>> bit_string = '10101110101100111010100000111001000110111111101001110000100111110111011111111110011010110101001001110110111011011001110001010000111001001001110111001110011101000000100000001100001101000111000110010100110101000110111101110001011011001001100011110011010101011100000001010111101110111001110011010011110101101001110101110100011111011111110001110001110100000110110100010000001010111010000100101110101001110000111001100010011110110100101001010000101001110101101111100111001111000010111001110000000101000000011110011110000110000101101101110110101101110101011110101111111000101011001000010000110100111111001011111100110011011011001001111110000000110100000001101000011010010100001100110100001001000111001011111100000011000101001100001010101001111000001010100101010101000100001000100011101111100101010010001111001110100001011110101100010111001010100010001011100000100101110001011101100010010111100010111110010010110111111100100100101010010000000010001011111110011000011000101000001000000000000011111101110010101100100000010111111110101000110010101111100101011101000010010110101000101101110001001101101000110101110011101011111100010000101001111011100100100010101001011011110001110000000000010011011111011110100000111001100010100000100001101111110010000111100110110110000001010010110011010111100101110101000001111001010011101001101101101101011000100010011110000101010110111011100010100011110101100110101011010111000011111000001111111111000101101101011110010111100101011100010000111011101101010101001011101101111011001001110000010011001111010001011110000011001011000110100011100000100100111000111100000010000001001001010001100000010011110100000111010010100001101001111111001111110111010001101010110100001100111010101000010000101000000111100001001001000100011100110010110101001110111101000101101011011100000010010000100111001100001110010101000100000010010111110001100011110010000100001000101100011000011000110010110011100010100010111011011111111010000001001100000100011010000000110111100111010101001110101000011111011000100010111101100110100010101111000110111110'
>>> len(bit_string)
2048
>>> sig_long = int(bit_string, 2)
22054057292543290008991218833668878365914778519473463062473060546762899555976103489048033910135613221569150796460758806399269198735780309519101363051388009338597879536630494212385605300708879019160215628821483902624509955250980351374010304684207884550324020859785789812498991361733361061223150200173076263554090698006436248180914014712709890577579243572383188197634606581121383593473899061397708617253275982314075801792358481980896751043809539358665686019958496887281091997170247998458556812030465091755579654010246474389968142047627934047174316731806191431717418170761689395728146445291177267566370799362894264463806L
Then I compute the SHA256 hash of the "to be signed certificate":
>>> import Crypto.Hash.SHA256
>>> comp_hash = Crypto.Hash.SHA256.new(cert[0]).digest()
>>> comp_hash
'\xa3t\x84\xd6\xf5\xfe\x16\xb9\xdb(&\x12\xb3m^+\x94\xa7bZ\xf9s\xf7\xbay\xa1j\xa3Y\xea\xa8\x7f'
Then the verify() method tells me the signature doesn't match.
>>> rsa_key.verify(comp_hash, (sig_long, None))
False
I hope there's a better way (this doesn't even work), but I've spent hours looking at PyCrypto and PyOpenSSL and haven't found it.
edits
This similar S.O. question from a few years ago has no answer: Verify SSL/X.509 certificate is signed by another certificate
I guess one way to accomplish this is to create an X509StoreContext containing only one certificate corresponding to the public key I want to check for.
>>> from OpenSSL import crypto
>>> x509_self_signed # already loaded
<OpenSSL.crypto.X509 object at 0x7fcc4049c9b0>
>>> cert_store = crypto.X509Store()
>>> cert_store.add_cert(x509_self_signed)
>>> store_ctx = crypto.X509StoreContext(cert_store, x509_self_signed)
>>> store_ctx.verify_certificate()
>>> # ^ that raises an exception if it fails to verify
verify_certificate() was added to PyOpenSSL only a little over a year ago, so maybe that's why it was hard to find...
https://github.com/pyca/pyopenssl/pull/155

How do use paramiko.RSAKey.from_private_key()?

Any idea how I can use the paramiko.RSAKey.from_private_key() function?
I know there is a from_private_key_file(), but I'm interested in using a function to parse a private key (like below) and use that private key for SSHClient.
Private key (sample):
-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKCAIEAmfgmlY95SHXhCeBNdkhSrsG4JVbqyew845yoZRX3wcS2/doz\niVQxgx0aiOwLi+/Rnkb3PLUIwoxb/LoD/W0YMS6/NSUMt+LdH+zsjeNF2iq4rDzU\nwDSqi27q/8u/egrK7H+9HNKEVXb/87utAAm3VTM9KqKaK3VuVFrNrnsDSuECAwEA\nAQKCAIBZn3y2KiGq8BLiMNJmO4sFdnW+Jm3cw8pdo17SGItzGxJ5iX3ePkfjzhkY\nAm5mMl6OBzj6+VX0CMeywIR6C/q8HwDYSmZcuU5v76/DoW5bI6xkPrroqEz6aRE5\nyN+2hf65RD3eoPATsdrP/kxiKjZg9uG9LhgIXyVwYFs1RcqewQJBAMCVJlEYXRio\neynUtyES9HNmUGUqHKmri1FZfO56/mFdG5ZXsKE48qURCAGVxI+goGQ4vtJIXB2J\nyTEr+5qYtE0CQQDMq9/iigk+XDOa9xGCbwxbLGdPawaEivezMVdPqVzH971L6kZ8\nhEnev1DqujgGCyR+QYPW1ZCXH05FY9CqWwrlAkATzYJyJlI0XebER2ZJVVyjnSq5\nLFpkLAqYY95P23/a3SsgC4ZTHbr9tEGhgBgFONwlUhx1HRGzy95PWxl1LSylAkBk\nwP93v8gJIM5urM27zfrhLxy0ZdVRji+d0N5QYuk/r19KbcvBJEZRFxE4W++UWgve\n81V5fqytGEYptpdUJXlZAkEArxZDiT1HXXGciIgzZbh53McogPCGHiKOOPSjpM41\npneDFVvwgezCWoDauxNDzu7Nl55qPJsmvfKZ+SKvCajrhQw==\n-----END RSA PRIVATE KEY-----\n
Code I wanted to run:
import paramiko
ssh = paramiko.SSHClient()
# how do I pass in the private_key, when my private_key (shown above) is in string?
mykey = paramiko.RSAKey.from_private_key(private_key)
ssh.connect('192.168.1.2', username = 'vinod', pkey = mykey)
Many thanks.
Lev's method worked for me:
>>> import paramiko
>>> f = open('/path/to/key.pem','r')
>>> s = f.read()
>>> import StringIO
>>> keyfile = StringIO.StringIO(s)
>>> mykey = paramiko.RSAKey.from_private_key(keyfile)
>>> ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
>>> ssh.connect('myserver.compute-1.amazonaws.com', username='ubuntu', pkey=mykey)
>>> stdin, stdout, stderr = ssh.exec_command('uptime')
>>> stdout.readlines()
[' 19:21:10 up 24 days, 42 min, 1 user, load average: 0.14, 0.06, 0.05\n']
This should do it:
import io
import paramiko
private_key_file = io.StringIO()
private_key_file.write('-----BEGIN RSA PRIVATE KEY-----\nlskjdflk\n...\n-----END RSA PRIVATE KEY-----\n')
private_key_file.seek(0)
private_key = paramiko.RSAKey.from_private_key(private_key_file)
from_private_key() apparently takes a file object:
from_private_key(cls, file_obj, password=None)
Create a key object by reading a private key from a file (or file-like) object. If the private key is encrypted and password is not None, the given password will be used to decrypt the key (otherwise PasswordRequiredException is thrown).
Parameters:
file_obj (file) - the file to read from
password (str) - an optional password to use to decrypt the key, if it's encrypted
Returns: PKey
a new key object based on the given private key
Raises:
IOError - if there was an error reading the key
PasswordRequiredException - if the private key file is encrypted, and password is None
SSHException - if the key file is invalid
So to feed it a key as a string you can use StringIO, something like:
private_key = StringIO.StringIO(key_string)
mykey = paramiko.RSAKey.from_private_key(private_key)
I have not tested this, though.
Here is where 'duck typing' comes in handy - it does not have to BE a duck (=file), it just has to BEHAVE like one.
A little experimentation shows that, any object that has a valid readlines() method is fine.
I faked it with:
def myfakefile(keystring):
myfakefile.readlines=lambda: keystring.split("\n")
return myfakefile
mykey = paramiko.RSAKey.from_private_key(myfakefile(keystring))
This is incredibly hacky, but it works.
What this does, is, when you call myfakefile(keystring), it creates myfakefile.readlines, which returns the (split) contents of keystrings.
Then, it returns the function.
The same function is passed to from_private_key. from_private_key, thinking it is a file, calls myfakefile.readlines(). This calls the newly created (lambda) function, which returns the sort of thing you would expect from file.readlines() - or, close enough, anyway.
Note that, saving the results will not work as expected:
k1=myfakefile(keystring1)
k2=myfakefile(keystring2)
# This will return keystring2, not keystring1!
paramkiko.RSAKey.from_private_keyfile(k1.readlines())
There are more robust methods of getting this to work as it should, but not worth the effort - just use StringIO if your needs are more complicated.
Very old question, but in case it helps some unfortunate soul: my sol'n to this problem was to generate a new key with default options, using
ssh-keygen -t rsa
My previous key was generated using
ssh-keygen -t rsa -b 4096 -a 100
which paramiko complained about as it did for OP.

python function for retrieving key and encryption

M2Crypto package is not showing the 'recipient_public_key.pem' file at linux terminal.
How do I get/connect with recipient public key.
Exactly, I need to check how can I open this file through linux commands.
import M2Crypto
def encrypt():
recip = M2Crypto.RSA.load_pub_key(open('recipient_public_key.pem','rb').read())
print recip;
plaintext = whatever i need to encrypt
msg = recip.public_encrypt(plaintext,RSA.pkcs1_padding)
print msg;
after calling the function its not giving any output and even any error
i also tried as 'Will' said
pk = open('public_key.pem','rb').read()
print pk;
rsa = M2Crypto.RSA.load_pub_key(pk)
what is the mistake I am not getting?
I have never used M2Crypto, but according to the API documentation, load_pub_key expects the file name as the argument, not the key itself. Try
recip = M2Crypto.RSA.load_pub_key('recipient_public_key.pem')

Categories

Resources