17

I'm using JGit to access a remote Git repo, and I need to use SSH for it. JGit uses JSch to provide secure access. However, I'm not sure how to set the key file and the knows hosts file for JGit. What I have tried is as follows.

Created a custom configuration of the SshSessionFactory, using by subclassing JSchConfigSessionFactory:

public class CustomJschConfigSessionFactory extends JschConfigSessionFactory {
    @Override
    protected void configure(OpenSshConfig.Host host, Session session) {
        session.setConfig("StrictHostKeyChecking", "yes");
    }
}

In the class which I access the remote Git repo, did the following:

CustomJschConfigSessionFactory jschConfigSessionFactory = new CustomJschConfigSessionFactory();

JSch jsch = new JSch();
try {
    jsch.addIdentity(".ssh/id_rsa");
    jsch.setKnownHosts(".ssh/known_hosts");
} catch (JSchException e) {
    e.printStackTrace();  
}
    SshSessionFactory.setInstance(jschConfigSessionFactory);

I can't figure out how to associate this JSch object with JGit so that it can successfully connect to the remote repository. When I try to clone it with JGit, I get the following exception:

org.eclipse.jgit.api.errors.TransportException: git@git.test.com:abc.org/test_repo.git: reject HostKey: git.test.com
at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:137)
at org.eclipse.jgit.api.CloneCommand.fetch(CloneCommand.java:178)
at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:125)
at GitTest.cloneRepo(GitTest.java:109)
at GitTest.main(GitTest.java:223)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.eclipse.jgit.errors.TransportException: git@git.test.com:abc.org/test_repo.git: reject HostKey: git.test.com
at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:142)
at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:121)
at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:248)
at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:147)
at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:136)
at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:122)
at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1104)
at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:128)
... 9 more
Caused by: com.jcraft.jsch.JSchException: reject HostKey: git.test.com
at com.jcraft.jsch.Session.checkHost(Session.java:748)
at com.jcraft.jsch.Session.connect(Session.java:321)
at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:116)
... 16 more

I have added the git.test.com entry to my /etc/hosts file. I have used the same code to access a git repo with a http url, so the code it working fine. It's the key handling part that is failing. Any idea on how to handle this?

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
Izza
  • 2,389
  • 8
  • 38
  • 60
  • I see that you created a jsch object, but you haven't assigned it to anything. Why have you created it? – nix86 Nov 26 '19 at 13:49

3 Answers3

14

You need to override the getJSch method in your custom factory class:

class CustomConfigSessionFactory extends JschConfigSessionFactory
{
    @Override
    protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
        JSch jsch = super.getJSch(hc, fs);
        jsch.removeAllIdentity();
        jsch.addIdentity( "/path/to/private/key" );
        return jsch;
    }
}

Calling jsch.removeAllIdentity is important; it doesn't seem to work without it.

A caveat: I wrote the above in Scala, and then translated it over to Java, so it might not be quite right. The original Scala is as follows:

class CustomConfigSessionFactory extends JschConfigSessionFactory
{
    override protected def getJSch( hc : OpenSshConfig.Host, fs : FS ) : JSch =
    {
        val jsch = super.getJSch(hc, fs)
        jsch.removeAllIdentity()
        jsch.addIdentity( "/path/to/private/key" )
        jsch
    }
}
Lucas Holt
  • 3,826
  • 1
  • 32
  • 41
paulmdavies
  • 1,248
  • 3
  • 12
  • 28
  • 1
    My problem was that I needed to be able to specify the passphrase for the private key, which you can either add as another parameter to `addIdentity` or by creating your own class that implements `UserInfo` and then set the `session` parameter to the `configure` method of `CustomConfigSessionFactory` to use that `UserInfo`. – WhiteKnight Feb 03 '16 at 13:06
  • 3
    Special thanks for the `jsch.removeAllIdentity()` hint! – Vincent Nov 08 '16 at 16:09
  • What I'm seeing is that still the ~/.ssh/id_rsa is returned from the Host but found no way to set this to null so it returns the one set in the code. – javydreamercsw Mar 24 '20 at 18:53
6

Managed to find the issue. The public key in the server side had a different name other than the usual id_rsa.pub, while the private key on my side was id_rsa. JSch expects by default the public key to have the same name as the private key plus the .pub suffix. Using a key pair with a common name (ex.: private = key_1 and public = key_1.pub) solves the issue.

Izza
  • 2,389
  • 8
  • 38
  • 60
6

Jsch sesems to not like a known_hosts file in the hashed format-- it must conform to the format produced by:

ssh-keyscan -t rsa hostname >> ~/.ssh/known_hosts

e.g.

<hostname> ssh-rsa <longstring/longstring>

not:

 |1|<hashed hostname>= ecdsa-sha2-nistp256 <hashed fingerprint>=
joshwa
  • 1,660
  • 3
  • 17
  • 26