How to use SftpFileSystemConfigBuilder.getInstance().setIdentityProvider() - java

I use Apache vfs2 to connect to SFTP server, but the setIdentities() method is deprecated, so I would like to change to use setIdentities() method, but i got the NullPointerException, how to change my code to fix it? thank you.
public SFtpUtil(String hostName, String portNo, String userId, String userPwd) {
sftpUri = getSftpUri(hostName, portNo, userId, userPwd);
log.info("sftpUri=" + sftpUri);
FileSystemOptions opts = new FileSystemOptions();
try {
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
SftpFileSystemConfigBuilder.getInstance().setConnectTimeout(opts, Duration.ofSeconds(10000));
// User setIdentities() method, it works, but deprecated
// SftpFileSystemConfigBuilder.getInstance().setIdentities(opts, new File[0]);
// I follow the suggestion to use setIdentityProvider()
SftpFileSystemConfigBuilder.getInstance().setIdentityProvider(opts, new IdentityInfo(null));
log.info("setting SftpFileSystemConfigBuilder finished.");
INFO | setting SftpFileSystemConfigBuilder finished.
INFO | create manager
INFO | manager init.
org.apache.commons.vfs2.FileSystemException: Could not connect to SFTP server at "sftp://xxxxxxxxxxxxx...../".
at org.apache.commons.vfs2.provider.sftp.SftpFileProvider.createSession(SftpFileProvider.java:72)
at org.apache.commons.vfs2.provider.sftp.SftpFileProvider.doCreateFileSystem(SftpFileProvider.java:92)
at org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider.getFileSystem(AbstractOriginatingFileProvider.java:92)
at org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider.findFile(AbstractOriginatingFileProvider.java:71)
at org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider.findFile(AbstractOriginatingFileProvider.java:55)
at org.apache.commons.vfs2.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:788)
at org.apache.commons.vfs2.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:835)
at lab.tmp.RunSftp.main(RunSftp.java:19)
Caused by: java.lang.NullPointerException
at com.jcraft.jsch.Util.checkTilde(Util.java:489)
at com.jcraft.jsch.Util.fromFile(Util.java:506)
at com.jcraft.jsch.KeyPair.load(KeyPair.java:540)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:423)
at org.apache.commons.vfs2.provider.sftp.IdentityInfo.addIdentity(IdentityInfo.java:87)
at org.apache.commons.vfs2.provider.sftp.SftpClientFactory.addIdentity(SftpClientFactory.java:117)
at org.apache.commons.vfs2.provider.sftp.SftpClientFactory.addIdentities(SftpClientFactory.java:104)
at org.apache.commons.vfs2.provider.sftp.SftpClientFactory.createConnection(SftpClientFactory.java:154)
at org.apache.commons.vfs2.provider.sftp.SftpFileProvider.createSession(SftpFileProvider.java:65)
... 8 more
someone can help me to fix or tell me how to change my code.

Related

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.

XMPPError: bad-request - modify error create new user using smack library 4.1.8 and openfire

i have been developing a chat application using smack client library 4.1.8 and xmpp server(openfire server) but while trying to create new user using Accountmanger class it raises and exception "XMPPError: bad-request - modify"
XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setHost("127.0.0.1")
.setDebuggerEnabled(true)
.setPort(5222)
.setSendPresence(true)
.setServiceName("127.0.0.1")
.build();
AbstractXMPPConnection conn2 = new XMPPTCPConnection(conf);
//conn2.login();
conn2.connect();
AccountManager accountManager = AccountManager.getInstance(conn2);
if (accountManager.supportsAccountCreation()) {
accountManager.sensitiveOperationOverInsecureConnection(true);
accountManager.createAccount("qidus", "123456");
conn2.login("qidus", "123456");
}
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
}
and the exception is this
04:01:41 AM SENT (0): <iq to='127.0.0.1' id='aYej1-3' type='get'><query xmlns='jabber:iq:register'></query></iq>
04:01:41 AM RECV (0):
org.jivesoftware.smack.XMPPException$XMPPErrorException: XMPPError: bad-request - modify
at org.jivesoftware.smack.XMPPException$XMPPErrorException.ifHasErrorThenThrow(XMPPException.java:135)
at org.jivesoftware.smack.PacketCollector.nextResultOrThrow(PacketCollector.java:232)
at org.jivesoftware.smack.PacketCollector.nextResultOrThrow(PacketCollector.java:213)
at org.jivesoftware.smackx.iqregister.AccountManager.getRegistrationInfo(AccountManager.java:332)
at org.jivesoftware.smackx.iqregister.AccountManager.supportsAccountCreation(AccountManager.java:144)
at chat.Chat$1.run(Chat.java:46)
You need to set properly Service Name. You can chek your serviceName in Openfire through admin panel (127.0.0.1:9090) it's in first page in middle of the page, look for "Server Name" after login.
By default it's your machine name.
However your code will run just once: 2nd time AccountManger will throw an exception due to already registered user.
You also get this error or exception when the username which you are passing for account creation has the null value.So check your username also.

JSch SSH connection throws NPE when initializing reverse tunnel

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

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.

Using Keys with JGit to Access a Git Repository Securely

I'm using JGit to access a remote Git repo, and I need to use SSH for it. JGit uses JSch to provide secure access. However, I'm not sure how to set the key file and the knows hosts file for JGit. What I have tried is as follows.
Created a custom configuration of the SshSessionFactory, using by subclassing JSchConfigSessionFactory:
public class CustomJschConfigSessionFactory extends JschConfigSessionFactory {
#Override
protected void configure(OpenSshConfig.Host host, Session session) {
session.setConfig("StrictHostKeyChecking", "yes");
}
}
In the class which I access the remote Git repo, did the following:
CustomJschConfigSessionFactory jschConfigSessionFactory = new CustomJschConfigSessionFactory();
JSch jsch = new JSch();
try {
jsch.addIdentity(".ssh/id_rsa");
jsch.setKnownHosts(".ssh/known_hosts");
} catch (JSchException e) {
e.printStackTrace();
}
SshSessionFactory.setInstance(jschConfigSessionFactory);
I can't figure out how to associate this JSch object with JGit so that it can successfully connect to the remote repository. When I try to clone it with JGit, I get the following exception:
org.eclipse.jgit.api.errors.TransportException: git#git.test.com:abc.org/test_repo.git: reject HostKey: git.test.com
at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:137)
at org.eclipse.jgit.api.CloneCommand.fetch(CloneCommand.java:178)
at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:125)
at GitTest.cloneRepo(GitTest.java:109)
at GitTest.main(GitTest.java:223)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.eclipse.jgit.errors.TransportException: git#git.test.com:abc.org/test_repo.git: reject HostKey: git.test.com
at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:142)
at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:121)
at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:248)
at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:147)
at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:136)
at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:122)
at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1104)
at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:128)
... 9 more
Caused by: com.jcraft.jsch.JSchException: reject HostKey: git.test.com
at com.jcraft.jsch.Session.checkHost(Session.java:748)
at com.jcraft.jsch.Session.connect(Session.java:321)
at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:116)
... 16 more
I have added the git.test.com entry to my /etc/hosts file. I have used the same code to access a git repo with a http url, so the code it working fine. It's the key handling part that is failing. Any idea on how to handle this?
You need to override the getJSch method in your custom factory class:
class CustomConfigSessionFactory extends JschConfigSessionFactory
{
#Override
protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
jsch.addIdentity( "/path/to/private/key" );
return jsch;
}
}
Calling jsch.removeAllIdentity is important; it doesn't seem to work without it.
A caveat: I wrote the above in Scala, and then translated it over to Java, so it might not be quite right. The original Scala is as follows:
class CustomConfigSessionFactory extends JschConfigSessionFactory
{
override protected def getJSch( hc : OpenSshConfig.Host, fs : FS ) : JSch =
{
val jsch = super.getJSch(hc, fs)
jsch.removeAllIdentity()
jsch.addIdentity( "/path/to/private/key" )
jsch
}
}
Jsch sesems to not like a known_hosts file in the hashed format-- it must conform to the format produced by:
ssh-keyscan -t rsa hostname >> ~/.ssh/known_hosts
e.g.
<hostname> ssh-rsa <longstring/longstring>
not:
|1|<hashed hostname>= ecdsa-sha2-nistp256 <hashed fingerprint>=
Managed to find the issue. The public key in the server side had a different name other than the usual id_rsa.pub, while the private key on my side was id_rsa. JSch expects by default the public key to have the same name as the private key plus the .pub suffix. Using a key pair with a common name (ex.: private = key_1 and public = key_1.pub) solves the issue.

Categories