2014-10-06 117 views
2

我想從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文件,然後嘗試與它建立膩子會話,則它也不會經過(服務器拒絕我們的密鑰)。

回答

3

我發佈的代碼存在問題。這只是我的一個巨大的失敗。

我使用密鑰庫別名作爲用戶名,其中在遠程linux機器上不存在作爲用戶帳戶。創建帳戶後,一切正常。我正在編輯錯誤的authorized_keys文件(在其他一些帳戶上),並希望它能夠通過一些僅爲妖精,獨角獸和顯然是我的大腦而聞名的晦澀黑暗魔法。

至於爲什麼它的工作,當我做ssh -i ...默認值。愚蠢的默認值。我沒有指定用戶名,所以它默認爲Cygwin當前正在使用的用戶名,它恰好與我在編輯authorized_keys文件的遠程帳戶名稱相同。

太好了。真棒。剛剛好。我需要找到一座橋樑來擺脫現在。

做我在我的代碼做的工作與ganymed,jsch和sshj(對其他兩個API的一些小的修改)。