JSchException: UnknownHostKey - java

I'm trying to use Jsch to establish an SSH connection in Java.
I have set "StrictHostKeyChecking" to yes. I understand that the hostkey of the server has to be obtained before hand and store in the hostkey file
before the first attempt to connect to the server. How can I get the HostKey of the server. My code produces the following exception:
com.jcraft.jsch.JSchException: UnknownHostKey: ASY-PC
RSA key fingerprint is 22:fb:ee:fe:18:cd:aa:9a:9c:78:89:9f:b4:78:75:b4
How can I make connection with StrictHostKeyChecking Yes.
Here is my code.
package sshexample;
import com.jcraft.jsch.*;
import java.io.*;
public class SSHexample
{
public static void main(String[] args)
{
String user = "user";
String password = "password";
String host = "192.168.100.103";
int port=22;
try
{
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "yes");
System.out.println("Establishing Connection...");
session.connect();
System.out.println("Connection established.");
System.out.println("Crating SFTP Channel.");
ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
sftpChannel.connect();
}catch(Exception e) {
e.printStackTrace();
}
}

You have to supply a KnownHostKeys file by calling following function
jsch.setKnownHosts(new FileInputStream(knownHostsFile));
this file should have all the the known hosts' fingerprints separated by new lines.
for example
hostname,10.1.1.120, ssh-rsa AAAAC3NzaC1yc2EAAAADAQABAAABAQCi5b647581SwC0uUDQw1ENjKSz3rhJMRRZEgIjHylvF4fbuAEzj645YoAf9SItb51MhetFAJrq98jYsHpedSm3IoMG+aR/P1CjsBz1RtJKlfR2NfYDCZ7Dyx11P8FnJbwbYif/GeG0xEujekwF1pyL0tNPmf0H4/GPR4mwrv/llGlB3Lo3BzxrGtl4f4X/oSHDoo7FrQkDwqOfeSM++3vPPHxyVO5zhFJ5u9f7M/uuxUeHS+YS5JWAI7NLXKgbiM9dluGzZU/6Awo3ux4x5ojL+kf29JEVxK+o6GfW2bIW+LhgIGZNThnN5nHzBVfNNHvQ7KC5ic0h2z2gbVpwJr1h
you can obtain this key from server by using any sftp client however following command may help if you are using linux or unix
ssh-keyscan -t rsa 10.1.1.120

After a few minutes of testing i found a solution for this. If you don't want to use the default knownHost File, just create your own
This how the file could look:
192.168.0.1 ssh-rsa
AAAAC3NzaC1yc2EAAAADAQABAAABAQCi5b647581SwC0uUDQw1ENjKSz3rhJMRRZEgIjHylvF4fbuAEzj645YoAf9SI
tb51MhetFAJrq98jYsHpedSm3IoMG+aR/P1CjsBz1RtJKlfR2NfYDCZ7Dyx11P8FnJbwbYif
/GeG0xEujekwF1pyL0tNPmf0H4/GPR4mwrv/llGlB3Lo3BzxrGtl4f4X
/oSHDoo7FrQkDwqOfeSM++3vPPHxyVO5zhFJ5u9f7M/uuxUeHS+YS5JWAI7NLXKgbiM9dluGzZU
/6Awo3ux4x5ojL+kf29JEVxK+o6GfW2bIW+LhgIGZNThnN5nHzBVfNNHvQ7KC5ic0h2z2gbVpwJr1h
And all those entries are separated by new lines. You get the RSA key that you want by asking your session:
session=null;
com.jcraft.jsch.Channel channel =null;
try{
ssh=new JSch();
ssh.setKnownHosts("test");
session=ssh.getSession(userTextField.getText(),ip,22);
session.setPassword(passwordField1.getText());
System.out.println(session.getHostKey());
session.connect();
channel=session.openChannel("sftp");
channel.connect();
ChannelSftp sftp=(ChannelSftp) channel;
System.out.println(sftp.getHome());
for (Object o : sftp.ls(sftp.getHome())) {
System.out.println(((ChannelSftp.LsEntry)o).getFilename());
}
} catch (JSchException e1) {
e1.printStackTrace();
addHost(session.getHostKey().getKey());
} catch (SftpException e1) {
e1.printStackTrace();
}
}
private void addHost(String key){
try {
FileWriter tmpwriter=new FileWriter("test",true);
tmpwriter.append(ip + " ssh-rsa " + key+"\n");
System.out.println(ip + " ssh-rsa " + key);
tmpwriter.flush();
tmpwriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
So session.getHostKey().getKey() is what you want to call to get the key.
You also need to call session.connect(); before you ask for the key and handle it in the catch.

As most the of the answers suggest you have to provide know host file but fail to address how to get it. You simply need to SSH to the host.
Eg ssh user#192.168.100.103
when prompted provide password. For first time connection it will you to save the hosts ssh key fingerprint. Once you are connected you can find your known_host file at
user/.ssh/known_hosts
For me on windows path is C:\Users\athakur\.ssh\known_hosts. You can directly use this file. Or edit the file and pick up entry from it corresponding to your IP address which would look something like
192.168.100.103 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA1UsgiLH5hjIScZlqPA4kNhPoXAX00mMv65N8qTvYd1D1M5DwWtTTcxK4w0wGKKVA7RERzWbtjPpSomJwT1LofZr+qafLdiEvhirIXVYHSWZqp6zTJW0jzk3p07ugjoHV3YUWKDzOaiFuOMslt8hD7pZv8nhOYfxBZdsVHdukYRP8MADXC0ZgOD5ZYZ0EglaQJYPG7n73PSMZWZT/oafUcx6bFiRF4QsXaguWuu6umX9gaV7VvoyMJg+kxPAKnGDFY7If61AG7vAchUUhlQ44SB1FFr25y+qeUg2NGqAxH/Z/ZAfvZ+pDv3Cd9s+KCnEIqxyxY/sPQ2zCvwf0Z9fTeQ==
Note : Host machines SSH fingerprint (based on hosts public key that you can find at /etc/ssh/ssh_host_rsa_key.pub) may change in SSH is reinstalled in that machine. Or you may encounter MIM attack (even it is for testing sake). In such cases you will have to pick new entry in same way mentioned above.

Maybe it's no more relevant but in my case, the similar problem was happened with docker-compose.yml in container, that was build from spring-boot application that 100 % worked locally
config-service:
image: <host>/<bla>-config-service:<version>
hostname: config-service
ports:
- 3000:3000
depends_on:
- discovery
environment:
- CONSUL_HOST=discovery
- CONSUL_PORT=<port>
- CONFIG_GIT_URI=git#<host>:<group>/<repository>.git
- CONFIG_GIT_BRANCH=development
volumes:
- ~/.ssh/:/root/.ssh/:ro
Solution was to apply hack on ~/.ssh folder.
chcon -Rt svirt_sandbox_file_t ~/.ssh
After that I suppose volumes was correctly mapped between docker container and local machine, and described exception was gone.

Based on #nothing-to-know answer, the following method can be very handy:
public static String getHostKey(String hostName, int port, String userName, char[] password) {
JSch ssh;
Session session = null;
String hostKey = "";
try {
ssh = new JSch();
session = ssh.getSession(userName, hostName, port);
session.setPassword(new String(password));
if (session.getHostKey() != null) {
hostKey = session.getHostKey().getHost() + " " + session.getHostKey().getType() + " " + session.getHostKey().getKey();
}
session.connect();
} catch (JSchException e1) {
hostKey = session.getHostKey().getHost() + " " + session.getHostKey().getType() + " " + session.getHostKey().getKey();
} finally {
session.disconnect();
return hostKey;
}
}

Related

Transfer file and change user using Java to unix box

I want to transfer file from windows to unix(linux) box using Java, then change to super user. I tried using the JSCH library, that is SFTP with java.
I am stuck at the step to change to super user.
The steps i followed are similar to how to transfer a file through SFTP in java?
I know similar questions have been asked, but i am not able to change user using this approach.
Request to please do not mark this as duplicate.
Can anyone help me with the steps, or any other approach? An example would be helpful for the same. Thanks in advance.
Edit-1 - sample snippet from the link how to transfer a file through SFTP in java?, which is used as a reference posted
public void send (String fileName) {
String SFTPHOST = "host:IP";
int SFTPPORT = 22;
String SFTPUSER = "username";
String SFTPPASS = "password";
String SFTPWORKINGDIR = "file/to/transfer";
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
System.out.println("preparing the host information for sftp.");
try {
JSch jsch = new JSch();
session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
session.setPassword(SFTPPASS);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
System.out.println("Host connected.");
channel = session.openChannel("sftp");
channel.connect();
System.out.println("sftp channel opened and connected.");
channelSftp = (ChannelSftp) channel;
channelSftp.cd(SFTPWORKINGDIR);
File f = new File(fileName);
channelSftp.put(new FileInputStream(f), f.getName());
log.info("File transfered successfully to host.");
} catch (Exception ex) {
System.out.println("Exception found while tranfer the response.");
}
finally{
channelSftp.exit();
System.out.println("sftp Channel exited.");
channel.disconnect();
System.out.println("Channel disconnected.");
session.disconnect();
System.out.println("Host Session disconnected.");
}
}
With this, i am able to connect to the remote system, but cannot change the user.
Edit-2 : On doing some more research, i could find that using SFTP with JSCH api can help me transfer the file, but not change user. If i change the Channel to 'exec', i can change the user, but both do not work in same session. So both do not work simultaneously.
Is there any another way (SSH, SCP transfer perhaps?)
So the question remains unsolved - Want to transfer file and change user through Java.

How to forward remote port 3306 to local port in android programmatically with ssh and connect to mysql

I want to transfer data from an android SQLite database to a remote mysql database securely. As I can't setup a VPN programmatically I want to transfer the data through an ssh tunnel. I have tried with jkraft.jsch. The forwarding runs without errors, but when I connect with mysql-connector
with
conexionMySQL = DriverManager.getConnection("jdbc:mysql://localhost:53009/",user, password);
I get the following error:
java.sql.SQLException: Communication link failure: java.io.EOFException, underlying cause: null
** BEGIN NESTED EXCEPTION **
java.io.EOFException
STACKTRACE:
java.io.EOFException
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1395)
at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
at com.mysql.jdbc.Connection.createNewIO(Connection.java:1808)
at com.mysql.jdbc.Connection.<init>(Connection.java:452)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)
at java.sql.DriverManager.getConnection(DriverManager.java:179)
at java.sql.DriverManager.getConnection(DriverManager.java:213)
at de.damian.android.common.Mysql$2.run(Mysql.java:151)
at java.lang.Thread.run(Thread.java:841)
** END NESTED EXCEPTION **
The code is:
public int connectSSH(int rport)
{
//
int assigned_port;
final int local_port = 53009;
// Remote host and port
final int remote_port = rport;
final String remote_host = "test.test.org";
try
{
jsch = new JSch();
// Create SSH session. Port 22 is your SSH port which.
// is open in your firewall setup.
session = jsch.getSession("xxx", remote_host, 22);
session.setPassword("xxxxxx");
// 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)
{
Log.e(Level.SEVERE.toString(), e.getMessage(), e);
return 0;
}
if (assigned_port == 0)
{
Log.e(Level.SEVERE.toString(), "Port forwarding failed !");
return 0;
}
return assigned_port;
}
The answer is quite simple: setPortForwarding must use "localhost" as hostname as the remoteport is not open on the remote interface and you are already logged in in the remote host.
assigned_port = session.setPortForwardingL(local_port,
"localhost", remote_port);

jcraft with copSSH throws Algorithm negotiation fail error in windows environment

I'm developing java program to connect with windows server over ssh. For this I used jcraft on java. And the ssh server is copSSH. The implementation throws
Error: com.jcraft.jsch.JSchException: Algorithm negotiation fail
error on java. At the same time it shows
fatal: Unable to negotiate with 192.168.28.111: no matching cipher
found. Their offer: aes128-cbc,3des-cbc,blowfish-cbc [preauth]
on CopSSH.
Java code block
public void sshExecPassword(String host, String USERNAME, String PASSWORD, String command) {
App objApp = new App();
int port = 22;
try {
/**
* Create a new Jsch object This object will execute shell commands
* or scripts on server
*/
JSch jsch = new JSch();
/*
* Open a new session, with your username, host and port Set the
* password and call connect. session.connect() opens a new
* connection to remote SSH server. Once the connection is
* established, you can initiate a new channel. this channel is
* needed to connect to remotely execution program
*/
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
Session session = jsch.getSession(USERNAME, host, port);
session.setConfig(config);
session.setPassword(PASSWORD);
session.connect();
// create the excution channel over the session
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
// Gets an InputStream for this channel. All data arriving in as
// messages from the remote side can be read from this stream.
InputStream in = channelExec.getInputStream();
// Set the command that you want to execute
// In our case its the remote shell script
String str = command;
channelExec.setCommand(str);
channelExec.connect();
// Read the output from the input stream we set above
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// retrieve the exit status of the remote command corresponding to
// this channel
int exitStatus = channelExec.getExitStatus();
// Safely disconnect channel and disconnect session. If not done
// then it may cause resource leak
channelExec.disconnect();
session.disconnect();
if (exitStatus < 0) {
System.out.println("Done, but exit status not set! " + exitStatus);
objApp.writeLogs("120","Done, but exit status not set! ");
} else if (exitStatus > 0) {
System.out.println("Done, but with error!");
objApp.writeLogs("120","Done, but with error!");
} else {
System.out.println("Done!");
objApp.writeLogs("121","SSH connection successful");
}
} catch (Exception e) {
System.err.println("Error: " + e);
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
objApp.writeLogs("120", sw.getBuffer().toString());
}
}
And the CopSSH host following versions
OpenSSH_7.1p2, OpenSSL 1.0.2e 3 Dec 2015
Can any one suggest a fix for it?
That happens due to lacking support for legacy ciphers in more recent releases of OpenSSH. Check this Copssh FAQ for a solution. Background information can also be found here.
Latest jcraft jar fix the issue

Cannot connect to mysql server from java

I get the error "Communications link failure" at this line of code:
mySqlCon = DriverManager.getConnection("jdbc:mysql://**server ip address**:3306/db-name", "mu-user-name", "my-password");
I checked everything in this post:
I increased max-allowed-packet in my.cnf in etc/mysql: max_allowed_packet = 5073741824------ [mysqldump] max_allowed_packet = 1G
The bind-address is: 127.0.0.1
All timeout values are equal to a number
Tomcat is not yet installed on server (new server)
There is no skip-networking in my.cnf
I can ping the server
I am connected to the mysql database via ssh
When I change the query string to this:
mySqlCon = DriverManager.getConnection("jdbc:mysql://**server ip address**:22/127.0.0.1:3306/db-name", "mu-user-name", "my-password");
I get the error Packet for query is too large (4739923 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
While I have changed the packet size on my.cnf and restarted the mysql service after that.
Any suggestions?
NOTE:
I can connect through ssh with this code, but this way doesn't seem rational! I can connect once in main and then I should pass the connection to all the classes.
public my-class-constructor() {
try {
go();
} catch (Exception e) {
e.printStackTrace();
}
mySqlCon = null;
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://" + rhost + ":" + lport + "/";
String db = "my-db-name";
String dbUser = "dbuser";
String dbPasswd = "pass";
try {
Class.forName(driver);
mySqlCon = DriverManager.getConnection(url + db, dbUser, dbPasswd);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void go() {
String user = "ssh-user";
String password = "ssh-pass";
String host = "ips-address";
int port = 22;
try {
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
lport = 4321;
rhost = "localhost";
rport = 3306;
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
session.connect();
int assinged_port = session.setPortForwardingL(lport, rhost, rport);
System.out.println("localhost:" + assinged_port + " -> " + rhost
+ ":" + rport);
} catch (Exception e) {
System.err.print(e);
e.printStackTrace();
}
}
Take a look here .
Looks the following describes your case
The largest possible packet that can be transmitted to or from a MySQL
5.7 server or client is 1GB.
When a MySQL client or the mysqld server receives a packet bigger than
max_allowed_packet bytes, it issues an ER_NET_PACKET_TOO_LARGE error
and closes the connection. With some clients, you may also get a Lost
connection to MySQL server during query error if the communication
packet is too large.
Both the client and the server have their own max_allowed_packet
variable, so if you want to handle big packets, you must increase this
variable both in the client and in the server.
So, it looks like you need to change the max_allowed_packet on the client as well:
mySqlCon = DriverManager.getConnection("jdbc:mysql://**server ip address**:3306/db-name?max_allowed_packet= 5073741824", "mu-user-name", "my-password");
I changed the binding address in my.cnf file in /etc/mysql to the ip address of the server, and it solved the problem.

Java program skips over System.out.println() - maybe JSch related?

I'm running a test case using testng. This testcase uses Jsch to try to send a ssh command to a remote machine. The Jsch function is supposed to return output from command (including any exceptions). However, I can't view the output and the test passes, even when there's been an timeout exception!
Here's the JSch function:
public static String sshCommand (String username, String password, String host, int port, String command) {
StringBuilder outputBuffer = new StringBuilder();
try {
System.out.println("about to send command: " + command);
JSch jschSSHChannel = new JSch();
Session session=jschSSHChannel.getSession(username, host, port);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(password);
session.connect();
Channel channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
InputStream commandOutput = channel.getInputStream();
channel.connect();
int readByte = commandOutput.read();
while(readByte != 0xffffffff)
{
outputBuffer.append((char)readByte);
readByte = commandOutput.read();
}
channel.disconnect();
session.disconnect();
} catch (Exception e) {
System.out.println("Exception encountered returning: " + e.toString());
return e.toString();
}
return outputBuffer.toString();
}
Here's the code that evaluates the output from the ssh function:
String output = jschClass.sshCommand("username", "password", ipAddress, 22, command);
System.out.println("Command output: " + output);
Assert.assertTrue(!output.contains("Exception"));
System.out.println("Test complete");
The problem is I never see the System.out.println("Command output: " + output); printed. It skips right over that statement and executes System.out.println("Test complete"); before exiting.
Additionally, the catch statement I put in the sshcommand function never seems to execute. An exception is displayed, but it doesn't seem to be executing the code I wrote. In fact it's almost like the exception is coming from somewhere else. Here's what I see in the program's output:
about to send command: ping -c 5 google.com
com.jcraft.jsch.JSchException: java.net.ConnectException: Connection refused: connect
Test complete
at com.jcraft.jsch.Util.createSocket(Util.java:349)
at com.jcraft.jsch.Session.connect(Session.java:215)
at com.jcraft.jsch.Session.connect(Session.java:183)
at com.cisco.ui2.openstack.jsch.JschHelper.sshCommand(JschHelper.java:28)
at com.cisco.ui2.openstack.PingPublicSite.createImageAndPingPublicSite(PingPublicSite.java:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[removed for space]
at com.jcraft.jsch.Util.createSocket(Util.java:343)
... 28 more
Any idea what's going on?

Categories