title: Use JKS keystore for public key authentication with Ganymed SSH
body: <p>I'm trying to extract a private key from a java keystore and then feed it to Ganymed SSH in order to establish a connection with public key authentication. It is however refusing to connect. </p>
  
  <p>I can successfully connect through Cygwin (<code>ssh -i</code>) with files generated by the following program, but the program itself is unable to get past authentication on the same machine. What am I doing wrong? </p>
  
  <p>I don't get an exception, <a href="http://www.ganymed.ethz.ch/ssh2/javadoc/ch/ethz/ssh2/Connection.html#authenticateWithPublicKey%28java.lang.String,%20char[],%20java.lang.String%29" rel="nofollow">Connection.authenticateWithPublicKey(String, char[], String)</a> simply returns false, so this should not be a formatting problem. It does not matter if I leave the private key unencrypted, the result is the same. Also, I was able to connect with a program similar to this with files generated by putty-gen and ssh-keygen.</p>
  
  <p>You will need the following to compile (bouncycastle and ganymed):</p>
  
  <ul>
  <li><a href="https://www.bouncycastle.org/download/bcprov-ext-jdk15on-151.jar" rel="nofollow">bcprov</a></li>
  <li><a href="https://www.bouncycastle.org/download/bcpkix-jdk15on-151.jar" rel="nofollow">bcpkix</a></li>
  <li><a href="http://central.maven.org/maven2/ch/ethz/ganymed/ganymed-ssh2/262/ganymed-ssh2-262.jar" rel="nofollow">ganymed-ssh2</a></li>
  </ul>
  
  <p>Generate the keystore with:</p>
  
  <pre><code>keytool -genkeypair -keystore keystore.jks -alias myalias -storepass password -keypass password -keyalg RSA -dname CN=myalias,O=example.com -storetype JKS -validity 365 -v
  </code></pre>
  
  <p>Code (expects host and port as args and the above keystore in working directory):</p>
  
  <pre class="lang-java prettyprint-override"><code>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 &lt;= 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;
          }
      }
  }
  </code></pre>
  
  <p>Sample output:</p>
  
  <pre><code>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.
  </code></pre>
  
  <p><strong>Edit01</strong></p>
  
  <p>I've tried this with <a href="http://www.jcraft.com/jsch/" rel="nofollow">jsch</a> and <a href="https://github.com/hierynomus/sshj" rel="nofollow">sshj</a> and all of them fail to connect. There has to be something wrong with the above code for private key extraction. What baffles me is that I can connect with <code>ssh -i</code>. Also, if I load the above private key into putty-gen, I get the same authorized_keys string as in output from my program (already present in my remote machine's <code>.ssh/authorized_keys</code>). But if I save it as a ppk file and then try to establish a putty session with it, it doesn't go through either (server refused our key).</p>