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

时间:2010-08-20 13:53:49

标签: java security openssh

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

4 个答案:

答案 0 :(得分:17)

我很惊讶那里没有什么明显的。我很好奇并实现了一种解码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();
    }
}

答案 1 :(得分:6)

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

答案 2 :(得分:2)

致WhiteFang34,

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

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

无论如何,感谢您编写代码。我希望您将此代码上传到github或其他地方,或者允许我上传到我的github存储库中。

答案 3 :(得分:2)

相同的解决方案,但将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 <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;
}