使用JKS密钥库进行公钥验证与Ganymed SSH

问题描述:

我想从java密钥库中提取一个私钥,然后将其提供给Ganymed SSH以建立与公钥认证的连接。但是它拒绝连接。使用JKS密钥库进行公钥验证与Ganymed SSH

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

我没有得到例外,Connection.authenticateWithPublicKey(String, char[], String)只是返回false,所以这不应该是一个格式问题。我没有加密私钥,结果是一样的。另外,我可以通过putty-gen和ssh-keygen生成的文件与类似的程序进行连接。

您将需要以下编译(BouncyCastle的和ganymed):

生成与密钥库:

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. 

Edit01

我已经jsch试过这种和sshj,并且它们都无法连接。上面的私钥提取代码必须有错误。让我感到困惑的是,我可以与ssh -i联系。另外,如果我将上面的私钥加载到putty-gen中,我会得到与我的程序输出中相同的authorized_keys字符串(已经存在于我的远程计算机的.ssh/authorized_keys中)。但是,如果我将它保存为ppk文件,然后尝试与它建立腻子会话,则它也不会经过(服务器拒绝我们的密钥)。

我发布的代码存在问题。这只是我的一个巨大的失败。

我使用密钥库别名作为用户名,其中在远程linux机器上不存在作为用户帐户。创建帐户后,一切正常。我正在编辑错误的authorized_keys文件(在其他一些帐户上),并希望它能够通过一些仅为妖精,独角兽和显然是我的大脑而闻名的晦涩黑暗魔法。

至于为什么它的工作,当我做ssh -i ...默认值。愚蠢的默认值。我没有指定用户名,所以它默认为Cygwin当前正在使用的用户名,它恰好与我在编辑authorized_keys文件的远程帐户名称相同。

太好了。真棒。刚刚好。我需要找到一座桥梁来摆脱现在。

做我在我的代码做的工作与ganymed,jsch和sshj(对其他两个API的一些小的修改)。