Как сгенерировать ssh-совместимый id_rsa (.pub) из Java
Я ищу способ программно создать ssh-совместимые файлы id_rsa и id_rsa.pub на Java.
Я добрался до создания KeyPair:
KeyPairGenerator generator;
generator = KeyPairGenerator.getInstance("RSA");
// or: generator = KeyPairGenerator.getInstance("DSA");
generator.initialize(2048);
keyPair = generator.genKeyPair();
Я не могу понять, как создать строковое представление PrivateKey и PublicKey в KeyPair.
Ответы
Ответ 1
Формат ключа, используемый ssh, определен в RFС# 4253. Формат открытого ключа RSA следующий:
string "ssh-rsa"
mpint e /* key public exponent */
mpint n /* key modulus */
Вся кодировка типа данных определена в разделе № 5 RFС# 4251. string и mpint (multiple precision integer) кодируются следующим образом:
4-bytes word: data length (unsigned big-endian 32 bits integer)
n bytes : binary representation of the data
например, кодировка строки "ssh-rsa":
byte[] data = new byte[] {0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a'};
Чтобы закодировать публикацию:
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);
}
Чтобы получить строковое представление ключа, просто закодируйте возвращаемый массив байтов в Base64.
Для кодировки частного ключа есть два случая:
- закрытый ключ не защищен паролем. В этом случае закрытый ключ кодируется в соответствии со стандартом PKCS # 8 и затем кодируется Base64. Можно получить кодировку PKCS8 закрытого ключа, вызвав
getEncoded
на RSAPrivateKey
.
- закрытый ключ защищен паролем. В этом случае ключевое кодирование представляет собой выделенный формат OpenSSH. Я не знаю, есть ли какая-либо документация в этом формате (за исключением исходного кода OpenSSH).
Ответ 2
Общее решение для любого типа PublicKey
(RSA, DSA и т.д.) является однострочным с использованием SSHJ:
byte[] b = new Buffer.PlainBuffer().putPublicKey(key).getCompactData()
а затем закодировать с помощью Base64.getEncoder().encodeToString(b)
.
Ответ 3
Как отметил Карстен, JSch может легко сгенерировать эту пару ключей.
Обратитесь к его примеру, KeyGen.java
Ответ 4
gotoalberto answer (цитируется ниже) для другого вопроса работает как для RSA, так и для DSA-ключей
Если вы хотите изменить процесс, т.е. закодировать объект PublicKey
Java в формат ввода для Linux authorized_keys
, можно использовать этот код:
/**
* 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());
}
}