¿Cómo puedo usar una entrada del archivo de sistemas authorized_keys
para una implementación de java.security.PublicKey
? Específicamente, quiero comparar una clave pública del archivo authorized_keys con una clave pública disponible en la interfaz Apache SSHD PublickeyAuthenticator
.Uso de clave pública de authorized_keys con seguridad Java
Respuesta
Me sorprendió que no haya nada evidente para esto. Me puse curioso e implementé una forma de decodificar los archivos authorized_keys
. Esto depende del Apache Commons Codec para la decodificación Base64.
import java.io.File;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Scanner;
import org.apache.commons.codec.binary.Base64;
public class AuthorizedKeysDecoder {
private byte[] bytes;
private int pos;
public PublicKey decodePublicKey(String keyLine) throws Exception {
bytes = null;
pos = 0;
// look for the Base64 encoded part of the line to decode
// both ssh-rsa and ssh-dss begin with "AAAA" due to the length bytes
for (String part : keyLine.split(" ")) {
if (part.startsWith("AAAA")) {
bytes = Base64.decodeBase64(part);
break;
}
}
if (bytes == null) {
throw new IllegalArgumentException("no Base64 part to decode");
}
String type = decodeType();
if (type.equals("ssh-rsa")) {
BigInteger e = decodeBigInt();
BigInteger m = decodeBigInt();
RSAPublicKeySpec spec = new RSAPublicKeySpec(m, e);
return KeyFactory.getInstance("RSA").generatePublic(spec);
} else if (type.equals("ssh-dss")) {
BigInteger p = decodeBigInt();
BigInteger q = decodeBigInt();
BigInteger g = decodeBigInt();
BigInteger y = decodeBigInt();
DSAPublicKeySpec spec = new DSAPublicKeySpec(y, p, q, g);
return KeyFactory.getInstance("DSA").generatePublic(spec);
} else {
throw new IllegalArgumentException("unknown type " + type);
}
}
private String decodeType() {
int len = decodeInt();
String type = new String(bytes, pos, len);
pos += len;
return type;
}
private int decodeInt() {
return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
| ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
}
private BigInteger decodeBigInt() {
int len = decodeInt();
byte[] bigIntBytes = new byte[len];
System.arraycopy(bytes, pos, bigIntBytes, 0, len);
pos += len;
return new BigInteger(bigIntBytes);
}
public static void main(String[] args) throws Exception {
AuthorizedKeysDecoder decoder = new AuthorizedKeysDecoder();
File file = new File("authorized_keys");
Scanner scanner = new Scanner(file).useDelimiter("\n");
while (scanner.hasNext()) {
System.out.println(decoder.decodePublicKey(scanner.next()));
}
scanner.close();
}
}
No es necesario agregar la biblioteca de commons como una dependencia solo para eso. Eche un vistazo a DatatypeConverter # parseBase64Binary http://docs.oracle.com/javase/7/docs/api/javax/xml/bind/DatatypeConverter.html#parseBase64Binary(java.lang.String) – LanguagesNamedAfterCofee
¡Gracias, amigo! Me ahorró horas de trabajo. –
Daría 5 ups si es posible! ¡Muchas gracias por esto! – Daniel
Para WhiteFang34,
Su código es impresionante y útil para mí también, pero tiene un pequeño insecto. El método Base64.decodeBase64 solo recibe una matriz de bytes. Así que lo arreglé así.
for (String part : keyLine.split(" ")) {
if (part.startsWith("AAAA")) {
byte [] bytePart = part.getBytes();
bytes = Base64.decodeBase64(bytePart);
break;
}
}
De todos modos, gracias por escribir el código. Espero que carguen este código en github o en otro lugar, o que me permitan subir a mis repositorios github.
Parece que depende de la clase 'Base64' que esté usando. El que se incluye con Commons Codec 1.4 y posterior tiene el método que utilicé: ['decodeBase64 (java.lang.String)'] (http://commons.apache.org/codec/api-release/org/apache/commons/ codec/binary/Base64.html # decodeBase64 (java.lang.String)) – WhiteFang34
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());
}
}
La misma solución, pero delega la decodeInt() a la DataInputStream. Elimino del código original el BouncyCastleProvider para KeyFactory tan pronto como ya conoce el algoritmo RSA.
Fuente original: https://github.com/ragnar-johannsson/CloudStack/blob/master/utils/src/com/cloud/utils/crypt/RSAHelper.java
private static RSAPublicKey readKey(String key) throws Exception {
// key = "ssh-rsa <myBase64key> <email>"
byte[] encKey = Base64.decodeBase64(key.split(" ")[1]);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(encKey));
byte[] header = readElement(dis);
String pubKeyFormat = new String(header);
if (!pubKeyFormat.equals("ssh-rsa"))
throw new RuntimeException("Unsupported format");
byte[] publicExponent = readElement(dis);
byte[] modulus = readElement(dis);
KeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(spec);
return pubKey;
}
private static byte[] readElement(DataInput dis) throws IOException {
int len = dis.readInt();
byte[] buf = new byte[len];
dis.readFully(buf);
return buf;
}
¡GRACIAS! Después de horas de golpear contra esto, tu código tuvo sentido para mí y resolvió el problema. Gran idea con el método 'readElement'. – CullenJ
- 1. Encriptación de la clave AES con la clave pública RSA
- 2. Clave de Apple clave privada/pública cuestión
- 3. Administración de authorized_keys en una gran cantidad de hosts
- 4. ¿Algún tutorial sobre cifrado de clave pública en java?
- 5. Lectura de clave pública/privada de la memoria con OpenSSL
- 6. cómo funciona la criptografía con clave pública
- 7. Encriptación con la aplicación clave pública/privada en .net
- 8. Gitosis requiere contraseña aunque la clave pública se da
- 9. Uso de una clave pública de RSA para descifrar una cadena cifrada con la clave privada de RSA
- 10. Encriptación RSA con clave pública codificada en base64 en Android
- 11. Aclaración sobre la clave pública de Android?
- 12. Ejemplo confiable de cómo usar SFTP usando autenticación de clave pública privada con Java
- 13. ¿Criptografía de clave pública con contraseñas elegidas por el usuario?
- 14. Cifrado de datos con clave pública en node.js
- 15. analizar y leer una clave pública en Java
- 16. El uso de esta palabra clave Java
- 17. ¿Cómo configuro la Autenticación de clave pública?
- 18. Error de Heroku: "Permiso denegado (clave pública)"
- 19. ¿Cómo combinar una clave privada y una clave pública de un secreto compartido en Java
- 20. iOS SecKeyRef (clave pública) enviarlo al servidor
- 21. Algoritmo de clave de activación- Seguridad
- 22. Gitolite no está actualizando el archivo authorized_keys
- 23. Extraer datos de la clave pública de RSA
- 24. C/C++ cifrar/descifrar con la clave pública
- 25. Cómo cifrar datos con clave pública en NodeJS?
- 26. Uso de la clave pública para verificar la firma en Node.JS crypto
- 27. Cómo almacenar clave privada y clave pública en KeyStore
- 28. ¿Cómo extraer la clave pública usando OpenSSL?
- 29. Dada una clave privada, ¿es posible derivar su clave pública?
- 30. Cómo cifrar una cadena con clave privada y descifrar con clave pública?
También podría solicitar a este en la lista de correo Mina. Esta es una muy buena pregunta, ya que este es uno de los pocos componentes que faltan para una implementación sshd de Java segura. –