Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating with JGit #43

Closed
HarrisonMc555 opened this issue May 6, 2021 · 2 comments
Closed

Integrating with JGit #43

HarrisonMc555 opened this issue May 6, 2021 · 2 comments

Comments

@HarrisonMc555
Copy link

I recently started using JGit for an internal application. When I tried to run it on our server, however, I got an error. Eventually, I discovered it was because JCraft's JSch does not support the OpenSSH private key format.

I looked into using this project as a drop-in replacement for JCraft's JSch. I used the following lines in my Gradle build.

dependencies {
    implementation("org.eclipse.jgit:org.eclipse.jgit:5.11.0.202103091610-r")
    // The default implementation of JSch does not support new OpenSSH key formats
    implementation("com.github.mwiede:jsch:0.1.62")
}

configurations.all {
    resolutionStrategy.eachDependency {
        if (requested.group == "com.jcraft" && requested.name == "jsch") {
            useTarget("com.github.mwiede:jsch:0.1.58")
            because("default jsch does not support OpenSSH keys")
        }
    }
}

Unfortunately, if I did that, I did not have a working implementation of org.eclipse.jgit.transport.SshConnectionFactory.

I then tried adding implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:5.11.0.202103091610-r") back into my list of dependencies. Now I had a working SshConnectionFactory. However, I then got a NullPointerException because the JschConfigSessionFactory expected a value for "signature.rsa" to be present in the JSch config, which this fork does not have.

Do you have any tips for how to integrate this fork with JGit? I know it's not your project but figured this would be a good place to ask. I've tried adding my own implementation of org.eclipse.jgit.transport.SshConnectionFactory but haven't been able to convince the class loader to find it (I'm very inexperienced with this area of Java).

If the answer is "you'll have to figure it out yourself" that's completely understandable 😃

@norrisjeremy
Copy link
Contributor

norrisjeremy commented May 7, 2021

I looked at the code for JschConfigSessionFactory and I believe the source of the NullPointerException would be here: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java#L401, where JschConfigSessionFactory attempts to add "ssh-rsa" into the JSch config with the same value as "signature.rsa".

In this fork of JSch, the "signature.rsa" element of the JSch config has been dropped, and in fact already has a "ssh-rsa" config element (see https://github.com/mwiede/jsch/blob/master/src/main/java/com/jcraft/jsch/JSch.java#L118).

I suspect your best bet to get this JSch fork working with JGit will be to somehow call JSch.setConfig("signature.rsa", JSch.getConfig("ssh-rsa")); early in your application before the Java service loader attempts to load org.eclipse.jgit.transport.SshConnectionFactory. I don't know how your application is structured to determine how feasible this is.

You might also be able to create your own instance of org.eclipse.jgit.transport.SshSessionFactory by extending the org.eclipse.jgit.transport.JschConfigSessionFactory class, and simply adding a static initializer that does this. Something like:

package xxx.yyy;

import org.eclipse.jgit.transport.JschConfigSessionFactory;

public class MyJschConfigSessionFactory extends JschConfigSessionFactory {
        static {
            JSch.setConfig("signature.rsa", JSch.getConfig("ssh-rsa"));
        }
}

You would then need to create a file under META-INF/services named org.eclipse.jgit.transport.SshSessionFactory that would contain the full class name to your override:

xxx.yyy.MyJschConfigSessionFactory

@HarrisonMc555
Copy link
Author

@norrisjeremy thank you so much for the thorough response! I actually ended up finding a similar solution by overriding the createDefaultJsch method. However, I like your idea better and will try that out as well. I'll try it out and post my results here afterward.

This is what I ended up with for anyone who is interested:

public class CustomJschConfigSessionFactory extends JschConfigSessionFactory
{
    @Override
    protected JSch createDefaultJSch(FS fs) throws JSchException
    {
        final JSch jsch = new JSch();
        JSch.setConfig("ssh-rsa", JSch.getConfig("ssh-rsa"));
        JSch.setConfig("ssh-dss", JSch.getConfig("signature.dss"));
        configureJSch(jsch);
        knownHosts(jsch, fs);
        identities(jsch, fs);
        return jsch;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Copied and pasted directly from JschConfigSessionFactory because the original methods were private.
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    protected static void knownHosts(JSch sch, FS fs) throws JSchException
    {
        final File home = fs.userHome();
        if (home == null) return;
        final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); //$NON-NLS-1$ //$NON-NLS-2$
        try (FileInputStream in = new FileInputStream(known_hosts))
        {
            sch.setKnownHosts(in);
        }
        catch (IOException none)
        {
            // Oh well. They don't have a known hosts in home.
        }
    }

    protected static void identities(JSch sch, FS fs)
    {
        final File home = fs.userHome();
        if (home == null) return;
        final File sshdir = new File(home, ".ssh"); //$NON-NLS-1$
        if (sshdir.isDirectory())
        {
            loadIdentity(sch, new File(sshdir, "identity")); //$NON-NLS-1$
            loadIdentity(sch, new File(sshdir, "id_rsa")); //$NON-NLS-1$
            loadIdentity(sch, new File(sshdir, "id_dsa")); //$NON-NLS-1$
        }
    }

    protected static void loadIdentity(JSch sch, File priv)
    {
        if (priv.isFile())
        {
            try
            {
                sch.addIdentity(priv.getAbsolutePath());
            }
            catch (JSchException e)
            {
                // Instead, pretend the key doesn't exist.
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants