使用来自authorized_keys的公钥和Java安全性

如何使用系统authorized_keys文件中的条目来实现java.security.PublicKey ? 我特别想要将authorized_keys文件中的公钥与Apache SSHD PublickeyAuthenticator接口中提供的公钥进行比较。

我很惊讶那里没有什么明显的。 我很好奇并实现了解码authorized_keys文件的方法。 这取决于用于Base64解码的Apache Commons Codec。

 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(); } } 

如果要反转该过程,即将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()); } } 

致WhiteFang34,

您的代码对我来说也很棒,也很有用,但它有一个小错误。 Base64.decodeBase64方法只接收字节数组。 所以我这样修好了。

  for (String part : keyLine.split(" ")) { if (part.startsWith("AAAA")) { byte [] bytePart = part.getBytes(); bytes = Base64.decodeBase64(bytePart); break; } } 

无论如何,感谢您编写代码。 我希望你把这个代码上传到github或其他地方,或者允许我上传到我的github repos。

相同的解决方案,但将decodeInt()委托给DataInputStream。 一旦知道了RSA算法,我就会从原始代码中删除KeyFactory的BouncyCastleProvider。

原始来源: 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  " 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; }