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.
Related
I have a code which calls a rest api protected with certificate, and that code was working for some time without issues, until I migrate application from Wildfly 10 to Wildfly 16.
The code itself is straight forward, it creates http request and set custom socket factory: `
private SSLSocketFactory getSSLSocketFactory() {
char[] certPassword = {}; // password hidden from you
try {
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final SSLContext sslContext = SSLContext.getInstance("TLS");
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fileInputStream = new FileInputStream(new File("cert.pk12"))) {
keyStore.load(fileInputStream, certPassword);
} catch (final Exception e) {
logger.error("....", e);
}
this.readLoadCertificateFile(keyStore);
kmf.init(keyStore, certPassword);
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{new AnyTrust()}, null);
return sslContext.getSocketFactory();
} catch (Exception e) {
logger.error(".....", e);
}
throw new IllegalStateException("....");
}
HTTPRequest req = ....
req.setSSLSocketFactory(getSSLSocketFactory());
tokenHttpResp = req.send();`
`
All seems good but when I run this code from within WF16 it throws
IOException: Failed to load .p12 keystore:C:\Cert\cert.p12; error
constructing MAC: java.lang.SecurityException: JCE cannot authenticate
the provider BC;
org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.engineLoad::-1
java.security.KeyStore.load in KeyStore.java::1445
I checked for the bouncy castle lib, and I don't have it in the application itself...
If any one knows what the issue could be I would appreciate any input.
Alternatively, I'm looking to move this socket factory creation in to the container itself, seems like Wildfly Elytron subsystem specifically designed for this, is it a good idea?
Answering my own question.
The error message like "JCE cannot authenticate the provider BC" indicating that the jar file, the Security Provider is loaded from, cannot be verified by JVM. Either the jar is not signed or signature cannot be verified. In my case, the newer Wildfly version has a newer version of bouncy castle library, which for some reason, cannot be verified by Java 8. Interesting enough, that it is fine with Java 10. Some people on the Internet says that this issue only occurs in Oracle's JVM and does not exist for Open JDK, I haven't tested it, just think it worth to mention.
To overcome issue you need to tell JVM to trust the security provider, for that, make sure that the Security Provider you want to use/JVM decided to use, mentioned in jre/lib/security/java.security file, it should have line like:
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
Then copy Jars with Security Provider into /jre/lib/ext folder
I'm trying to test my SSL implementation in unit tests and have one scenario I can't quite understand.
When I connect to a host once and fail, every following connection will fail also, even if it has the correct certificates. I'm assuming somewhere along the way I'd have to flush a cache.
Here is my code, server and client are running locally. I use one jks-File for both trustStore and keyStore. The error occurrs no matter what the initial error was, I'll always get the first error the next time.
If I don't perform the first request the second one works.
If you're wondering what the use case is here, we have some local servers that use https certificates from an internal PKI, when someone misconfigures a server or a certificate, we'd like to be able to change them obviously, without shutting down the whole VM.
//attempt a connection without certificates, will fail
try (final InputStream stream = new URL("https://localhost:" + port).openStream()){
System.out.println(IOUtils.toString(stream, Charset.defaultCharset()));
} catch (IOException e){
System.out.println("Failed to load: " + StackTraceUtil.getStackTrace(e));
}
//copies the jks file to a temporary location
final File jksFile = copyJKSFile();
//ignore host names, running locally, won't use this in production
HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> hostname.equalsIgnoreCase("localhost"));
//set the system properties
System.setProperty("javax.net.ssl.keyStore", jksFile.getAbsolutePath());
System.setProperty("javax.net.ssl.keyStorePassword", password);
System.setProperty("javax.net.ssl.trustStore", jksFile.getAbsolutePath());
System.setProperty("javax.net.ssl.trustStorePassword", password);
//this should work now
try (final InputStream stream = new URL("https://localhost:" + port).openStream()){
System.out.println(IOUtils.toString(stream, Charset.defaultCharset()));
} catch (IOException e){
System.out.println("Failed to load: " + StackTraceUtil.getStackTrace(e));
}
Thanks for any help!
So I found a solution and I thought I'd share it in case someone else would have this problem at some point.
The class javax.net.ssl.HttpsURLConnection uses a javax.net.ssl.SSLSocketFactory to load the Key- and TrustStore, which uses a javax.net.ssl.SSLContext internally. When you don't overwrite anything, it uses the default implementation, which loads the files and can't be reset once loaded.
So what I did was not to use the default implementation, but to set my own SSLContext, when I knew the files would change.
final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
If you want to use older versions of TLS, the full list should be here
I'm trying to consume an IBM MQ (version 8.0.0.8) with SSL using a Java client built basically with:
Oracle JKD 8 and IBM JRE 7 (for testing purposes I have one client for each of them)
com.ibm.mq.allclient-9.1.0.0.jar
javax.jms-api-2.0.1.jarspring-jms-4.3.7.RELEASE.jar
spring-jms-4.3.7.RELEASE.jar
The MQ is a Request/Reply type.
I have the correct certificate and all MQ properties set, but for some reason the connection “drops” and I get no errors on my client side and my requests never get any response and keeps running “forever”, never getting any response. The only clue that I have is an error message in the MQ log that says:
Process(31600.16) User(QMQM) Jobname(JOB_NAME)
Host(HOST_NAME)
VRMF(8.0.0.8) QMgr(MANAGER_NAME)
AMQ9638: SSL communications error for channel 'CHANNEL_NAME'. EXPLANATION:
Cause . . . . . : An unexpected SSL communications error occurred for a channel, as reported in the preceding messages. The
channel is 'CHANNEL_NAME';
The strange thing is that the SSL Handshake happens, my certificate is accepted by the MQ, but for some reason something happens after this. I’m trying using both Oracle JRE 8 and IBM JRE 7. Maybe is something on MQ side (IBM MQ v8.0.0.8) or some configuration that I’m missing on my side.
I have already installed the JCE Unlimited Policies, so the problem is not CipherSpec X CipherSuite.
I'm using -Djavax.net.debug=all and I can see that my certificate is being used correctly and I can't see any problems there...
My contact point on the MQ Team told me that for some reason my application is revoking the certificate (something related with CLR), but I have no idea why this would happen.
My Java code:
public Message callMQ() {
Message message = null;
try {
MQConnectionFactory factory = mqQueueConnectionFactory();
JMSContext context = factory.createContext();
Destination requestQueue = context.createQueue("queue:///REQUEST_QUEUE");
Destination replyQueue = context.createQueue("queue:///REPLY_QUEUE");
JmsTemplate jmsTemplate = new JmsTemplate(factory);
FIXMLRootInbound inbound = new FIXMLRootInbound();
String xml = XmlUtil.xmlObjectToString(inbound);
message = jmsTemplate.sendAndReceive(requestQueue,
session -> {
Message req = session.createTextMessage(xml);
req.setJMSCorrelationID(UUID.randomUUID().toString());
req.setJMSDestination(requestQueue);
req.setJMSReplyTo(replyQueue);
return req;
});
} catch (Throwable e) {
e.printStackTrace();
}
return message;
}
private MQConnectionFactory mqQueueConnectionFactory() throws NoSuchAlgorithmException, KeyStoreException,
IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, JmsException {
SSLSocketFactory sslSocketFactory = sslContext().getSocketFactory();
MQEnvironment.sslSocketFactory = sslSocketFactory;
MQEnvironment.sslCipherSuite = "TLS_RSA_WITH_AES_256_CBC_SHA";
MQEnvironment.sslFipsRequired = false;
MQConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName(host);
try {
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
mqQueueConnectionFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE,
WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setQueueManager(queueManager);
mqQueueConnectionFactory.setSSLCipherSuite("TLS_RSA_WITH_AES_256_CBC_SHA");
mqQueueConnectionFactory.setCCSID(285);
mqQueueConnectionFactory.setChannel(channel);
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setSSLSocketFactory(sslSocketFactory);
mqQueueConnectionFactory.setSSLFipsRequired(false);
} catch (Exception e) {
log.error("Error creating MQQueueConnectionFactory.", e);
}
return mqQueueConnectionFactory;
}
private SSLContext sslContext() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
try (InputStream cert = new FileInputStream("C:\\myplace\\Dev\\Certificates\\MY_KEYSTORE.jks")) {
final KeyStore caCertsKeyStore = KeyStore.getInstance("JKS");
caCertsKeyStore.load(cert, "changeit".toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.ONLY_END_ENTITY,
PKIXRevocationChecker.Option.SOFT_FAIL,
PKIXRevocationChecker.Option.NO_FALLBACK));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(caCertsKeyStore, new X509CertSelector());
pkixParams.addCertPathChecker(rc);
kmf.init(caCertsKeyStore, "changeit".toCharArray());
tmf.init( new CertPathTrustManagerParameters(pkixParams) );
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException("Exception creating SSLContext", e);
}
}
Since you are using the 9.1.0.0 com.ibm.mq.allclient.jar you do not need all of the code you have related to the key store, for example:
SSLSocketFactory sslSocketFactory = sslContext().getSocketFactory();
//Note that MQEnvironment is used with IBM MQ Classes for Java not IBM MQ Classes for JMS
MQEnvironment.sslSocketFactory = sslSocketFactory;
MQEnvironment.sslCipherSuite = "TLS_RSA_WITH_AES_256_CBC_SHA";
MQEnvironment.sslFipsRequired = false;
mqQueueConnectionFactory.setSSLSocketFactory(sslSocketFactory);
private SSLContext sslContext() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
try (InputStream cert = new FileInputStream("C:\\myplace\\Dev\\Certificates\\MY_KEYSTORE.jks")) {
final KeyStore caCertsKeyStore = KeyStore.getInstance("JKS");
caCertsKeyStore.load(cert, "changeit".toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.ONLY_END_ENTITY,
PKIXRevocationChecker.Option.SOFT_FAIL,
PKIXRevocationChecker.Option.NO_FALLBACK));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(caCertsKeyStore, new X509CertSelector());
pkixParams.addCertPathChecker(rc);
kmf.init(caCertsKeyStore, "changeit".toCharArray());
tmf.init( new CertPathTrustManagerParameters(pkixParams) );
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException("Exception creating SSLContext", e);
}
}
You can instead replace that with setting the following two system properties, this will work with both Oracle and IBM Java:
System.setProperty("javax.net.ssl.keyStore", "C:\\myplace\\Dev\\Certificates\\MY_KEYSTORE.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
The above settings have always worked for IBM Java, but with Oracle Java this did not work with older versions of MQ. It was fixed for Oracle java in the following versions of IBM MQ (Base 9.0 and 9.1 have the same fix):
Version Maintenance Level
v7.1 7.1.0.8
v7.5 7.5.0.7
v8.0 8.0.0.5
IBM Java and Oracle Java have different CipherSuite names, these are documented in the IBM MQ v9.1 Knowledge center page "TLS CipherSpecs and CipherSuites in IBM MQ classes for JMS.
You have specified TLS_RSA_WITH_AES_256_CBC_SHA in the posted code, this would be the SSLCIPH value on the MQ Queue manager SVRCONN channel, and would map to the following CipherSuites:
IBM Java: SSL_RSA_WITH_AES_256_CBC_SHA
Oracle Java: TLS_RSA_WITH_AES_256_CBC_SHA
Related to the above, if you are using Oracle Java you need to set the following system property so that the MQ JMS classes will be able to use the right mapping of the Oracle CipherSuite name:
System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", "false");
The error you provided appears to be from the IBM i MQ Queue manager, but does not provide enough information to diagnose the problem.
The error states the following:
An unexpected SSL communications error occurred for a channel, as reported in the preceding messages.
Can you edit your question and provide the details from the "preceding messages".
You stated
for some reason my application is revoking the certificate (something related with CLR)
It could be that the MQ queue manager is attempting itself to connect to the OCSP server specified in the your client cert's AuthorityInfoAccess (AIA) certificate extension. If MQ can not reach this OCSP server with a default configuration the connection will be denied. If you are unable to update your network to allow connection to the OCSP server then you can disable this check but note that you will not know if a cert is revoked. To disable the checking the following can be added to the queue manager's qm.ini file SSL stanza:
SSL:
OCSPAuthentication=Optional
OCSPCheckExtensions=no
One last comment, the CipherSuite you listed in your example code TLS_RSA_WITH_AES_256_CBC_SHA is a TLS1.0 CipherSuite. Like SSL before it this and TLS1.1 are commonly deprecated across many industries. I looked for reference to post and a google of "tls 1.0 end of life" gives many references.
Quoting one "TLS 1.0 end-of-life on June 30th, 2018" below:
The Deadline The PCI Council is responsible for determining when older protocols are phased out. They initially decided TLS 1.0 would
reach end of life on 30 June 2016, later extending the date to 30 June
2018. The final deadline has past and all web servers, web browsers, web software and email applications have had to discontinue TLS 1.0
support, or fall foul of important security updates.
I would suggest choosing one listed as TLS1.2 in the Knowledge center page I linked to above for example TLS_RSA_WITH_AES_256_CBC_SHA256.
I am new to opc ua and not a pro in java. While setting up a client in java I'm having trouble with the certificate dealing. I want to connect to the server via Basic 256, SignAndEncrypt. As I understand, in this stage of security a certificate, created or loaded by the client, is send to the server, where it must be accepted. The server then sends a certificate back to the client, which then needs to be accepted by the client. Please, correct me, if I'm wrong.
Creating/Loading a certificate on the client side and sending it to the server does already work fine (see code below) and I can then accept it on the server side manually. But after that I'm stuck: How can I see this certificate validation in my code and how can I find the server certificate, let alone accept it?
I used the SampleConsoleClient of opc ua for some orientation during implementation. But in contrast to there, I do not use any user input.
Here's some of my code so far.
Initialization:
try {
client = new UaClient(serverUri);
} catch (final URISyntaxException e) {
throw new InitializationException("The server uri has an invalid syntax.", e);
}
try {
client.setApplicationIdentity(createApplicationIdentity());
} catch (final SecureIdentityException e) {
throw new InitializationException(
"Application Identity could not be created due to a Security Identity Exception.", e);
} catch (final IOException e) {
throw new InitializationException("Application Identity could not be created due to an IO Exception.",
e);
}
createApplicationIdentity():
final ApplicationDescription appDescription = new ApplicationDescription();
appDescription.setApplicationName(new LocalizedText(APPLICATION_NAME, Locale.ENGLISH));
appDescription.setApplicationUri(APPLICATION_URI);
appDescription.setProductUri(PRODUCT_URI);
appDescription.setApplicationType(ApplicationType.Client);
// Setting security features
client.setSecurityMode(SecurityMode.BASIC256_SIGN_ENCRYPT);
client.setCertificateValidator(validator);
validator.setValidationListener(myValidationListener); //myValidationListener is similar to most lines in MyCertificateValidationListener in the opc ua samples
final File privatePath = new File(validator.getBaseDir(), "private");
final KeyPair issuerCertificate = null;
final int[] keySizes = null;
final ApplicationIdentity identity = ApplicationIdentity.loadOrCreateCertificate(appDescription,
"Sample Organisation", "opcua", privatePath, issuerCertificate, keySizes, true);
identity.setApplicationDescription(appDescription);
return identity;
After initializing, I try to connect like this (with annotation, how I imagine the connection could work properly):
final String securityPolicy = client.getEndpoint() == null
? client.getSecurityMode().getSecurityPolicy().getPolicyUri()
: client.getEndpoint().getSecurityPolicyUri();
client.setSessionName(String.format("%s#%s/Session%d", APPLICATION_NAME,
ApplicationIdentity.getActualHostNameWithoutDomain(), ++sessionCount));
try {
//Idea: catch the server certificate and accept it. Only if that was possible: connect
client.connect();
} catch (final ServiceException e) {
e.printStackTrace();
}
client.setKeepSubscriptions(false);
// After that resolving namespace index (works fine)
}
And the error, that is thrown:
WARN (?:?): /<IPofServer> Error org.opcfoundation.ua.common.ServiceResultException: Bad_SecurityChecksFailed
(0x80130000) "An error occurred verifying security." at
org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
com.prosysopc.ua.client.ConnectException: Failed to create secure channel to server: : opc.tcp://<IPofServer>
[http://opcfoundation.org/UA/SecurityPolicy#Basic256,SignAndEncrypt]
ServiceResult=Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at com.prosysopc.ua.client.UaClient.n(Unknown Source)
at com.prosysopc.ua.client.UaClient.connect(Unknown Source)
at *lineOfCode*
Caused by: org.opcfoundation.ua.common.ServiceResultException:
Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
With the lineOfCode being client.connect().
Thanks in advance for the help!!
The server will send it's certificate to the client. The client then has to
verify the validity of the certificate. This amounts to verifying the signature of the certificate, checking the validity, whether the hostname in the certificate matches the hostname in the endpoint, checking CRLs and so forth. Usually the SDK (the validator) should do this for you, but you might need to feed some parameters into the validator which checks should actually be performed. The security policy Basic256 imposes some minimal requirements on the certificate which the certificate should meet, of course. You can check the requirements here: http://opcfoundation-onlineapplications.org/profilereporting/ -- go to Security Category -> Facets -> Security policy.
check whether the server certificate is trusted. This usually amounts to checking whether a copy of the (puclic key) certificate has been put into some certicate store chosen as a trust store. If you write the client it's up to you to say which store to choose, but you will need to tell the validator where to look. I don't know that much about OPc UA development in Java, but you should check which certificate stores the validator expects. Maybe there is a default keyfile.
(On server side the same happens with the client certificate).
This asssumes you are starting out with self-signed certificates. If you are using certificates signed by a CA both applications (server and client) will need to be able to verify the whole chain of the other party. It can be stored locally in some store or can be send by the other party. At least one certificate in the chain has to be trustest (has to be put into the trust store).
For a general description on how UA security works have a look at this link:
https://opcfoundation.org/wp-content/uploads/2014/08/11_OPC_UA_Security_How_It_Works.pdf
For a detailed account you should consult the specification, available at GitHub.
Edit: one addtional remark which may help here: you seem to be using some SDK for the purpose in question. While validation of certificates, i.e. doing the signature checks etc, is usually covered by such an SDK the configuration of the application is the task of the application (programmer). This includes the location where you store trusted certificates and where and how you gather together missing parts of certificate chains. You might first try to check how demo clients and servers deal with this task, in other words check out the configuration tasks for such applications by trying to create a secure connection from, say, UA Expert to the sample servers from the OPC foundation. In the .Net SDK of the OPC foundation the location for the trust store defaults to a certain directory in the file system (a subfolder of C:\ProgramData\OpcFoundation, it's Windows only). You can, however, overwrite this when you initialize the validator. Other clients use their own directory structure for the storage of trusted certificates
You are obviously referring to the Prosys OPC UA Java SDK.
What always happens first, when you try to establish a secure connection for the first time, is that the server will deny access to your client application - and returns Bad_SecurityChecksFailed.
Only, after you have told the server to trust (the certificate of) your client application, will you get to the phase where the client application will try to verify the server's certificate - and your 'validationListener' will be triggered.
Thank you all for your answers. In the meantime, I tried basically copy/paste and modifying the connect() and initalize() methods from the SampleConsoleClient in the Prosys SDK samples And that worked. Guess it had something to do with renewing some information, but I'm not quite sure of that... Fact is, my application is now working, but thanks for your efforts!
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.