2010-03-17 18 views
25

Estoy intentando escribir un script para generar pares de claves de identidad SSH para mí.Cómo generar pares de claves SSH con Python

from M2Crypto import RSA 
key = RSA.gen_key(1024, 65337) 
key.save_key("/tmp/my.key", cipher=None) 

El archivo /tmp/my.key se ve muy bien ahora.

Ejecutando ssh-keygen -y -f /tmp/my.key > /tmp/my.key.pub Puedo extraer la clave pública.

Mi pregunta es ¿cómo puedo extraer la clave pública de python? Usando key.save_pub_key("/tmp/my.key.pub") ahorra algo como:

-----BEGIN PUBLIC KEY----- 
MFwwDQYJKoZIhvcNAQEBBQADASDASDASDASDBarYRsmMazM1hd7a+u3QeMP 
... 
FZQ7Ic+BmmeWHvvVP4Yjyu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQ== 
-----END PUBLIC KEY----- 

cuando estoy buscando algo como:

ssh-rsa AAAABCASDDBM$%3WEAv/3%$F ..... OSDFKJSL43$%^DFg== 
+0

Compruebe pycryto, ya que tiene el formato 'OpenSSH' para el método exportKey. –

+0

pycrypto [no se ha mantenido y tiene vulnerabilidades conocidas] (https://github.com/dlitz/pycrypto/issues/173). 'pycryptodome' es un reemplazo directo. – galgalesh

+0

Creo que quisiste decir '65537' en lugar de' 65337'. El primero es mucho más común. [Los riesgos asociados con el uso de otros números se disputan] (http://security.stackexchange.com/questions/2335/should-rsa-public-exponent-be-only-in-3-5-17-257-or- 65537-debido a la seguridad-c), pero el consenso es que 65537 (es decir, 2^16 + 1) es seguro. 65337 no es compatible con NIST. – Zenexer

Respuesta

0

se puede obtener el AAAA ... Dfg == cadena fuera de él, mientras que es un objeto? Si es así, simplemente puede abrir un archivo usted mismo y guardarlo en lugar de usar la función incorporada save_pub_key.

0

sólo una suposición ... pero ¿ha intentado algo como esto ?:

print "ssh-rsa " + "".join([ l.strip() for l in open('/tmp/my.key.pub') if not l.startswith('-----')]) 
0

No sé de tal biblioteca en Python.

Puede que la biblioteca paramiko sea útil (también available from PyPI). Implementa el protocolo SSH, y tiene funcionalidad para manejar claves existentes, pero no las genera.

La generación de claves puede ser una adición útil a esa biblioteca (podría work with the developers incorporarla en la biblioteca), y un inicio más fácil que hacerlo desde cero.

1

¿Qué le parece usar subprocess para invocar ssh-keygen?

from subprocess import Popen, PIPE 
import shlex 

def get_pub_key(path): 
    args = shlex.split('ssh-keygen -y -f') 
    args.append(path) 
    p = Popen(args, stdout=PIPE) 
    stdout = p.communicate()[0] 
    if p.returncode != 0: 
     raise Exception("Error handling would be nice, eh?") 
    return stdout.strip() 

print get_pub_key('/tmp/my.key') 

El pequeño programa anterior producirá una salida como esta:

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA ... 9Jbn6D74JOKpaOU050ltyNRw== 
+2

Invocar comandos externos siempre debe ser el último recurso. – dom0

+3

Depende, @csde_rats. Tengo mis dudas sobre los bits "siempre" y "el último resto". Invocar procesos de otros procesos no es tan malo. Los sistemas operativos completos se construyeron sobre ese principio :) –

4

La clave usada por ssh es sólo base64, no sé M2Crypto mucho, pero después de una rápida visión general se parece que podría hacer lo que quiere de esta manera:

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 %[email protected]%s' % (b64key, username, hostname) 

with open(os.getenv('HOME')+'/.ssh/id_rsa.pub') as keyfile: 
    keyfile.write(keystring) 

no he probado la clave generada con SSH, así que por favor, hágamelo saber si funciona (que debe pensar i)

+1

esto está muy cerca de lo que estoy buscando. Lamentablemente, no estoy seguro de que funcione. Los caracteres codificados con b64 casi coinciden con las salidas de ssh-keygen, pero hay 24 caracteres más entre el primer AAAA y el resto de la clave. es decir, la clave b64 se ve como "ssh-rsa AAAAabcdef ... ==" y la clave ssh-keygen se ve como "ssh-rsa AAAA <24 letters> abcdef ... ==" ¿Algún consejo más? – Lee

+0

Afirme que su respuesta funciona o no funciona. – ThorSummoner

2

La base 64 decodifica versión de salida de ssh-keygen al contenido de key.pub() el formato del archivo de claves es

b64encode('\x00\x00\x00\x07ssh-rsa%s%s' % (key.pub()[0], key.pub()[1])) 
+0

Mirándolo más, los primeros 4 bytes representan la longitud de la cadena ssh-rsa, seguidos de los bytes encontrados en key.pub() [0], por lo que es fácil de construir. – manis

4

Edición 05/09/2012:

me di cuenta de que pycrypto ya con esto:

import os 
from Crypto.PublicKey import RSA 

key = RSA.generate(2048, os.urandom) 
print key.exportKey('OpenSSH') 

este código funciona para mí:

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())),) 
+0

Acabo de agregar este código a un fork de pycrypto en https://github.com/jorgeecardona/pycrypto –

+0

¿Te importaría explicar un poco más acerca de qué se está haciendo con la llave después de la generación? Especialmente con respecto a 'modulus'? – Will

+0

Hay un relleno allí, no puedo explicarlo muy bien en este momento, esto fue hace 3 años, este código en realidad es feo, y deberías tratar de leer la implementación pycrypto en su lugar, el código está aquí: https: //github.com/dlitz/pycrypto/blob/master/lib/Crypto/PublicKey/RSA.py#L678 –

1

Aquí hay un ejemplo que usa la biblioteca Twisted Conch que aprovecha PyCrypto debajo de las cubiertas.Puede encontrar la documentación de la API en http://twistedmatrix.com/documents/current/api/twisted.conch.ssh.keys.html:

from twisted.conch.ssh import keys 

# one-time use key 
k="""-----BEGIN RSA PRIVATE KEY----- 
PRIVATE KEY STUFF 
-----END RSA PRIVATE KEY-----""" 

# create pycrypto RSA object 
rsa = keys.RSA.importKey(k) 

# create `twisted.conch.ssh.keys.Key` instance which has some nice helpers 
key = keys.Key(rsa) 

# pull the public part of the key and export an openssh version 
ssh_public = key.public().toString("openssh") 
print ssh_public 
27

Por si acaso hay algún futuros viajeros que buscan hacer esto. El módulo RSA admite la escritura de la clave pública en formato OpenSSH ahora (posiblemente no en el momento de las publicaciones anteriores). Así que creo que puede hacer lo que necesita con:

from os import chmod 
from Crypto.PublicKey import RSA 

key = RSA.generate(2048) 
with open("/tmp/private.key", 'w') as content_file: 
    chmod("/tmp/private.key", 0600) 
    content_file.write(key.exportKey('PEM')) 
pubkey = key.publickey() 
with open("/tmp/public.key", 'w') as content_file: 
    content_file.write(pubkey.exportKey('OpenSSH')) 

Obviamente no almacenan estás clave privada en/tmp ...

+4

En 'key.exportKey ('PEM')', el argumento indica el formato ** de salida **. Hay tres opciones: 'DER' - codificación binaria, 'PEM' - codificación de textura, 'OpenSSH' - codificación de textura según la especificación OpenSSH. – signal

+0

@signal De acuerdo con [la documentación] (https://pythonhosted.org/pycrypto/Crypto.PublicKey.RSA._RSAobj-class.html#exportKey), OpenSSH para exportación es "solo adecuado para claves públicas (no claves privadas) ". – elBradford

+0

Creo que la frase que cita se relaciona con el uso de la opción de formato 'OpenSSH' con exportKey en lugar del método exportKey en sí, por ejemplo, los documentos dicen que puede usar el formato 'OpenSSH' arg para la clave pública, como lo hice, y 'PEM' para la clave privada como lo he hecho. – fruitbeeriswrong

15

Uso cryptography! pycrypto ya no se encuentra en desarrollo activo y, si es posible, debería estar utilizando la criptografía. Desde junio que es posible generar claves públicas SSH, así:

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 
) 

Nota: Se necesita al menos la versión 1.4.0.

+3

Puntos a tener en cuenta: Requisito de la versión de 'criptografía> = 1.4.0' y falta una instrucción de importación:' desde cryptography.hazmat.backends importan default_backend como crypto_default_backend' – Samveen

+0

Gracias! Ajustado en consecuencia. –

+0

¿Cómo convertir esto al otro formato, es decir, 'ssh-rsa AAAABCASDDBM $% 3WEAv/3% $ F .....OSDFKJSL43 $%^DFg == ' – user592419