Search code examples
javasshjsch

Two-step verification using JSch library


I am trying to use Java to connect to a Linux server which has a two-step verification login. I'm using the JSch library, this is the code I've got so far:

session = jsch.getSession(username, ip);
Properties config = new java.util.Properties(); 
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
        
session.setPassword(password);
session.connect();
System.out.println("Connected to " + ip);

Obviously when I run this script I get an "Auth failed" error as I haven't enter the authentication key. So how do I login using the verification key. If this isn't possible could someone suggest a library that has this functionality.

This is logging into the server using putty. So you enter the username, then the time based generated code, then the password. enter image description here


Solution

  • I worked this out eventually, you have to implement UserInfo and UIKeyboardInteractive. Use the promptKeyboardInteractive method, to make it return the authentication key, shown in the following code which works for me:

    import java.security.InvalidKeyException;
    
    import com.jcraft.jsch.Channel;
    import com.jcraft.jsch.ChannelExec;
    import com.jcraft.jsch.JSch;
    import com.jcraft.jsch.Session;
    import com.jcraft.jsch.UIKeyboardInteractive;
    import com.jcraft.jsch.UserInfo;
    
    public class UserAuthKI{
      public static void main(String[] arg){
    
        try{
          JSch jsch=new JSch();
    
          String host="";
          String user="";
    
          Session session=jsch.getSession(user, host, 22);
    
          // username and passphrase will be given via UserInfo interface.
          UserInfo ui=new MyUserInfo();
          session.setUserInfo(ui);
          session.connect();
    
          Channel channel =session.openChannel("exec");
          ((ChannelExec)channel).setCommand("echo 'hello'");
          
          channel.setInputStream(System.in);
          channel.setOutputStream(System.out);
    
          channel.connect();
          
        }
        catch(Exception e){
          System.out.println(e);
        }
      }
    
      public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{
        public String getPassword(){ return "passwordHere"; }
        public boolean promptYesNo(String str){
          return true;
        }
    
        public String getPassphrase(){return null;}
        public boolean promptPassphrase(String message){ return false; }
        public boolean promptPassword(String message){
          return true;
        }
        public void showMessage(String message){
          System.out.println(message);
        }
    
        public String[] promptKeyboardInteractive(String destination,
                                                  String name,
                                                  String instruction,
                                                  String[] prompt,
                                                  boolean[] echo){
        
          System.out.println("destination: "+destination);
          System.out.println("name: "+name);
          System.out.println("instruction: "+instruction);
          System.out.println("prompt.length: "+prompt.length); 
        
          String[] str = new String[1];
        
          if(prompt[0].contains("Password:")){
            str[0] = getPassword();
          }
          else if(prompt[0].contains("Verification code: ")){
            try {
              str[0] = PasswordUtils.verify_code("CODEHERE");
            } catch (InvalidKeyException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
          }
          else{
            str = null;
          }
        
          return str;
        }
      }
    }
    

    (PasswordUtils.verif_code() is the static method which generates the key)