当前位置: 首页 > 知识库问答 >
问题:

使用JKS keystore对Ganymed SSH进行公钥身份验证

郤瀚
2023-03-14

我正在尝试从java密钥库中提取一个私钥,然后将其提供给Ganymed SSH,以便建立与公钥身份验证的连接。然而,它拒绝联系。

我可以通过Cygwin(ssh-i)与以下程序生成的文件成功连接,但程序本身无法通过同一台机器上的身份验证。我做错了什么?

我没有得到异常,connection.authenticateWithPublicKey(String,char[],String)简单地返回false,所以这不应该是一个格式问题。如果我不加密私钥,结果是一样的。此外,我还能够与类似的程序连接由putty-gen和ssh-keygen生成的文件。

您将需要以下代码来编译(bouncycastle和ganymed):

  • BCPROV
  • bcpkix
  • Ganymed-SSH2

生成密钥存储库时使用:

keytool -genkeypair -keystore keystore.jks -alias myalias -storepass password -keypass password -keyalg RSA -dname CN=myalias,O=example.com -storetype JKS -validity 365 -v
import ch.ethz.ssh2.Connection;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.openssl.PEMEncryptor;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;

public class KeystoreGanymedSSH {

    public static void main(String[] args)
            throws Exception {
        String keystorePath = "keystore.jks";
        char[] password = "password".toCharArray();
        String alias = "myalias";

        String host = args[0];
        int port = Integer.parseInt(args[1]);

        // keystore init
        KeyStore keystore = KeyStore.getInstance("JKS");
        InputStream in;
        try {
            in = new FileInputStream(keystorePath);
        } catch (FileNotFoundException ex) {
            System.out.println("Generate keystore using this command:");
            System.out.println("keytool -genkeypair -keystore keystore.jks"
                    + " -alias myalias -storepass password -keypass password"
                    + " -keyalg RSA -dname CN=myalias,O=example.com -storetype"
                    + " JKS -validity 365 -v");
            throw ex;
        }
        try {
            keystore.load(in, password);
        } finally {
            in.close();
        }

        // get public key in OpenSSH format
        String authorizedKeysEntry = genAuthorizedKeysEntry(keystore, alias);
        if (authorizedKeysEntry == null) {
            throw new Exception("could not generate authorized_keys entry");
        }
        System.out.println("Public key for pasting into OpenSSH authorized_keys file (always same):");
        System.out.println(authorizedKeysEntry);
        System.out.println();

        Writer writer;

        // write to file
        writer = new OutputStreamWriter(
                new FileOutputStream(new File("authorized_keys")), "UTF-8");
        try {
            writer.write(authorizedKeysEntry);
        } finally {
            writer.close();
        }

        // obtain PEM encrypted char[]
        Key key = keystore.getKey(alias, password);
        writer = new CharArrayWriter();
        JcaPEMWriter pw = new JcaPEMWriter(writer);
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        PEMEncryptor encryptor = new JcePEMEncryptorBuilder("DES-EDE3-CBC")
                .setSecureRandom(random).build(password);
        pw.writeObject(key, encryptor);
        pw.flush();
        char[] privateKey = ((CharArrayWriter)writer).toCharArray();

        System.out.println("Encrypted private key (changes on each run):");
        System.out.println(new String(privateKey));
        String name = "RSA".equals(key.getAlgorithm()) ? "id_rsa" : "id_dsa";
        writer = new OutputStreamWriter(
                new FileOutputStream(new File(name)), "UTF-8");
        try {
            writer.write(privateKey);
        } finally {
            writer.close();
        }

        // attempt ganymed connection   
        Connection connection = null;
        try {
            System.out.println("Connecting to " + host + ":" + port);
            connection = new Connection(host, port);        
            connection.connect(); // no known_hosts

            if (!connection.isAuthMethodAvailable(alias, "publickey")) {
                System.out.println("Public key auth is not available.");
                return;
            }

            boolean result = connection.authenticateWithPublicKey(
                    alias, privateKey, new String(password));

            System.out.println(result ? "Authentication success." : "Authentication failure.");
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

    }

    private static String genAuthorizedKeysEntry(
            KeyStore keystore, String alias) throws GeneralSecurityException, IOException {
        Certificate[] chain = keystore.getCertificateChain(alias);
        if (chain == null || chain.length <= 0) {
            return null;
        }
        PublicKey publicKey = chain[0].getPublicKey();
        if ("RSA".equals(publicKey.getAlgorithm())) {
            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            String type = "ssh-rsa";
            dos.writeInt(type.getBytes("UTF-8").length);
            dos.write(type.getBytes("UTF-8"));
            byte[] exponent = rsaPublicKey.getPublicExponent().toByteArray();
            dos.writeInt(exponent.length);
            dos.write(exponent);
            byte[] modulus = rsaPublicKey.getModulus().toByteArray();
            dos.writeInt(modulus.length);
            dos.write(modulus);
            String encoded = DatatypeConverter.printBase64Binary(
                    baos.toByteArray());
            return type + " " + encoded + " " + alias;
        } else if ("DSA".equals(publicKey.getAlgorithm())) {
            DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
            DSAParams params = dsaPublicKey.getParams();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            String type = "ssh-dss";
            dos.writeInt(type.getBytes("UTF-8").length);
            dos.write(type.getBytes("UTF-8"));
            byte[] p = params.getP().toByteArray();
            dos.writeInt(p.length);
            dos.write(p);
            byte[] q = params.getQ().toByteArray();
            dos.writeInt(q.length);
            dos.write(q);
            byte[] g = params.getG().toByteArray();
            dos.writeInt(g.length);
            dos.write(g);
            byte[] y = dsaPublicKey.getY().toByteArray();
            dos.writeInt(y.length);
            dos.write(y);
            String encoded = DatatypeConverter.printBase64Binary(
                    baos.toByteArray());
            return type + " " + encoded + " " + alias;
        } else {
            return null;
        }
    }
}
Public key for pasting into OpenSSH authorized_keys file (always same):
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCJLXgRaVdbZNuCsTgUsw2UPGdEA4La8ggQZWkevgAEMrgF+YYT2uN6BYDgD7hzs3ZTLXz2KUQLkMe7xLvimAsg6YXUi46IGEkTSOBFR0yYj+12O2BNbAxOXLIDIMBK5bsDwnuOsFedbeILFU4DaV+igJKO1zHWNbmbmd4RlfrIgH7Blfce8zSVkEdLkqEmydbg4xmj6r+MlzA5HSNZJILivb1XYNnoLjRH1SwUC8Rj6bjgBdNEXLOH0FNpCatHk9R00GaSZjcDZRKNAKnBSEIpw01TKaJlyQUTGqYGjK7UIbbafwMuYKR1rIzkyh4Usxvd3FvMdmKQSUeCnZU296YF myalias

Encrypted private key (changes on each run):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,86DF7B50D4E319F6

QGngqwq+NXQGee7pEVROnwvIX6cyzo9QjdKtlherLCIYQjD4zagCvCd4vOUmwe0S
g/KaPeq8tanU0KB6m/WfAsgfOAkR+ujIod0hkUoCZp/Jg9LO2Me7ZFDNW/cBqbW0
CifaqDmOVgJ+HnZHJouMZyPf+To8SDdMSJZQ/3Wc2ZQn6BIhEjLzdz0hSVXGz2Gs
wmVYy4oedjB2f+dQliEnwFXBOusRMfqgiPqkdQj38voipqYBmPHUYSity0HGsRlo
KgovSeQhEDPT8GyYVJcKZLV3BUipvNvKJBP613wBZsuCtvhUNNNOfVFeTkD8+7PG
q2YzF3nasOM471EHyj18zPZ+DdPQMDPHtpVFQXJCFlf7xlGVAesPPUFJICXFE5OZ
JRAJC8+7WuT8O974uT9zDLeV5XLJRJK6o8dYXtZBK0YMpZy91b9axYDeVQh+Sfd+
V/T5RQ2osXg72lDBtz6vzeyMGj+Y9PQwZb97tdRW1X/ON4Eiiz/+1SVeDbWilo29
gMSPl4wb379Dvi7Z+b5OTGoF+F1p7Cp48sUEgIP8vPXinoOhBLdy1zq8oNVbo7PS
M5+41PKL4ao8pL9BCOALZpzP2R9LxoHjjL1auaWMzKLECtiEDvgu4GJeTVXvg7sO
yptecswVCF8fY+pV8dTtYU3vUs4UsdC9PG9IhqeRbML9dX7htsgkBmHdYAq5WOS/
RREuU+jyrCnc6kpOhIK/1wMOoIMOBnJ8EJXpMJaZtNwOQr05bOfFvozOEe35JwnP
NElP7CYBIvQrTyfrRxtJE+ntQO+uJbIvxFDY0EoQJX6YPFr0V7rnWy4W1yH0Yv6E
pmwERGYr1lbBIpxjTzTwZ3r845EUEwiwEt3+xfepBh3HUXg/mZYUw4cEz3HbzZDT
tWRPFpsBaicfatzbqvL7Teq1V8baUj1CW0wrANZbHc0FvSzuHMygub2ARgM3QAMj
L5yaITjH8/Tnbew7jPi5kSTXdNUnAJf3M/m6DC7svJtx+1Xwd0tfzp3GHYLaT+Mm
vOu8R5g/JJvBVMTzP8gyI32jDViRuHHwyFOlyJ35IrRCkW8i+aBmG1iT2WANWRai
2ujX4Gc+M2VncUdFR9MoCxUOy/7qKDcGNMpk8sgIi6Pc8SLiodueiWP3W6AXJKvs
u5akyk7jj8zq9+fe85T/cZ8lYe81hd3oA/9b/9cs8sdlhTmYjfUr1FgFHNyFPwdV
QnyayxeAy3xvoYXBBr7JrmWXLDTHghhMBHGHW7imoLNN8QZtTF+pGWzsxNcAVbEz
kmLll9ki0CUIbfufszp/b05OBC2M0EHn9uW61bwbiZfWxhfTlC2zHNHpig6zQhHu
q8n//KgHB5LDctGHoeqlUwoLbt78wd0bAD23GeZ2q1CdB6FYxoMYL8FuVOnxoUh3
fquXzH0wjv3Qm4Rwit+8zSdbD/+QbtJ2c/ZguUy4T3phI5BGzhLh2IDO8T9B6y5B
MmTyFjfZjVj+zU4F0BAIzzLlYTl332ecMj87StoNazqIF5Dj2ZqjUtF46MDeMZjO
tRvpIi8bWBm78rFNC51TZSBcfw714yOxHsPU0PqUMQMCgXawcDkTt2645/+ZZQtk
-----END RSA PRIVATE KEY-----

Connecting to 10.0.3.138:22
Authentication failure.

共有1个答案

陈松
2023-03-14

我发布的代码没有错误。这只是我的一个巨大失败。

我使用keystore别名作为一个用户名,该用户名在远程linux Box上不作为用户帐户存在。创建帐户后,一切正常。我编辑了错误的authorized_keys文件(在其他帐户上),并期望它能通过一些只有妖精、独角兽和显然我的大脑才知道的晦涩的黑魔法发挥作用。

至于为什么在我做ssh-i的时候它能起作用...默认值。愚蠢的违约。我根本没有指定用户名,所以它默认为Cygwin当前正在使用的用户名,只是碰巧它与我正在编辑authorized_keys文件的远程帐户同名。

太好了。太棒了.只是...完美。我得去找座桥从现在开始跳下去。

执行我在代码中所做的操作可以使用ganymed、jsch和sshj(对其他两个API进行了一些小的修改)。

 类似资料:
  • 我正在尝试使用私钥和公钥对为sftp服务器设置身份验证。 我已经在本地计算机上设置了带有Bitvise SSH服务器的sftp服务器。我用SSH服务器生成了私钥和公钥。我已经在SSH服务器的主机密钥部分上设置了私钥,并且已经创建了一个虚拟帐户并将公钥设置到该帐户。 谢谢

  • 我正在构建一个具有客户机/服务器基础结构的应用程序,并希望使用公钥/私钥方法实现一个身份验证机制。 让我们假设一个客户机拥有私钥,而服务器只拥有公钥。在身份验证过程中,客户端使用私钥对消息进行签名,并将其发送到服务器,在服务器上使用公钥对消息进行验证。如果验证成功,则对客户端进行身份验证。 下面是一些JUnit测试代码,我在这些代码中熟悉了这些概念:

  • 我正在尝试使用urllib3连接到网页。代码如下所示。 如果我们假设url是需要使用用户名和密码进行身份验证的某个网页,那么我是否使用正确的代码进行身份验证? 我使用urllib2做这件事很舒服,但使用urllib3做不到同样的事情。 非常感谢

  • jwt不应该仅仅用于认证用户吗?我读到过可以在里面存储非敏感的东西,比如用户ID。将权限级别之类的东西存储在令牌中可以吗?这样我可以避免数据库调用。

  • 问题内容: 我正在用TastyPie制作内部API。我有 禁用身份验证规则后,我的API效果很好。启用它后,无论尝试如何,都会收到401(未经授权)的响应。 我敢肯定,一旦你看到它的实际作用,这就是其中的一件事,但与此同时,请告知如何提出请求(GET)。 问题答案: 将用户名和api_key参数添加到你的GET变量中。确保你拥有 设置时,请确保遵循docs的其他说明: ApiKey身份验证 作为要

  • 面向公共的服务需要api密钥。 “专用”服务只能接受来自群集内部的呼叫,而不能接受外部世界的呼叫。 每个api都标识一个用户,并且用户对象必须可用于rest服务。 在一个JAX-RS应用程序中有什么标准的方法可以做到这一点吗?(我们用的是resteasy。) 我读过关于过滤器、拦截器和基本身份验证的所有内容,但我不清楚什么是最好的方法。