Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to let user confirm host fingerprint with JSch?

In an Android app, I am attempting to connect to an SSH server using the JSch library. The remote server is specified by the user, so I don't know the remote fingerprint in advance. At the same time I don't want to set StrictHostKeyChecking to no as I see in so many examples.

I'd like to get the remote server fingerprint, show it to the user for acceptance. Is this possible either with JSch or regular Java, perhaps with sockets?

Here's an example you can try, just paste it in the onCreate of an Android activity:

new Thread(new Runnable() {
    @Override
    public void run() {

        com.jcraft.jsch.Session session;
        JSch jsch;
        try {
            jsch = new JSch();
            jsch.setLogger(new MyLogger());

            session = jsch.getSession("git", "github.com", 22);
            session.setPassword("hunter2");

            Properties prop = new Properties();
            prop.put("StrictHostKeyChecking", "yes");
            session.setConfig(prop);

            //**Get a host key and show it to the user**

            session.connect();  // reject HostKey: github.com
        }
        catch (Exception e){
            LOG.error("Could not JSCH", e);
        }
    }
}).start();
like image 596
Mendhak Avatar asked Dec 28 '25 05:12

Mendhak


2 Answers

OK I've found a way to do this. It may not be the best way but it is a way. Using the UserInfo.promptYesNo required looping at the expense of CPU while waiting for user response or with the overhead of an Executor/FutureTask/BlockingQueue. Instead the async thread which executes the connection (since network tasks cannot occur on UI thread) is more conducive to doing this twice - once to 'break' and get the user to accept, second to succeed. I guess this is the 'Android way'. For this, the hostkey needs storing somewhere. Suppose I store it in Android's PreferenceManager, then to start with grab the key from there, defaulting to empty if not available

String keystring = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("target_hostkey","");

if(!Strings.isNullOrEmpty(keystring)){
    byte[] key = Base64.decode ( keystring, Base64.DEFAULT );
    jsch.getHostKeyRepository().add(new HostKey("github.com", key ), null);
}

Next, proceed as usual to connect to the server

session = jsch.getSession("git", "github.com", 22);
session.setPassword("hunter2");

Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "yes");
session.setConfig(prop);
session.connect(); 

But this time, catch the JSchException. In there, the session has a HostKey available.

catch(final JSchException jex){

    LOG.debug(session.getHostKey().getKey());
    final com.jcraft.jsch.Session finalSession = session;

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            new MaterialDialog.Builder(MyActivity.this)
                    .title("Accept this host with fingerprint?")
                    .negativeText(R.string.cancel)
                    .positiveText(R.string.ok)
                    .content(finalSession.getHostKey().getFingerPrint(jsch))
                    .onPositive(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                            PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit().putString("target_hostkey", finalSession.getHostKey().getKey()).apply();
                        }
                    }).show();
        }
    });

}

After this, it's a matter of re-invoking the Thread or AsyncTask but this time the hostkey is added to the hostkey repository for JSch.

like image 165
Mendhak Avatar answered Dec 30 '25 17:12

Mendhak


Two possibilities:

  1. When StrictHostKeyChecking is set to ask, JSch calls UserInfo.promptYesNo with a confirmation prompt. Implement the UserInfo interface to display the confirmation to the user. Disadvantage is that you cannot customize the message in any way (of course, unless you try to parse it, relying on a hard-coded template).

    The message is like:

    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
    IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
    Someone could be eavesdropping on you right now (man-in-the-middle attack)!
    It is also possible that the -key_type- host key has just been changed.
    The fingerprint for the -key_type- key sent by the remote host is
    -key_fprint-
    Please contact your system administrator.
    Add correct host key in -file- to get rid of this message.

    For an example implementation, see the official JSch KnownHosts.java example.

  2. Even before the above, JSch calls HostKeyRepository.check, passing it hostname and the key.

    You can implement that interface/method, to do any prompt you like.


Check Session.checkHost implementation.

like image 33
Martin Prikryl Avatar answered Dec 30 '25 17:12

Martin Prikryl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!