Secure FTP using private key authentication in java - java

import com.jcraft.jsch.*;
import com.jcraft.jsch.JSchException;
import oracle.jdbc.driver.OracleDriver;
import java.io.*;
import java.util.*;
import java.sql.*;
import java.net.*;
public class SecureFTP {
public static void main(String[] args) throws IOException , ClassNotFoundException, JSchException, SftpException{
JSch jsch = new JSch();
File file = new File("/home/xxxxx/.ssh/id_rsa");
Session session = null;
URL keyFileURL = null;
URI keyFileURI = null;
if (file.exists())
{
keyFileURL = file.toURL();
if (keyFileURL == null)
{
System.out.println("what");
throw new RuntimeException("Key file not found in classpath");
}
}
else System.out.println("FIle not found");
try{
keyFileURI = keyFileURL.toURI();
}
catch(Exception URISyntaxException)
{
System.out.println("Wrong URL");
}
String privateKey = ".ssh/id_rsa";
//jsch.addIdentity(privateKey);
jsch.addIdentity(new File(keyFileURI).getAbsolutePath());
System.out.println(new File(keyFileURI).getAbsolutePath() + " LOL");
session = jsch.getSession("username", "servername");
//session.setPassword("password");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
// connect
session.connect();
// get SFTP channel
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp schannel = (ChannelSftp) channel;
FileInputStream fis = new FileInputStream(sourcefile);
schannel.put(fis, destinationfile );
fis.close();
}
schannel.exit();
session.disconnect();
}
}
As you can see from the code I have commented out, I have tried everything possible to get this working and the only thing that works is if I set the password directly. I am trying to use the RSA private key generated, but I keep getting an auth fail.
I have added the public key to the list of authorized keys on the target server. And there is no passphrase.
Is there something else I am supposed to do? Like say, while generating the keys? Is there a step I am missing?
Is there another library I can use to implement the same function?

Make sure the necessary files exist (id_rsa and id_rsa.pub on the client, authorized_keys on the server). Make sure you can use public key authentication with another tool, like ssh, using these files.
If that looks alright, the problem may be with your Java security provider. Read on if you think you have the right files in place.
There are different formats for RSA private key storage, and SSH uses one that is not standard. Most providers expect something called a CRT RSA key, and when JSch doesn't give them a key in that format, they raise an exception which JSch silently eats and goes on to the next authentication method.
What is your provider? The following snippet will help you find out:
import java.security.KeyFactory;
…
KeyFactory f = KeyFactory.getInstance("RSA");
System.out.println(f.getProvider().getName());
Update: I did some checking around, and as of Java 5, the SunPKCS11 provider is installed with the highest precedence on Solaris systems, for performance. Since I don't run Solaris, I can't test it, but I believe this may be causing the problem.
JSch doesn't allow you to specify the provider to use for this operation through its API, so you will have to change the precedence of the installed providers. In fact, I'd suggest trying to remove the SunPKCS11 from this application; run this code once when your application starts up:
Security.removeProvider("SunPKCS11-Solaris");

Have you have copied the key into the file $HOME/.ssh/authorized_keys on the target server? If so, you should probably mention that. If not, that is required for this to work. Also, are you generating the key without a password? If the private key is password protected, you will need to provide that password to addIdentity.
After verifying those things, I'd recommend trying to connect via the command line using OpenSSH, as the Java code you have here looks correct. If the command line does not work, invoke it with -vvv to get verbose output about what it is doing. It is possible that the server is configured with PubkeyAuthentication set to no.

Related

How to get server's SSH version without authenticating in JSch?

I tried to use JSch:
#Test
public void test() throws Exception {
var session = new JSch().getSession("host");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
var version = session.getServerVersion();
System.out.println("version = " + version);
}
but I'm getting
java.lang.NullPointerException: Cannot read the array length because "str" is null
It seems that this implementation requires you to connect and authenticate.
How can I get this information with Java, without the need to authenticate?
This Python code works for me on a server that requires authentication yet I didn't have to authenticate in order to get the remote version.
import paramiko
ssh = paramiko.Transport(("host", 22))
ssh.start_client()
print(ssh.remote_version)
ssh.close()
The SSH identification string is sent in plain text right after opening the connection.
So you do not need an SSH library to obtain the string.
You can do with a simple code like shown here:
Read Data from a Java Socket
Or just try to login using dummy credentials with JSch and then read Session.getServerVersion.
You do not even have to try to login. You can abort the connection right at the host key check:
config.put("StrictHostKeyChecking", "yes");

Calling a .jar from ColdFusion and run on the client machine

I am using ColdFusion 11 and I have an eSignWeb.jar file (created in java obviously) in my class path. What this .jar does is to receive a path where a file is located on the client's computer and then analyze that file using Bouncy Castle.
But when I run it, I get the following error:
Code: java.io.FileNotFoundException:
C:\Users\name\Documents\clientfile.cer (The system can not find the
specified path)
The error is because my .jar file tries to look for the clientfile.cer file on the server and not the client machine.What I need is that this .jar running on the server can search this file on the client's computer.
Important:
The clientfile.cer file can never upload to the server, so it has to be analyzed on the client side.
I have to use ColdFusion with java
My code in ColdFusion is simple:
<cfset eSign = createObject("java","eSignWeb.SignAB") />
<cfoutput>
<!--- TEST1--->
<cfdump var="#eSign#">
#eSign.callsignCF()#
<hr />
<!--- TEST2 WITH cfscript --->
<cfscript>
classLoader = createObject("java","eSignWeb.SignAB");
response = classLoader.callsignCF();
WriteOutput("Test whith CFSCRIPT " & response);
</cfscript>
</cfoutput>
The code in my .jar file:
package eSignWeb;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class SignAB {
public String callsignCF(){
String vCer;
vCer = "C:\\Users\\name\\Documents\\clientfile.cer"; //Path where the .cer file is located on the client side
Security.addProvider(new BouncyCastleProvider()); // add bc as provider
String archivoCer;
X509Certificate certificado = null;
Date fechaFinal;
Date fechaInicial;
Date fechaActual;
//start cer
archivoCer = vCer;
try {
FileInputStream is = new FileInputStream(archivoCer);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificado = (X509Certificate)cf.generateCertificate(is);
fechaFinal = certificado.getNotAfter();
fechaInicial = certificado.getNotBefore();
fechaActual = new Date();
if(fechaActual.before(fechaInicial) && fechaActual.after(fechaFinal)){
return "Invalid date";
}else{
String myVarConRFC = "2.5.4.45";
String dn = certificado.getSubjectX500Principal().getName();
LdapName ldapDN = new LdapName(dn);
ldapDN.getRdns().forEach((index) -> {
if(index.getType().equals(myVarConRFC)){
//RFC
String myRFC = new String((byte[]) index.getValue());
}
});
}
}
catch (InvalidNameException ex) {
return "E0: Error on cer. Code: "+ ex;
}
catch(CertificateException a) {
return "E1: The specified file is not a valid certificate file. Code: " +a;
}
}
catch(FileNotFoundException fnfe) {
return "E2: Certificate file does not exist. Code: " +fnfe;
}
// End cer
return "Unknown error";
}
}
As you can see, the .jar runs on the server and that's why it does not find the .cer file. Any idea of ​​how to get a functionality that allows .jar to search the file on the client's machine?
If I could upload the .cer file to the server, it would be very easy, but that is forbidden. The .cer file should never leave the client's machine.
Thank you very much for any guidance in this regard!
CF alone won't work for that type of task, because it only operates on the CF server. It can't access client files at all. Only files uploaded to the server or a location accessible to the server (which you said you can't do).
In order to access files on a client machine, you need something like a signed Java Applet or Web Start Application, which does run on the client. The "signed" part is important. For security reasons, applets and the like can't just access files on the client machine whenever they wish. The user must explicitly grant the Applet or Web Start application permission to do so.

how we make Riak KV Cluster with security implementation using java client

Can anybody tell how we interact with security enables Riak KV Cluster using java client.
I tried with following java code to interact with security enabled riak cluster, but getting SSLEngine Problem, Below is the java code ......
InputStream inputStream = null;
KeyStore ks = null;
try {
inputStream = new FileInputStream("/etc/ssl/certs/rootCA.pem");
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate) certFactory.generateCertificate(inputStream);
inputStream.close();
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, "password".toCharArray());
ks.setCertificateEntry("cacert", caCert);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (RiakConfig.class) {
List<RiakNode> riakNodeList = new ArrayList<RiakNode>();
for (final String riakServer : riakServerArray) {
RiakNode node = new RiakNode.Builder()
.withMinConnections(10)
.withMinConnections(50)
.withRemoteAddress(riakServer.split(":")[0])
.withRemotePort(Integer.parseInt(riakServer.split(":")[1]))
.withAuth("riakuser", "riakuser", ks)
.build();
riakNodeList.add(node);
}
cluster = new RiakCluster.Builder(riakNodeList).build();
cluster.start();
}
suggenst anyone how we do that???
getting SSLEngine Problem
Please, always provide the specific problem you are getting. "getting SSLEngine Problem" is a useless bit of information.
Based on this document it appears you are using the correct steps. Without more information about the security issue you are getting, further help is impossible. Most likely you do not have your certificates set up correctly. The RabbitMQ documentation includes a comprehensive TLS/SSL troubleshooting guide that can help you determine if your certificates were created correctly.
Additionally, I suggest that you review how the Riak Java Client sets up certificates and then uses them.
This part of the Makefile is where certs are imported with keytool:
https://github.com/basho/riak-java-client/blob/develop/Makefile#L43-L62
This is a class that uses the key store to create connections for use in tests:
https://github.com/basho/riak-java-client/blob/develop/src/test/java/com/basho/riak/client/core/operations/itest/RiakJKSConnection.java
Achually this is working fine for single node, in cluster i am facing SSL Engine Problem.

Why is Jsch asking for Kerberos username? [duplicate]

So I'm writing a little program that needs to connect to a remote server through SFTP, pull down a file, and then processes the file. I came across JSch through some answers here and it looked perfect for the task. So far, easy to use and I've got it working, with one minor thing I'd like to fix. I'm using the following code to connect and pull the file down:
JSch jsch = new JSch();
Session session = null;
try {
session = jsch.getSession("username", "127.0.0.1", 22);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword("password");
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp) channel;
sftpChannel.cd(REMOTE_FTP_DIR);
sftpChannel.lcd(INCOMING_DIR);
sftpChannel.get(TMP_FILE, TMP_FILE);
sftpChannel.exit();
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
}
So this works and I get the file. I'm running this code on a linux server and when I run the code JSch asks me for my Kerberos username and password. It looks like:
Kerberos username [george]:
Kerberos password for george:
I just hit enter for both questions and then the program seems to continue on with no problems. However I need this code to be automated through a cron task and so I'd rather not having it pausing the program to ask me these two questions. Is there something I'm not supplying it so that it won't ask this? Something I need to do to stop it asking? Hopefully someone has some ideas. Thanks.
Thought I'd post an answer here since in case anyone else ends up running into a similar issue. Turns out I am missing a piece of code that makes all the difference. I just needed to add
session.setConfig("PreferredAuthentications",
"publickey,keyboard-interactive,password");
before
session.connect();
and everything works perfectly now.
While the solution in the self-accepted answer is correct, it lacks any explanation.
The problem is that the OP have a Kerberos/GSSAPI authentication set as the preferred (the JSch default). Yet OP does not seem to actually use/want it, as OP claims not to specify any username or password for the Kerberos prompts.
This problem can appear spontaneously, when either Kerberos gets installed on the the client PC or the server starts to support Kerberos.
The solution is to remove the Kerberos/GSSAPI (gssapi-with-mic) from the list of preferred authentication methods in JSch:
session.setConfig(
"PreferredAuthentications", "publickey,keyboard-interactive,password");
All answers are correct, I'll just add here the way it can be done for Spring Integration when trying to integrate with an SFTP server.
So, if you are using SFTP Spring Integration and the weird user and password for Kerberos is prompting in the same way the OP is asking.
Then modify your Spring configuration (I'm using Java Spring Integration config, if you are using XML config you can try to translate it yourself - I really don't like XML config :P ):
So in the bean you are using as SessionFactory you need to add this change in config:
#Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost("hostname");
factory.setPort(22);
factory.setUser("username");
factory.setPassword("superstrongpassword");
factory.setAllowUnknownKeys(true);
factory.setSessionConfig(buildSessionProperties());
return new CachingSessionFactory<>(factory);
}
/**
* Build JSch property PreferredAuthentications without "gssapi-with-mic"
* This way it won't prompt for Kerberos authentication every time it tries to connect
* to the SFTP.
*/
private Properties buildSessionProperties() {
Properties sessionProperties = new Properties();
sessionProperties.setProperty("PreferredAuthentications", "publickey,keyboard-interactive,password");
return sessionProperties;
}

Android SSHJ exception upon connect() - "KeyFactory ECDSA implementation not found"

I'm trying to open an SSH client session from my Android app. Trying to connect to a device on the local network (a Raspberry Pi). I'm using the SSHJ library version 0.10.0. It fails on the ssh.connect() call, with a TransportException which is ultimately caused by a NoSuchAlgorithmException. Refer exception tree below.
SSHClient ssh = new SSHClient(new AndroidConfig());
Session session = null;
try {
//ssh.loadKnownHosts();
// Exception thrown on this line
ssh.connect("192.168.1.109", 22);
// Doesn't reach below
ssh.authPassword("user", "password");
session = ssh.startSession();
}
catch (net.schmizz.sshj.transport.TransportException ex) {
;
}
Exception tree:
net.schmizz.sshj.transport.TransportException
net.schmizz.sshj.common.SSHException
net.schmizz.sshj.common.SSHRuntimeException
java.security.GeneralSecurityException: java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found
java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found
Other system info:
SSHJ library : v0.10.0
Android device : Galaxy Note 3 running Android 4.4.2
I used the maven dependency support in Android Studio to bring in the SSHJ JAR and it pulled in the following three libraries in addition to the SSHJ v0.10.0 jar...
bouncy castle...
bcpkix-jdk15on-1.50.jar
bcprov-jdk15on-1.50.jar
logging....
slf4j-api-1.7.7.jar
Don't have a clue where to start with this exception ... any suggestions appreciated! Thanks.
UPDATE: 31-Oct-2014
As suggested by LeeDavidPainter, I included the SpongyCastle 1.51.0 JAR and added this line at the top:
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
I'm now getting a different exception on the same line:
net.schmizz.sshj.transport.TransportException
net.schmizz.sshj.common.SSHException
net.schmizz.sshj.common.SSHRuntimeException
java.security.GeneralSecurityException: java.security.spec.InvalidKeySpecException: key spec not recognised
java.security.spec.InvalidKeySpecException: key spec not recognised
Also note I tried the following line as well, with the same result:
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
I have another app on my phone which is basically doing exactly what I want to achieve - its called RaspberryPiController - it connects to your RPi over SSH with username and password auth. This works fine, so it would seem its not a network issue.
Android ships with a cut down version of BouncyCastle which does not include the ECDSA algorithms. So even though you include the full version in your class path, the Android runtime version will be picked up and used.
You may want to look at http://rtyley.github.io/spongycastle/ which was created to get around this, its a repackaged version of Bouncycastle that can be installed as a separate JCE provider in Android. Just install it as the default JCE provider before you try to connect with SSHJ (untested).
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
First add this BouncyCastle library in app/build.gradle file:
implementation 'org.bouncycastle:bcpkix-jdk15on:1.64'
Then in your activity file, add a static block to remove the default BouncyCastle provider found in Android with our version:
static {
Security.removeProvider("BC");//first remove default os provider
Security.insertProviderAt(new BouncyCastleProvider(), 1);//add new provider
}
This will resolve the algorithm implementation not found issue.
Downgrade to sshj 0.9.0 here: http://mvnrepository.com/artifact/net.schmizz/sshj/0.9.0
The problem seems to have been introduced in 0.10.x. Also, I have tried the other JCE provider but got into the same trouble.
Jsch most likely worked because it does not support the Elliptic Curve algorithms for SSH AFAIK. If you don't need Elliptic Curve algorithms then that's your answer.
Couldn't get anywhere with this issue in SSHJ, so decided to give JSch a try which offers the same functionality. Its available as a maven repo as well - I used jsch version 0.1.51 ('com.jcraft:jsch:0.1.51').
It worked first time with this code fragment;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import java.io.ByteArrayOutputStream;
import java.util.Properties;
JSch jsch = new JSch();
com.jcraft.jsch.Session session = null;
String result = "";
try {
session = jsch.getSession("user", "192.168.1.109", 22);
session.setPassword("password");
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
// SSH Channel
ChannelExec channel = (ChannelExec)session.openChannel("exec");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
channel.setOutputStream(stream);
// Execute command
channel.setCommand("ls -ltr");
channel.connect(1000);
java.lang.Thread.sleep(500); // this kludge seemed to be required.
channel.disconnect();
result = stream.toString();
}
catch (JSchException ex) {
String s = ex.toString();
System.out.println(s);
}
catch (InterruptedException ex) {
String s = ex.toString();
System.out.println(s);
}
finally {
if (session != null)
session.disconnect();
}
It feels like a more robust implementation when using it compared to SSHJ - or this impression might be caused by them selecting quite conservative timeouts. For example, if the target device is switched off, the session.connect() call will, by default, keep trying to connect for something like 20 seconds before giving up.
Based off of LeeDavidPainter's solution,
/**
* Creates a new SSH client stub
*/
public SSH(final String host, final int port)
{
SecurityUtils.setSecurityProvider(SecurityUtils.BOUNCY_CASTLE); //<-- Here
Security.insertProviderAt(new BouncyCastleProvider(), 1); //<-- Here
this.ssh.addHostKeyVerifier(new PromiscuousVerifier());
this.shell = new SSHShellSession();
this.ssh = new SSHClient();
this.connected = false;
this.initiated = false;
this.host = host;
this.port = port;
}
The two commented areas above //<-- here are the solution.

Categories

Resources