2010-09-14 13 views
12

Estoy buscando una manera de crear programáticamente archivos id_rsa e id_rsa.pub compatibles con ssh en Java.Cómo generar ssh compatible id_rsa (.pub) desde Java

que llegaron hasta crear el par de claves:

KeyPairGenerator generator; 
generator = KeyPairGenerator.getInstance("RSA"); 
// or: generator = KeyPairGenerator.getInstance("DSA"); 
generator.initialize(2048); 
keyPair = generator.genKeyPair(); 

No puedo imaginar sin embargo cómo crear la representación de cadena del PrivateKey y PublicKey en el par de claves.

+0

posible duplicado de [Dada Java ssh-rsa PublicKey, ¿cómo puedo construir una clave pública SSH2?] (Http: // stackoverflow.com/questions/3588120/given-a-java-ssh-rsa-publickey-how-can-i-build-an-ssh2-public-key) – erickson

Respuesta

24

El formato de clave utilizado por ssh se define en el RFC#4253. El formato de clave pública RSA es la siguiente:

string "ssh-rsa" 
    mpint  e /* key public exponent */ 
    mpint  n /* key modulus */ 

Todo el tipo de codificación de datos se define en la sección # 5 de RFC#4251. cadena y Mpint (precisión múltiple entero) tipos están codificados de esta manera:

4-bytes word: data length (unsigned big-endian 32 bits integer) 
    n bytes  : binary representation of the data 

por ejemplo, la codificación de la cadena "ssh-rsa" es:

byte[] data = new byte[] {0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a'}; 

para codificar el público:

public byte[] encodePublicKey(RSAPublicKey key) throws IOException 
    { 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     /* encode the "ssh-rsa" string */ 
     byte[] sshrsa = new byte[] {0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a'}; 
     out.write(sshrsa); 
     /* Encode the public exponent */ 
     BigInteger e = key.getPublicExponent(); 
     byte[] data = e.toByteArray(); 
     encodeUInt32(data.length, out); 
     out.write(data); 
     /* Encode the modulus */ 
     BigInteger m = key.getModulus(); 
     data = m.toByteArray(); 
     encodeUInt32(data.length, out); 
     out.write(data); 
     return out.toByteArray(); 
    } 

    public void encodeUInt32(int value, OutputStream out) throws IOException 
    { 
     byte[] tmp = new byte[4]; 
     tmp[0] = (byte)((value >>> 24) & 0xff); 
     tmp[1] = (byte)((value >>> 16) & 0xff); 
     tmp[2] = (byte)((value >>> 8) & 0xff); 
     tmp[3] = (byte)(value & 0xff); 
     out.write(tmp); 
    } 

Para tener una representación de cadena de la clave solo codifica la matriz de bytes devuelta en Base64.

Para la codificación de clave privada no es dos casos:

  1. la clave privada no está protegido por una contraseña. En ese caso, la clave privada se codifica de acuerdo con el estándar PKCS # 8 y luego se codifica con Base64. Es posible obtener la codificación PKCS8 de la clave privada llamando al getEncoded en RSAPrivateKey.
  2. la clave privada está protegida por una contraseña. En ese caso, la clave de codificación es un formato dedicado OpenSSH. No sé si hay alguna documentación sobre este formato (excepto el código fuente de OpenSSH, por supuesto)
+3

Muchas gracias. También encontré jsch (http://www.jcraft.com/jsch/), una biblioteca gratuita que ofrece esta funcionalidad. – Carsten

+1

parece que sería más simple de usar: MessageDigest.getInstance ("MD5"). Digest (key.getEncoded()). – Pierre

+1

hay una manera de convertir programáticamente el [Jsch] (http://www.jcraft.com/jsch/) generado ssh rsa-keys a un formato [Java Cipher] (http://docs.oracle.com/ javase/7/docs/api/javax/crypto/Cipher.html) ¿se puede usar para el cifrado? Por lo que puedo decir [eso requiere comandos openssh] (http://stackoverflow.com/a/7473874/1020470). 'openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key_file -nocrypt> pkcs8_key', pero ¿y si no tienes acceso a openssh? –

1

Como Carsten ha mencionado, JSch puede generar aquellos par de claves con facilidad. Consulte a su ejemplo, KeyGen.java

1

gotoalberto 's answer (citado más abajo) para una pregunta diferente que funciona para ambas claves RSA y DSA:

Si desea revertir el proceso, es decir, codificar un objeto Java PublicKey a un formato de entrada de Linux authorized_keys, se puede utilizar este código:

/** 
    * Encode PublicKey (DSA or RSA encoded) to authorized_keys like string 
    * 
    * @param publicKey DSA or RSA encoded 
    * @param user username for output authorized_keys like string 
    * @return authorized_keys like string 
    * @throws IOException 
    */ 
    public static String encodePublicKey(PublicKey publicKey, String user) 
      throws IOException { 
     String publicKeyEncoded; 
     if(publicKey.getAlgorithm().equals("RSA")){ 
      RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; 
      ByteArrayOutputStream byteOs = new ByteArrayOutputStream(); 
      DataOutputStream dos = new DataOutputStream(byteOs); 
      dos.writeInt("ssh-rsa".getBytes().length); 
      dos.write("ssh-rsa".getBytes()); 
      dos.writeInt(rsaPublicKey.getPublicExponent().toByteArray().length); 
      dos.write(rsaPublicKey.getPublicExponent().toByteArray()); 
      dos.writeInt(rsaPublicKey.getModulus().toByteArray().length); 
      dos.write(rsaPublicKey.getModulus().toByteArray()); 
      publicKeyEncoded = new String(
        Base64.encodeBase64(byteOs.toByteArray())); 
      return "ssh-rsa " + publicKeyEncoded + " " + user; 
     } 
     else if(publicKey.getAlgorithm().equals("DSA")){ 
      DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey; 
      DSAParams dsaParams = dsaPublicKey.getParams(); 

      ByteArrayOutputStream byteOs = new ByteArrayOutputStream(); 
      DataOutputStream dos = new DataOutputStream(byteOs); 
      dos.writeInt("ssh-dss".getBytes().length); 
      dos.write("ssh-dss".getBytes()); 
      dos.writeInt(dsaParams.getP().toByteArray().length); 
      dos.write(dsaParams.getP().toByteArray()); 
      dos.writeInt(dsaParams.getQ().toByteArray().length); 
      dos.write(dsaParams.getQ().toByteArray()); 
      dos.writeInt(dsaParams.getG().toByteArray().length); 
      dos.write(dsaParams.getG().toByteArray()); 
      dos.writeInt(dsaPublicKey.getY().toByteArray().length); 
      dos.write(dsaPublicKey.getY().toByteArray()); 
      publicKeyEncoded = new String(
        Base64.encodeBase64(byteOs.toByteArray())); 
      return "ssh-dss " + publicKeyEncoded + " " + user; 
     } 
     else{ 
      throw new IllegalArgumentException(
        "Unknown public key encoding: " + publicKey.getAlgorithm()); 
     } 
    } 
+0

Personalmente, me gusta más la respuesta de @ ymnk, incluso si la solución de gotoalberto es más accesible. ¡Aprobado para una de las bibliotecas de Java más útiles! – user314104

2

La solución genérica para cualquier PublicKey tipo (RSA, DSA, etc.) es una de una sola línea usando SSHJ:

byte[] b = new Buffer.PlainBuffer().putPublicKey(key).getCompactData() 

y luego codificar usando Base64.getEncoder().encodeToString(b).