How do I fix "There are not any available ciphers" JSch exception when attempting to use Spring Config Server?

1.6k views Asked by At

I am attempting to get an embedded Spring Config Server implementation working that reads configuration from GitHub. I'm following this tutorial:

https://mromeh.com/2017/12/04/spring-boot-with-embedded-config-server-via-spring-cloud-config/

I am getting the following Exception when my Spring Boot app tries to start up:

Caused by: com.jcraft.jsch.JSchException: There are not any available ciphers. at com.jcraft.jsch.Session.send_kexinit(Session.java:629) at com.jcraft.jsch.Session.connect(Session.java:307) at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:146) ... 23 more

The only interesting bit in my code that I see contributing to this is my bootstrap.yml file, which looks like this:

spring:
  application:
    name: DemoApplication.yml

---
spring:
  cloud:
    config:
      failFast: true
      server:
        bootstrap: true
        git:
          uri: [email protected]:mycompany/demo-config.git

I am running OpenJDK 8 v212 on MacOS, per running the following:

#> java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)

I've searched through the Spring code and documentation, and have yet to find anything about passing configuration parameters or adding code to affect how the Jsch session being used by Spring is constructed. Everything I find suggests that what I'm doing should just work.

I'm at a loss as to where to go from here. Can someone tell me what I'm missing...what I need to do to get past this problem?

2

There are 2 answers

6
df778899 On BEST ANSWER

To consolidate the comments earlier...

Behind the scenes, Spring is using JGit to make the SSH connection. By default this uses JSch to make the SSH connection, which is configured by the ~/.ssh/config file.

The wiki also has details of how to bypass JSch and use a native ssh command, the GIT_SSH environment variable can be set, e.g. to /usr/bin/ssh in OS X or Linux, or even something like C:\Program Files\TortoiseGit\bin\TortoiseGitPlink.exe.


Following the comment about how to avoid a dependency on setting the environment variable, note how the GIT_SSH environment variable is checked using a SystemReader in the TransportGitSsh.useExtSession() method.

This means one way would be to override the SystemReader class. It's not a small interface though, so would involve a fair bit of wrapping code - with the custom bit in getenv():

import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;

public class CustomSystemReader extends SystemReader {
    private final SystemReader systemReader;

    public CustomSystemReader(SystemReader systemReader) {
        this.systemReader = systemReader;
    }

    @Override
    public String getHostname() {
        return systemReader.getHostname();
    }

    @Override
    public String getenv(String variable) {
        if ("GIT_SSH".equals(variable))
            return "/usr/bin/ssh";
        return systemReader.getenv(variable);
    }

    @Override
    public String getProperty(String key) {
        return systemReader.getProperty(key);
    }

    @Override
    public FileBasedConfig openUserConfig(Config parent, FS fs) {
        return systemReader.openUserConfig(parent, fs);
    }

    @Override
    public FileBasedConfig openSystemConfig(Config parent, FS fs) {
        return systemReader.openSystemConfig(parent, fs);
    }

    @Override
    public long getCurrentTime() {
        return systemReader.getCurrentTime();
    }

    @Override
    public int getTimezone(long when) {
        return systemReader.getTimezone(when);
    }
}

Which can then be wired in like this:

    SystemReader.setInstance(
            new CustomSystemReader(SystemReader.getInstance()));
0
CryptoFool On

@df778899 gave me the direction I needed to figure this out, with the statement

As you've found, by default this uses JSch, which is configured by the ~/.ssh/config file - if that exists you may find a clue in there.

I had already looked to this file for clues, especially looking for anything about encryption setup. I saw that I had this commented out line near the top of the file:

#   Ciphers +aes256-cbc

What I had missed (I thought I'd done a text search for "cipher" but obviously, I didn't) was that buried way down in the middle of the file amidst a bunch of unrelated settings, I was doing the same thing again, not commented out this time:

Host *
    ...
    Ciphers +aes256-cbc
    ...

It is this line that is killing me with Jsch. If I comment out this one line, my simple setup works fine. @df778899's statement about ~/.ssh/config being critical to Jsch's setup was the push I needed.

This isn't going to part of my solution, but I'll point out that the following code seems to be how I can set GIT_SSH in Java, at the top of my main():

public static void main(String[] args) {

    try {
        Map<String, String> env = System.getenv();
        Field field = env.getClass().getDeclaredField("m");
        field.setAccessible(true);
        ((Map<String, String>) field.get(env)).put("GIT_SSH", "/usr/bin/ssh");
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

    SpringApplication.run(DemoApplication.class, args);
}

This also "solves" my problem. That bit about getDeclaredField("m") is particularly strange. What the heck is "m"?