I want to perform a sha256withRSA signature of an hexadecimal string (0xDEADBEEF in this example) using the PKCS1_PSS module in Python:
import Crypto
from Crypto.Signature import PKCS1_PSS
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto import Random
import binascii
message = 'DEADBEEF'
message = binascii.unhexlify(message)
h = SHA256.new(message) # sha256 hashing the message
my_hash = binascii.hexlify(h.digest())
I get the following hash :
b'5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953'
Then I sign it using the following Private Key :
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDRFNU++93aEvz3cV8LSUP9ib3iUxT7SufdVXcgVFK9M3BYzvro
A1uO/parFOJABTkNhTPPP/6mjrU2CPEZJ1zIkpaSNJrrhpp/rNMO9nyLYPGs9Mfd
BiWUPmHW5mY1oD0ye4my0tEsHOlgHC8AhA8OtiHr6IY0agXmH/y5YmSWbwIDAQAB
AoGAAj/IH3pUI6FqqTrF+/gYzCRsL4AXTLC8l8vwkR93GGPyRHJNjqtik8I3WrXJ
zUiBGZ0iNouIsL/+QQuNlGiw/c5i2X3nTntREDS9xs2M0x+MWD/5qI1sn0Qk0HNP
BbDczlvO8wXNFGIHiTiPVEawoeNwhMqJDyGcbsEOZp2pLokCQQDvlMBU6dOeOP9a
jnENFSlrvzNR0nugFeoGmfq6s4Czz2QtUd9baKqBfEBSdJskwFVHgxbFA1Dc7iFu
rJkoQEeFAkEA32j9ibSVryxLvWUZngKNwo2xE+wcYDAYVBMsYC3OBU3FXhVkFD06
ZVnJsY/4bd2VdQI+bI2KV99aHutMJG2WYwJABMn2ZjweTMVa5VZ/kAFiSJMT1Yjd
i7+kY+lkB6Na6T02BWnjixI2hkwThRJrn3pwufM2201Lqn7gEDRHA3T1eQJBAKZG
1RUNo6558HEo8vUIf4vCu33RaJkqkqDYmFmJHeISrQfGMfNiUrkmJ5iRR9w1ZExu
/Bj9C281XDTQ+Z3PNnMCQQCan+pvj0OZH6o0PAMJGBBwRECPpfZ6mUjwA2YD3g61
MHjtIYmKKGmn64Qs8zQ4mNEDboQqyaov3Ij/I6c0ZQlc
-----END RSA PRIVATE KEY-----
Signing command :
key = RSA.import_key(open('private_key.pem').read())
signature = PKCS1_PSS.new(key).sign(h)
print(binascii.hexlify(signature))
The result :
b'67bcc8c0cd625a58272dc8808602beb630c0dc47622da153c6b3f7fcfdddd5e082beb9e73ed0e66f9751e68106b42ff71f8d291045ca7e9a5a265e885e19c016b6095e5f895801d3f735393e8cd3e4a18382a914487b46cf6c3ca3346c0b6f4bac923e491ca9933e12f826914b90955ce24d0203824dbb2c9cb7cb617af7cdef'
When I try to calculate the signature myself using the RSA encryption method (x = (m^d)%n ), I get a different value :
n = key.n
d = key.d
x = 0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953 # hash
calculated_signature = pow(x,d,n)
print (hex(calculated_signature))
I get this :
'0x4e3484dde9ca8987b77a52c696b6848e5980c858f635a62e10fe24d45bea52bac4873378b9612f47398cb73102243b73cfa2da87b487ec0d26f949e09e4edb299bd4acc4aa0eb43afa47b814b3430ff9b01b8e41bcf53ba310ae26c191cb516c07f3ebe272bcbb95acb9e7bbdc22f0c17997ba6c1884c177ba41d4e384184275'
I'm wondering why I get this difference? My understanding of the sign method previously used is that it encrypts the input (the hash) using the operation (x^d)%n.
Am I missing something ? Can anyone suggest me the reason of the difference ? Is it due to some bad manipulation/understanding from my side ?
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
I'm trying to understand the steps to take an OpenSSH public key like so:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8HGCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxzpQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6HTaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw==
And then to convert it into an standard fingerprint like so:
2048 49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98 id_rsa.pub (RSA)
I have attempted to dive into the OpenSSH source to understand this, but it is over my head. My first guess was to do a simple MD5 on the key text, but the result does not match the above output.
It is the MD5 sum of the base64-encoded key:
import base64
import hashlib
def lineToFingerprint(line):
key = base64.b64decode(line.strip().split()[1].encode('ascii'))
fp_plain = hashlib.md5(key).hexdigest()
return ':'.join(a+b for a,b in zip(fp_plain[::2], fp_plain[1::2]))
https://github.com/ojarva/sshpubkeys
pip install sshpubkeys
Usage:
import sshpubkeys
key = sshpubkeys.SSHKey("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8H" +
"GCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxz" +
"pQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4" +
"Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl" +
"3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6H" +
"TaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw== user#host")
print(key.bits) # 2048
print(key.hash()) # '49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98'
How to get private key in string from below...
key = paramiko.RSAKey.generate(1024)
ssh_key = 'ssh-rsa' + key.get_base64()
Updated: I wanted to get Private Key from the ssh_key variable, any idea how to get it?
Like,
-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAswAD2ZlxCGm3Cy9y9diAnCh+Ji7SUnsbC1SEquqRatM7hCKS\nt3D1NFWLZrNrKUw3bgENLL1Umv5wQ9Sb42rA3TTKgjikeMiJg4tQtqSKDvjC1eq6\nMbQzqzTpjDD/WGk9cFTauurVHPk0MMwCPNUTCzQrYiM6yNKmySQ3tRFvZLSu1RLJ\ngK93qPIDPjqxUwEWtb8clu1Dt+q7hKpB+cQBXLGfUTa/1LLQFSPhZ5jwjuAaLXnE\nz0QYpwqn1OgG4t7yXYSpFWu37ItoHGKu6tmhxiPZ9rywFGqgzFMqMJCD2Qn2Fl6P\nCLWxSZvak1/rbMyx1627ZfZhKfplyzjvSevyJQIDAQABAoIBABggzmQMfOIHbfcC\nBFsDPgLOqoJ3GT6o/XI/iEqJp+RgaMtc5a34efT9+vZzBOTiMy6O0bnQI5Fo7rUV\n+ZVkFG886Hc5L2Jglbi3lL+p2jo+afTv2P1OLxDK5xhQmnrOZMu5hGphsSxgoxpY\nxYxxpEmEVLy7xva+/mBqk1/C4+MNBhpBgFuMtT03FcYJGwD9G5CwAP1uCyT8xfF3\nby8IXVSE3li65Q0lUk1SUu0I8DOSJdZcB2c8J9J8/Hop6sJx8UBwyIJ9zV1sWZ00\nzWFKdgnF1UDQecaub0pt/4cuZ9seUJxBPAvFi2tLQIENJs+higFW535T40TYARvR\nzfNIUFkCgYEA46RRftlmNcvSam+wW2SvNZ34Ygrod1bAiCy2ld5zAuAKQdiKyoKJ\nKwrrHOIr5XkmLRddn4QPFC80O+jD4Ek1PDdvjxkVJUKvFXwqzbzkTehEABPxtwFj\nTB+bVOH0Fkh6qf15UAsQ6C+NTJMC1b88S5mT7zQw6y/cr6i2Os0+bacCgYEAyUx4\na7eKnGVuVUDdpiRVVzeMuOHwunDQ3EirQ5hYOnKfDVPGRwpXkDbjns8m/o0cAg1r\njguGL5H0ApRhrODQnCwqwA5aolWn9Ou5sDPREL6ji5Dn/iQevx5HOOcKzsYbnwS7\nC0Ng/47SkvodOBURE5G1KJn/X1osYE3lAh59E1MCgYA0/rb+OexCj92/a5r529my\nOMFvSazOw4J08TvcbfoGbFPW+mttM7BtNdCAW+LuPCL4El14Wk0MXOTjIZAgMcrT\n5u/wt10l3H1YGhz44oWf4XebMQlvmkIcoasOur+RRHRSCLFqQflApHlXn03kpu2J\niApZgKP2zKCqwDMUQ13KNwKBgGSP00jctF5VPb1RXml1lTyw/5gNJ5m8alrh4s0t\n+kWYpWSfCVrMC9p70VKw4iYgkHFmCuk3UJ3LKD9xqSoYVZ7vYZv4j8axS+rLKESi\nXjsjQcijZ4JKwSFxGtuOcJPD3GCgmGCUFI0W6JOMZ7Rwscs462uRI0abYbfqshFQ\n8eMJAoGBALV38hiL7jy5kMi83Y17XEZ9qsTQbofkUaqieI2BdE5qWaVICkTKrt50\nM7FVe56CLwtpIZxGg3R1nO8zBT0aDOT7qKWXRw9C2jNMbJoI+rDM93m4lku/NirO\nLevEvuunmIk+ukyuoieKmqBOGpqJBk1uBZg0Owhqxq4ezq6ZI6Fy\n-----END RSA PRIVATE KEY-----\n
Any idea? Thanks in advance...
Use the write_private_key() method of the key object:
import StringIO
out = StringIO.StringIO()
key.write_private_key(out)
print out.getvalue()
You also need to reset the StringIO (using Python 3)
from io import StringIO
out = StringIO()
key.write_private_key(out)
out.seek(0)
key_str = out.read()
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.