How to generate SSH key pairs with Python
I'm attempting to write a script to generate SSH Identity key pairs for me.
from M2Crypto import RSA
key = RSA.gen_key(1024, 65337)
key.save_key("/tmp/my.key", cipher=None)
The file /tmp/my.key
looks great now.
By running ssh-keygen -y -f /tmp/my.key > /tmp/my.key.pub
I can extract the public key.
My question is how can I extract the public key from python? Using key.save_pub_key("/tmp/my.key.pub")
saves something like:
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADASDASDASDASDBarYRsmMazM1hd7a+u3QeMP
...
FZQ7Ic+BmmeWHvvVP4Yjyu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQ==
-----END PUBLIC KEY-----
When I'm looking for something like:
ssh-rsa AAAABCASDDBM$%3WEAv/3%$F ..... OSDFKJSL43$%^DFg==
Solution 1:
Use cryptography
! pycrypto
is not in active development anymore and if possible you should be using cryptography. Since June it's possible to generate SSH public keys as well:
from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend
key = rsa.generate_private_key(
backend=crypto_default_backend(),
public_exponent=65537,
key_size=2048
)
private_key = key.private_bytes(
crypto_serialization.Encoding.PEM,
crypto_serialization.PrivateFormat.PKCS8,
crypto_serialization.NoEncryption()
)
public_key = key.public_key().public_bytes(
crypto_serialization.Encoding.OpenSSH,
crypto_serialization.PublicFormat.OpenSSH
)
Note: You need at least version 1.4.0
.
Note: If your SSH client does not understand this private key format, replace PKCS8
with TraditionalOpenSSL
.
Solution 2:
Just in case there are any future travellers looking to do this. The RSA module support writing out the public key in OpenSSH format now (possibly didn't at the time of earlier posts). So I think you can do what you need with:
from os import chmod
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
with open("/tmp/private.key", 'wb') as content_file:
chmod("/tmp/private.key", 0600)
content_file.write(key.exportKey('PEM'))
pubkey = key.publickey()
with open("/tmp/public.key", 'wb') as content_file:
content_file.write(pubkey.exportKey('OpenSSH'))
The files are opened with a 'wb' as the keys must be written in binary mode. Obviously don't store you're private key in /tmp...
Solution 3:
Edit 05/09/2012:
I just realized that pycrypto already has this:
import os
from Crypto.PublicKey import RSA
key = RSA.generate(2048, os.urandom)
print key.exportKey('OpenSSH')
This code works for me:
import os
from Crypto.PublicKey import RSA
key = RSA.generate(2048, os.urandom)
# Create public key.
ssh_rsa = '00000007' + base64.b16encode('ssh-rsa')
# Exponent.
exponent = '%x' % (key.e, )
if len(exponent) % 2:
exponent = '0' + exponent
ssh_rsa += '%08x' % (len(exponent) / 2, )
ssh_rsa += exponent
modulus = '%x' % (key.n, )
if len(modulus) % 2:
modulus = '0' + modulus
if modulus[0] in '89abcdef':
modulus = '00' + modulus
ssh_rsa += '%08x' % (len(modulus) / 2, )
ssh_rsa += modulus
public_key = 'ssh-rsa %s' % (
base64.b64encode(base64.b16decode(ssh_rsa.upper())), )
Solution 4:
The key used by ssh is just base64 encoded, i don't know M2Crypto very much, but after a quick overview it seems you could do what you want this way:
import os
from base64 import b64encode
from M2Crypto import RSA
key = RSA.gen_key(1024, 65537)
raw_key = key.pub()[1]
b64key = b64encode(raw_key)
username = os.getlogin()
hostname = os.uname()[1]
keystring = 'ssh-rsa %s %s@%s' % (b64key, username, hostname)
with open(os.getenv('HOME')+'/.ssh/id_rsa.pub') as keyfile:
keyfile.write(keystring)
I didn't test the generated key with SSH, so please let me know if it works (it should i think)
Solution 5:
The base64 decoded version of ssh-keygen output to the contents of key.pub() the format of the keyfile is
b64encode('\x00\x00\x00\x07ssh-rsa%s%s' % (key.pub()[0], key.pub()[1]))