JSch SSH connection throws NPE when initializing reverse tunnel - java

When I'm adding a reverse tunnel to a com.jcraft.jsch.Session object, the connection initialization fails with the following stacktrace:
com.jcraft.jsch.JSchException: java.lang.NullPointerException
at com.jcraft.jsch.Session._setPortForwardingR(Session.java:2165)
at com.jcraft.jsch.Session.setPortForwardingR(Session.java:1937)
at com.jcraft.jsch.Session.setPortForwardingR(Session.java:1883)
at com.project.client.handlers.SshClientHandler.<init>(SshClientHandler.java:41)
at com.project.client.pcConnection.init(SdConnection.java:30)
at Sdclient.main(Unknown Source)
Caused by: java.lang.NullPointerException
at com.jcraft.jsch.Packet.padding(Packet.java:58)
at com.jcraft.jsch.Session.encode(Session.java:892)
at com.jcraft.jsch.Session._write(Session.java:1362)
at com.jcraft.jsch.Session.write(Session.java:1357)
at com.jcraft.jsch.Session._setPortForwardingR(Session.java:2160)
... 5 more
The full code there is
private static JSch sshConn = null;
private Session sshSession;
public SshClientHandler(int _sshLocalSp, int _sshRemoteSp) {
JSch.setLogger(new JSCHLogger());
sshConn = new JSch();
try {
createTemporarySshFiles();
sshConn.setKnownHosts(GeneralMethods.getPreference(PcPreferencesEnum.SSH_KNOWN_HOSTS_FILE));
sshConn.addIdentity(GeneralMethods.getPreference(PcPreferencesEnum.SSHC_PRIVATE_KEY_FILE), GeneralMethods.getPreference(PcPreferencesEnum.SSHC_PUBLIC_KEY_FILE), "".getBytes());
sshSession = sshConn.getSession(GeneralMethods.getPropValue("pcclient.id"), "sshserver.project.com", 22);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
sshSession.setConfig(config);
sshSession.setTimeout(15000);
sshSession.setPassword("");
//sshSession.setPortForwardingR("50000:localhost:22");
sshSession.setPortForwardingR(50000, "127.0.0.1", 22);
sshSession.connect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The connection estabishes successfully w/ publickey authentication when I remove the line
sshSession.setPortForwardingR(50000, "127.0.0.1", 22);
The SSH user has the right to connect to the local port 50000 on the remote machine. Here is a snippet from it's authorized_keys
no-pty,permitopen="localhost:50000",command="/bin/echo not-allowed-to-do-this",no-X11-forwarding ssh-rsa AAAA[...]
I switched arguments for setPortForwardingR back and forth, as - for example - some documents I found online use the remote machine as second argument, some use localhost, but with no success.
Watching auth.log on the remote server indicates that the connection is not even initiated. The NullPointerException gets thrown on the actual line of the setPortForwardingR call. I ensured that my local SSH server is running on the local port 22, and I can connect manually to it. I tried different ports (to my local MySQL server, e.g.), but it always fails with the same stacktrace.
I'm using jsch-0.1.52.jar.

You have to call the .setPortForwardingR() only after the .connect().
See for example:
http://www.jcraft.com/jsch/examples/Daemon.java.html

Related

Why does JSch work on a Linux system, but not on Windows?

I created a Java program that has to make a SSH connection and execute ls on an Linux archive.
It works fine when I run this code on a Linux system using JSch. However, I try to make the program run on a Windows machine, it exits without any message at session.connect().
Using Log4j, the only thing I do know is, that it stops at the session.connect() line.
Is there a difference in using Jsch from Linux and Windows?
Below is the code for the connection.
public void openSession() {
JSch jsch = new JSch();
try {
session = jsch.getSession(user, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
logger.info("Establishing Connection...");
session.connect();
System.out.println("Connection established.");
logger.info("Connection established.");
System.out.println("Creating SFTP Channel.");
logger.info("Connection established.");
sftpChannel = (ChannelSftp) session.openChannel("sftp");
sftpChannel.connect();
System.out.println("SFTP Channel created.");
logger.info("SFTP Channel created.");
} catch (JSchException e) {
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
logger.error(errors);
logger.error(e.getCause());
}
}
Here is the exception, that seems to cause the issue:
Exception in thread "main" java.lang.ExceptionInInitializerError
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:548)
at com.jcraft.jsch.jce.AES256CTR.init(AES256CTR.java:56)
at com.jcraft.jsch.Session.checkCipher(Session.java:2497)
at com.jcraft.jsch.Session.checkCiphers(Session.java:2474)
at com.jcraft.jsch.Session.send_kexinit(Session.java:624)
at com.jcraft.jsch.Session.connect(Session.java:307)
at com.jcraft.jsch.Session.connect(Session.java:183)
at controller.SshFileHandler.openSession(SshFileHandler.java:241)
at controller.SshFileHandler.<init>(SshFileHandler.java:50)
at controller.MainController.selectDataSource(MainController.java:69)
at controller.MainController.run(MainController.java:42)
at controller.Application.main(Application.java:22)
Caused by: java.lang.SecurityException: Can not initialize cryptographic mechanism
at java.base/javax.crypto.JceSecurity.<clinit>(JceSecurity.java:119)
... 12 more
Caused by: java.lang.SecurityException: Can't read cryptographic policy directory: unlimited
at java.base/javax.crypto.JceSecurity.setupJurisdictionPolicies(JceSecurity.java:333)
at java.base/javax.crypto.JceSecurity$1.run(JceSecurity.java:110)
at java.base/javax.crypto.JceSecurity$1.run(JceSecurity.java:107)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
at java.base/javax.crypto.JceSecurity.<clinit>(JceSecurity.java:106)
... 12 more

Simple Socks over SSH tunnel in Andorid Java

I need to setup a socks proxy (tunnel) over ssh in android with java (android studio). I searched a lot but I couldn't find any solutions. This is my code:
int assigned_port;
int local_port=8588;
int remote_port=22;
String remote_host = "server";
String login = "root";
String password = "password";
try {
JSch jsch = new JSch();
// Create SSH session. Port 22 is your SSH port which
// is open in your firewall setup.
Session session = jsch.getSession(login, remote_host, 22);
session.setPassword(password);
// Additional SSH options. See your ssh_config manual for
// more options. Set options according to your requirements.
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
// config.put("Compression", "yes");
config.put("ConnectionAttempts","2");
session.setConfig(config);
// Connect
session.connect();
// Create the tunnel through port forwarding.
// This is basically instructing jsch session to send
// data received from local_port in the local machine to
// remote_port of the remote_host
// assigned_port is the port assigned by jsch for use,
// it may not always be the same as
// local_port.
assigned_port = session.setPortForwardingL(local_port,
remote_host, remote_port);
} catch (JSchException e) {
System.out.println("JSch:" + e.getMessage());
return;
}
if (assigned_port == 0) {
System.out.println("Port forwarding failed!");
return;
}
There is no error but it didn't work. I need a simple code of socks tunneling.

"reject HostKey" when connecting to remote host through jumphost with JSch

Need to SSH to destination host through jumphost. Had tried the same mentioned in JSch JumpHosts example.
Session[] sessions = new Session[2];
Session session = null;
sessions[0] = session = jsch.getSession(getUserName(), "jumphost1.com", 22);
session.setPassword(getHostPassword());
UserInfo userInfo = new UserInfo();
userInfo.setPassword(getHostPassword());
session.setUserInfo(userInfo);
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
prop.put("PreferredAuthentications", "publickey,keyboard-interactive,password");
session.setConfig(prop);
session.connect();
String host = "host1.com";
int assignedPort = session.setPortForwardingL(0, host, 22);
LOGGER.info("Jump host the {} of agent {} and port forwarding {}", i, host, assignedPort);
sessions[i] = session = jsch.getSession(getUserName(), "127.0.0.1", assignedPort);
session.setPassword(getHostPassword());
userInfo = new UserInfo();
userInfo.setPassword(getHostPassword());
session.setUserInfo(userInfo);
session.setHostKeyAlias(host);
session.connect();
Getting below exception when connection to destination host:
Caused by: com.jcraft.jsch.JSchException: reject HostKey: 127.0.0.1
at com.jcraft.jsch.Session.checkHost(Session.java:799)
at com.jcraft.jsch.Session.connect(Session.java:345)
at com.jcraft.jsch.Session.connect(Session.java:183)
I am trying to login to host host1.com through jumphost1.com
login to jumphost1.com
then ssh host1.com
execute the commands in the host1
Your code for connecting through jumphost is correct.
The only problem is that your local host key repository contains a different host key for the second host, than what you receive from the real (second) host.
You actually do not seem to care about security, as you set StrictHostKeyChecking=no for the jumphost session (what the official example rightly does not do!). But you do not do the same for the second session, hence the error.
See also How to resolve Java UnknownHostKey, while using JSch SFTP library?

JSch - How to let user confirm host fingerprint?

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();
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.
Two possibilities:
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.
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.

Remotely accessing mySQL through SSH in Java

I'm trying to connect my local Java application to a remote mySQL server. I have shell access to the server and its mySQL, but no root access.
I attempted to implement some code I found online that seems to accomplish this goal. First I SSH into the server, and then I attempt to access the mySQL database. However, I get the following error:
Jul 09, 2014 2:20:06 PM [myClassName] connect
SEVERE: null, message from server: "Host '[remoteHost]' is not allowed to connect to this MySQL server"
I understand that mySQL by default disallows remote client access, but what I don't understand is that in this case it seems to be disallowing itself access to its own mySQL server. (i.e. ["remoteHost"] in the error message is the same host as the one that hosts the mySQL server I'm trying to access.)
The code template I'm using is below. I've left all the fields (user, pass, host, etc.) the same as on the template for the purposes of this question.
Do I need to ask my system administrator to give me special permissions? I have no trouble accessing the mySQL server through terminal. Thanks in advance everyone
Credit to The Kahimyang Project (http://kahimyang.info/kauswagan/code-blogs/1337/ssh-tunneling-with-java-a-database-connection-example).
import java.util.logging.Logger;
import com.jcraft.jsch.*;
import java.util.logging.Level;
public class MysqlManager {
// Logger
private final static Logger LOGGER =
Logger.getLogger(MysqlManager.class.getName());
public static void main(String args[]) {
MysqlManager mng = new MysqlManager ();
mng.connect();
}
public void connect() {
//
int assigned_port;
final int local_port=3309;
// Remote host and port
final int remote_port=3306;
final String remote_host="kahimyang.info";
try {
JSch jsch = new JSch();
// Create SSH session. Port 22 is your SSH port which
// is open in your firewall setup.
Session session = jsch.getSession("user", remote_host, 22);
session.setPassword("ssh_password");
// Additional SSH options. See your ssh_config manual for
// more options. Set options according to your requirements.
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
config.put("Compression", "yes");
config.put("ConnectionAttempts","2");
session.setConfig(config);
// Connect
session.connect();
// Create the tunnel through port forwarding.
// This is basically instructing jsch session to send
// data received from local_port in the local machine to
// remote_port of the remote_host
// assigned_port is the port assigned by jsch for use,
// it may not always be the same as
// local_port.
assigned_port = session.setPortForwardingL(local_port,
remote_host, remote_port);
} catch (JSchException e) {
LOGGER.log(Level.SEVERE, e.getMessage()); return;
}
if (assigned_port == 0) {
LOGGER.log(Level.SEVERE, "Port forwarding failed !");
return;
}
// Database access credintials. Make sure this user has
// "connect" access to this database;
// these may be initialized somewhere else in your code.
final String database_user="user";
final String database_password="password";
final String database = "database";
// Build the database connection URL.
StringBuilder url =
new StringBuilder("jdbc:mysql://localhost:");
// use assigned_port to establish database connection
url.append(assigned_port).append ("/").append(database).append ("?user=").
append(database_user).append ("&password=").
append (database_password);
try {
Class.forName(
"com.mysql.jdbc.Driver").newInstance();
java.sql.Connection connection =
java.sql.DriverManager.getConnection(url.toString());
java.sql.DatabaseMetaData metadata = connection.getMetaData();
// Get all the tables and views
String[] tableType = {"TABLE", "VIEW"};
java.sql.ResultSet tables = metadata.getTables(null, null, "%", tableType);
String tableName;
while (tables.next()) {
tableName = tables.getString(3);
// Get the columns from this table
java.sql.ResultSet columns =
metadata.getColumns(null, tableName, null, null);
String columnName;
int dataType;
while (columns.next()) {
columnName = columns.getString(4);
dataType = columns.getInt(5);
// Your actual task;
}
}
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException |
java.sql.SQLException e) {
LOGGER.log(Level.SEVERE, e.getMessage());
}
}
}
To figure out if your problem is Java-related or not, you could try to telnet to the SQL server.
$ telnet localhost 3306
If you are not allowed to connect, you will receive an error message similar to yours.
To allow access, your system administrator needs to run something like this:
$ mysql -u root -p
Enter password:
mysql> use mysql
mysql> GRANT ALL ON *.* to root#'localhost' IDENTIFIED BY 'your-root-password';
mysql> FLUSH PRIVILEGES;
About your concern (SQL server disallowing access from localhost): access should only allowed, if it is really necessary. So if you have only remote SQL clients, there is no need to allow access from the host localhost.

Categories