I am working on a client program(in android) which needs to communicate with a jboss server via SSL. Currently I have followed the instructions from http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html to get the self-signed certificate and connect to the server. I am wondering if this can be done from java since whenever server certificate changes , I have to manually get the certificate and compile it into the code itself. I have also noticed that google chrome in android can do this. Any idea how to achieve this.
Thanks.
I am using this test code to get server certificate without success.
public class TestCert {
public static void main(String args[]) throws Exception {
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(<ip>, <port>);
socket.startHandshake();
SSLSession session = socket.getSession();
java.security.cert.Certificate[] servercerts = session.getPeerCertificates();
List mylist = new ArrayList();
for (int i = 0; i < servercerts.length; i++) {
mylist.add(servercerts[i]);
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(mylist);
FileOutputStream f = new FileOutputStream("/tmp/CertPath.dat");
ObjectOutputStream b = new ObjectOutputStream(f);
b.writeObject(cp);
}
}
Related
I'm using jsch to connect sftp server from java and send a file to a specific directory
private static boolean enviarCSV(String localFile) throws IOException, JSchException, SftpException {
logger.info("Enviando fichero a maquina destino..");
ChannelSftp channelSftp = setupJsch();
channelSftp.connect();
String remoteDir = Config.getParametro("wssf.conf.remoteDir");
channelSftp.put(localFile, remoteDir + localFile);
logger.info("Enviado con exito!");
channelSftp.exit();
return false;
}
private static ChannelSftp setupJsch() throws JSchException {
JSch jsch = new JSch();
String user = Config.getParametro("wssf.conf.login.usuario");
String password = Config.getParametro("wssf.conf.login.password");
String remoteHost = Config.getParametro("wssf.conf.remotehost");
Session jschSession = jsch.getSession(user, remoteHost, 40020);
jschSession.setConfig("StrictHostKeyChecking", "no");
jschSession.setPassword(password);
jschSession.connect();
return (ChannelSftp) jschSession.openChannel("sftp");
}
I need to use public key in order to connect the SFTP server. I'm pretty new in security and I'm not sure how to do it, additionaly I only see examples using private keys, but I think I dont need it , do I ?
Much appreciate if you help me
Thanks
Keys come in pairs. One private, one public.
To authenticate with your public key (that you freely share), you will need to prove to the other side that you in fact also have the matching private key (because anyone could have your public key, but only you have the private one).
You do this by enrcypting or singing something with your private key - which can be verified through your public key.
So for "public key authentification" you in fact have to work with the private key as well ... and can use the examples referring to that :-)
I have the following code to start my software:
public static void main(String[] args) throws Exception {
// set system property for exit on failure
System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
// create tomcat
Tomcat tomcat = new Tomcat();
// create connector, configure and add to tomcat
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setMaxPostSize(-1);
connector.setPort(8080);
connector.setURIEncoding("UTF-8");
((Http11NioProtocol)connector.getProtocolHandler()).setConnectionUploadTimeout(36000000);
((Http11NioProtocol)connector.getProtocolHandler()).setDisableUploadTimeout(false);
((Http11NioProtocol)connector.getProtocolHandler()).setConnectionTimeout(3600000);
((Http11NioProtocol)connector.getProtocolHandler()).setCompression("on");
((Http11NioProtocol)connector.getProtocolHandler()).setCompressibleMimeType("text/html,text/xml,text/plain,application/javascript");
tomcat.setConnector(connector);
// add web app with jsps and servlets
StandardContext standardContext = (StandardContext)tomcat.addWebapp("", new File(".").getAbsolutePath()+"/src/webroot");
standardContext.getJarScanner().setJarScanFilter(new JarScanFilter() { #Override public boolean check(JarScanType jarScanType, String s) {
if(s != null){
if(s.startsWith("mchange-commons-java")){
return false;
}
}
return true;
}});
standardContext.setParentClassLoader(Run.class.getClassLoader());
WebResourceRoot webResourceRoot = new StandardRoot(standardContext);
File additionWebInfClassesFolder = new File(new File(".").getAbsolutePath(), "target/classes");
WebResourceSet webResourceSet = new DirResourceSet(webResourceRoot, "/WEB-INF/classes", additionWebInfClassesFolder.getAbsolutePath(), "/");
webResourceRoot.addPreResources(webResourceSet);
standardContext.setResources(webResourceRoot);
// start tomcat
tomcat.start();
// stay in this method as long as tomcat is running
tomcat.getServer().await();
}
Now I have my certificate files (private key, certificate) and I want to add SSL functionality to this Tomcat Server. I know that this might not be best practice, but I am looking for a very simple way to do that. I know I can create a keystore file and add the properties to the connector but what I basically want is to have a string with my certificate content and apply that.
My solution winds up looking a lot like the code I finally stumbled upon here to help me fix my issues: https://github.com/OryxProject/oryx/blob/master/framework/oryx-lambda-serving/src/main/java/com/cloudera/oryx/lambda/serving/ServingLayer.java#L202
Note: I believe I am using Tomcat 10.
private Connector createSslConnector(){
Connector httpsConnector = new Connector();
httpsConnector.setPort(443);
httpsConnector.setSecure(true);
httpsConnector.setScheme("https");
httpsConnector.setAttribute("SSLEnabled", "true");
SSLHostConfig sslConfig = new SSLHostConfig();
SSLHostConfigCertificate certConfig = new SSLHostConfigCertificate(sslConfig, SSLHostConfigCertificate.Type.RSA);
certConfig.setCertificateKeystoreFile("/root/.keystore");
certConfig.setCertificateKeystorePassword("changeit");
certConfig.setCertificateKeyAlias("mykeyalias");
sslConfig.addCertificate(certConfig);
httpsConnector.addSslHostConfig(sslConfig);
return httpsConnector;
}
thanks in advance for the help.
I developed a suite of api tests in Java TestNG against an api deployed to a non-secure internal QA environment. Recently this application was re-deployed to a new, secure environment. When this happened I began to see the following error on every api request, both GET and POST:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My first move was to install the appropriate certificate, even though it is signed by DigiCert, so it should be approved by default.
keytool -import -alias ca -file qa4cert.crt -keystore cacerts -storepass changeit
And that had no effect. I also tried adding the certificate via the IDE (Intellij). Again, no effect, still seeing the same error.
Since I was unable to make progress here, and I am still working against an internal QA environment with no sensitive data, I was comfortable dropping the certificate validation and install an all-trusting cert manager. My simple implementation is below:
package test_utils;
import javax.net.ssl.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class SSLTool {
private static boolean isTrustAllCertsInitialized = false;
public static void disableCertificateValidation() {
if (isTrustAllCertsInitialized) {
return;
}
isTrustAllCertsInitialized = true;
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLContext.setDefault(ctx);
System.out.println("new trust manager should be set");
} catch (Exception e) {
e.printStackTrace();
}
return ctx;
}
}
I ran the above code as part of my --before-- implementation in the test suite and verified we reached the end of the try block, so the new All_trusting TrustManager should be set. Unfortunately this also had no effect, I am still seeing the error.
To try and isolate the issue, I processed the same POST request through both Postman and curl, both returned the desired result, no errors.
For sanity's sake I also hit a few public urls both secure and insecure and got the desired results as well.
At this point I'm stumped. Since Postman and curl work, the error must have something to do with my implementation, but I don't understand why it would work with our old environment but not in the new one. A debugging version of my code that makes a failing get request is below. This fails both when run through the ide, and through the command line with maven.
Test File
public class DebugTests extends BaseTest {
#Test
public void debug() {
BaseApi api = new BaseApi();
api.debugGet("<<MYURL>>");
}
BaseTest
#Listeners(Listener.class)
public class BaseTest {
#BeforeMethod
public void before() {
// the below function is used to disable certificate validation. It is ONLY meant to be used in testing environments
// if used in production it exposes our test suite to MITM attacks.
SSLTool.disableCertificateValidation();
}
#AfterMethod
public void after() {
}
}
Relevant Code from the API object
public BaseApi() {
SSLContext ctx = SSLTool.disableCertificateValidation();
client = HttpClients.custom().setSSLContext(ctx).build();
System.out.println("trust manager set");
}
public void debugGet(String endpoint) {
try {
client.execute(this.buildGetConnection(endpoint, false));
} catch (Exception e) {
e.printStackTrace();
}
}
protected HttpGet buildGetConnection(String endpoint) {
return this.buildGetConnection(endpoint, true);
}
protected HttpGet buildGetConnection(String endpoint, boolean auth) {
//TODO build a switch to change testing environments based off command line
HttpGet get = new HttpGet(rootUrl + endpoint);
if(auth) {
StsAuthApi authApi = new StsAuthApi();
get.setHeader("Authorization", "Bearer " + authApi.getToken());
}
get.setHeader("accept","application/json");
get.setHeader("Content-Type","application/xml");
System.out.println("making GET request to " + rootUrl + endpoint);
return get;
}
In my debugging implementation rooturl is an empty string so the url matches the string supplied via the test case.
HttpClients.createDefault() will not use your null trust manager. It internally creates and initialises an SSLContext like this:
final SSLContext sslContext = SSLContext.getInstance(SSLContextBuilder.TLS);
sslContext.init(null, null, null);
You can create the HttpClient like this instead:
HttpClients.custom()
.setSSLContext(ctx)
.build();
Where ctx is the one you created in the disableCertificateValidation() method.
The question about why it didn't work when you added your certificate to cacerts is still open. If you did that correctly then the default client should have used it. You can set the system property javax.net.debug=all if you want to debug that one further (it will print out your truststore when it's first initialised into a context).
I am using JAVA 8. I am trying to connect a Socket Server using client certificates and certificate tree.
I have followings provided by client:
Client CERT (PEM)
Private Key (PEM)
CA Tree (PEM) - with 4 Certificates
I have created keystore.jks using following steps:
Combining client cert and CA tree in a single pem file using cat
Crested PKCS12 file from combined file encrypted using private key(OpenSSL Command)
Generated JKS keystore file using keytool
I have created trustore.jks using following steps:
Split CA Tree (4 certificates) into 4 different files
Generated trustore file using keytool by importing each file one by one
My Sample code is as following :
package com.tutorial.exception.customize;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.util.Scanner;
/**
* Created by SomnathG on 12/1/2016.
*/
public class Client {
public Client() {
System.setProperty("javax.net.ssl.keyStore", {keystore Location});
System.setProperty("javax.net.ssl.keyStorePassword", {password});
System.setProperty("javax.net.ssl.trustStore", {trustore location});
System.setProperty("javax.net.ssl.trustStorePassword", {password});
System.setProperty("javax.net.debug", "all");
System.setProperty( "sun.security.ssl.allowUnsafeRenegotiation", "true" );
}
public void connectHost(){
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = null;
try {
sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port);
sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"});
sslSocket.startHandshake();
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
System.out.println("Sending request to Socket Server");
outputStream.write("Hello".getBytes());
outputStream.write("exit".getBytes());
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
int bytesRead = 0;
String messageString = "";
DataInputStream in = new DataInputStream(sslSocket.getInputStream());
while(!end)
{
bytesRead = in.read(messageByte);
messageString += new String(messageByte, 0, bytesRead);
if (messageString.length() == 100)
{
end = true;
}
}
System.out.println("MESSAGE: " + messageString);
// byte[] read = (byte[]) ois.readObject();
//String s2 = new String(read);
//System.out.println("" + s2);
//System.out.println("Message: " + message);
//close resources
//System.out.println(receive(inputStream));
}catch (IOException e) {
e.printStackTrace();
System.out.println("=====");
System.out.println(e.getMessage());
System.out.println("=====");
CertPathValidatorException ce = new CertPathValidatorException(e);
System.out.println("******");
System.out.println(ce.getIndex());
System.out.println(ce.getReason());
System.out.println("******");
//e.printStackTrace();
}
}
public static void main(String[] args){
new Client().connectHost();
}
}
I am getting following exception after executing the code:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at com.tutorial.exception.customize.Client.connectHost(Client.java:33)
at com.tutorial.exception.customize.Client.main(Client.java:82)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
After analyzing the log I have found "clientHello" and "serverHello" messages but after that application is throwing above mentioned exception.
What am I doing wrong? Please advice.
Thanks,
Somnath Guha
I have figured out the issue after analyzing the debug lo.
"BasicConstraints" was missing from the server V3 certificates and thus java was failing to recognize the certificate as a valid certificate. Once that constraint has been added then the client was able to handshake with the server and able to communicate with server.
BasicConstraints:[
CA:true
PathLen:2147483647
]
I want to connect my Eclipse plug-in to an HTTPS URL, but have a problem because the user would need to accept the certificate. Of course there are a couple of tutorials for how to do this in plain Java, but that might be hard to do inside an Eclipse plug-in and I think I'd reinvent the wheel that way.
Because Eclipse has some built in tooling to connect to sites with different network protocols. An example would be the "Install new Software..." action. The tooling even has a preference page that lists HTTPS separately.
According to the Eclipse Help, the KeyStore is used "as a repository for Certificates used for trust decisions [...] when making SSL connections". Yet I couldn't figure out how to use it.
So my question is: How do I use the Eclipse's build in facilities to connect to my HTTPS site?
Based on this answer here I build my own plug-in which loads just the one certificate I need (lucky me) in its EarlyStartup:
public class EarlyStartup implements IStartup {
private static final String ALIAS = "ACME";
#Override
public void earlyStartup() {
final char[] passphrase = "changeit".toCharArray();
final char separator = File.separatorChar;
final File dir = new File(System.getProperty("java.home") + separator + "lib" + separator + "security");
final File file = new File(dir, "cacerts");
try (InputStream certIn = getClass().getResourceAsStream("acme.org.crt");
final InputStream localCertIn = new FileInputStream(file);) {
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(localCertIn, passphrase);
if (keystore.containsAlias(ALIAS)) {
return;
}
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final Certificate cert = cf.generateCertificate(certIn);
keystore.setCertificateEntry(ALIAS, cert);
try (OutputStream out = new FileOutputStream(file)) {
keystore.store(out, passphrase);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
}