So I wanted to use this XML parser class to parse an XML file from one of my sites, which acts as an API.
So I came across this XMLParser class over here: XMLParser.java
Since I use https over at my site, I quickly found out this isn't going to work with it, unless SSL is implemented within the code, where it fetches XML file from the URL using the following method:
getXmlFromUrl(String url)
So for my test environment I was quick to create a new httpClient method, which would accept any SSL certificate from.
public static HttpClient createHttpClient()
{
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true);
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
return new DefaultHttpClient(conMgr, params);
}
So this is my final XMLParser.java after I did it.
XMLParser_New.java
But this all went in vain, as I am repeatedly getting the following exception.
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
Where have I gone wrong or what is it that I don't understand.
Thank you :)
How are you implementing SSL. Have you followed these steps to implement it:
Need to create X.509 certificate on Server.
Store trusted CAs on terminal side.
Encrypt and decrypt messages on both ends.
Are you using self signed certificate. Then you need to store that certificate on device and load your own trust store to check the SSL certificates, rather than android default trust store.
Here is very good article to implement SSL on android:
http://www.codeproject.com/Articles/826045/Android-security-Implementation-of-Self-signed-SSL
I think in your case trust certificate on terminal side is missing. That's why it is throwing this exception.
Related
I've got a program that makes use of the java.net.http.HttpClient, which was introduced in Java 11, to connect and send requests to internal services. These services are mutually authenticated, both presenting certificates issued by an internal CA.
For example,
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
KeyManager keys = /* load our cert and key */;
TrustManager trust = /* load our trusted CA */;
sslContext.init(keys, trust, secureRandom);
HttpClient.Builder builder = HttpClient.newBuilder().sslContext(sslContext);
HttpClient client = builder.build();
On our hosts, the client's certificate and private key get rotated pretty regularly, more often than the host or application gets a chance to restart. I'd like to be able to reload the HttpClient's SSLContext with the new cert/key pair while it's still running, but can't see any way to do so.
After the HttpClient has been built, it only provides an sslContext() getter to retrieve the SSLContext. It doesn't seem to have an API to set a new one.
Is there any other mechanism to achieve this?
(I'm thinking of something like Jetty's SslContextFactory#reload(SSLContext) method.)
I think this question is similar to How to renew keystore (SSLContext) in Spring Data Geode connections without restarting? the answer I have provided there is similar to this one.
This option is unfortunately not available by default. After you have supplied the SSLContext to the HttpClient and build the client you cannot change the SSLContext. You will need to create a new SSLContext and a new HttpClient, but there is a workaround which will do the trick to apply a reload/update.
I had the same challenge for one of my projects and I solved it by using a custom trustmanager and keymanager which wraps around the actual trustmanager and keymanager while having the capability of swapping the actual trustmanager and keymanager. So you can use the following setup if you still want to accomplish it without the need of recreating the HttpClient and SSLContext:
SSLFactory baseSslFactory = SSLFactory.builder()
.withDummyIdentityMaterial()
.withDummyTrustMaterial()
.withSwappableIdentityMaterial()
.withSwappableTrustMaterial()
.build();
HttpClient httpClient = HttpClient.newBuilder()
.sslParameters(sslFactory.getSslParameters())
.sslContext(sslFactory.getSslContext())
.build()
Runnable sslUpdater = () -> {
SSLFactory updatedSslFactory = SSLFactory.builder()
.withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
.withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
.build();
SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory)
};
// initial update of ssl material to replace the dummies
sslUpdater.run();
// update ssl material every hour
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);
// execute https request
HttpResponse<String> response = httpClient.send(aRequest, HttpResponse.BodyHandlers.ofString());
See here for the documentation of this option: Swapping KeyManager and TrustManager at runtime
And here for an actual working example: Example swapping certificates at runtime with HttpUrlConnection
And here for a server side example: Example swapping certificates at runtime with Spring Boot and Jetty Also other servers are possible such as Netty or Vert.x as long as they can either use SSLContext, SSLServerSocketFactory, TrustManager or KeyManager
You can add the library to your project with:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.4.8</version>
</dependency>
You can view the full documentation and other examples here: GitHub - SSLContext Kickstart
By the way I need to add a small disclaimer I am the maintainer of the library.
I'm attempting to implement a WebSocket Client in an application that supports secure transmissions through SSL. The application already supports standard SSL connections over HTTP, by implementing custom Key and Trust managers (these custom implementations are in place to prompt the user for a certificate when needed).
I'm having trouble getting a secure connection to our remote WebSocket endpoint. The failure is occurring during the handshake. I've tried two different implementations of the WebSocket API (both Tyrus and Jetty), and both fail in the same way, which, of course, leads me to point to our SSL implementation.
As I mentioned, the failure is occurring during the handshake. It seems that the connection cannot figure out that there are client certificates that are signed by the supported authorities returned from the server. I'm stumped to figure out if I haven't supplied the client certificates to the WebSocket API correctly, or if our custom Key/Trust managers are even getting used.
Here's a dump of the SSL Debug logs:
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
(list of about 15 cert authorities supported by the server)
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** CertificateChain
<empty>
***
I've set breakpoints in our TrustManager implementation, to determine if they are ever getting called, and it seems that they are not being called at this point.
I've been attempting to debug this for a few days now, and am running out of things to try.
Any suggestions would be greatly appreciated!
Here's a snippet of the Jetty Code:
SSLContext context = SSLContext.getInstance("TLS");
// getKeyManagers / getTrustManagers retrieves an
// array containing the custom key and trust manager
// instances:
KeyManager[] km = getKeyManagers();
TrustManager[] tm = getTrustManagers();
context.init(km, tm, null);
SslContextFactory contextFactory = new SslContextFactory();
contextFactory.setContext(context);
WebSocketClient client = new WebSocketClient(contextFactory);
SimpleEchoClient echoClient = new SimpleEchoClient();
try {
client.start();
ClientUpgradeRequest request = new ClientUpgradeRequest();
Future<Session> connection = client.connect(echoClient, uri, request);
Session session = connection.get();
// if everything works, do stuff here
session.close();
client.destroy();
} catch (Exception e) {
LOG.error(e);
}
can you try with rejectUnAuthorized:false so that your certificates for which your browser is unable to authorize will skip the authorization.
var ws = new WebSocket('wss://localhost:xxxx', {
protocolVersion: 8,
origin: 'https://localhost:xxxx',
rejectUnauthorized: false
});
My client implements Two-Way SSL in the following way:
private final static String KEYSTORE = "/security/client.jks";
private final static String KEYSTORE_PASSWORD = "secret";
private final static String KEYSTORE_TYPE = "JKS";
private final static String TRUSTSTORE = "/security/certificates.jks";
private final static String TRUSTSTORE_PASSWORD = "secret";
private final static String TRUSTSTORE_TYPE = "JKS";
...
KeyStore keystore = KeyStore.getInstance(KEYSTORE_TYPE);
FileInputStream keystoreInput = new FileInputStream(new File(KEYSTORE));
keystore.load(keystoreInput, KEYSTORE_PASSWORD.toCharArray());
KeyStore truststore = KeyStore.getInstance(TRUSTSTORE_TYPE);
FileInputStream truststoreIs = new FileInputStream(new File(TRUSTSTORE));
truststore.load(truststoreIs, TRUSTSTORE_PASSWORD.toCharArray());
SSLSocketFactory socketFactory = new SSLSocketFactory(keystore, KEYSTORE_PASSWORD, truststore);
Scheme scheme = new Scheme("https", 8543, socketFactory);
SchemeRegistry registry = new SchemeRegistry();
registry.register(scheme);
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry);
httpclient = new DefaultHttpClient(ccm);
HttpResponse response = null;
HttpGet httpget = new HttpGet("https://mylocalhost.com:8543/test");
response = httpclient.execute(httpget);
...
And I try to retrieve the X.509 certificate on the server's side from the client via javax.servlet.http.HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate") as it is decribed here: http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html#getAttribute%28java.lang.String%29.
I get the HttpServletRequest on the server's side via:
HttpServletRequest servletRequest = (HttpServletRequest) msg.get("HTTP.REQUEST"); via the handleMessage(Message msg) method of my interceptor class which extends AbstractPhaseInterceptor<Message>. I have to use JAX-RS 1.1.1 on the server's side because of some Maven dependencies which I am not allowed to change and so I cannot use ContainerRequestFilter (supported from JAX-RS 2.0 on).
My problem is that getAttribute("javax.servlet.request.X509Certificate") on the server's side returns null all the time. If I verify the traffic between server and client, I can see that the certificate from the server is sent to the client, that handshake works. But I cannot see that the client certificate is sent to the server and I think it is the reason why getAttribute("javax.servlet.request.X509Certificate") returns null. Does someone know how I can solve that problem? I tried some other implementations on the client's side already, but with no change.
What am I doing wrong? Many thanks in advance!
Additional information: I have seen on the server's side that javax.servlet.request.ssl_session_id, javax.servlet.request.key_size and javax.servlet.request.cipher_suite are set, but the key javax.servlet.request.X509Certificate is not set. I'm using Jetty Server 8.1.15, Apache CXF 2.7.x and JAX-RS 1.1.1. I tried with Jetty configuration via http://cxf.apache.org/docs/jetty-configuration.html and http://cxf.apache.org/docs/secure-jax-rs-services.html#SecureJAX-RSServices-Configuringendpoints, the attribute still isn't set.
Problem is solved. It wasn't a problem in the code, it was a certificate problem only. My problem was that I was a beginner regarding X509 certificates as well, it was a handshake problem between server and client. In this case, only the SSL/Handshake debug helped me. The debug log told that the server only accepted client certificates from a specific CA, the server told the client the required CA in a certificate request during the ServerHello message. Since the Client didn't have a certificate from that CA, it didn't send something and the connection between client and server was closed then, with the result that javax.servlet.request.X509Certificate was not set.
For all others who might join the same problem sometime (which seems to be a common SSL configuration problem regarding to IBM as it is mentioned in the first link below), the following sources helped me a lot:
- http://www-01.ibm.com/support/docview.wss?uid=swg27038122&aid=1
(pages 16 and 17)
- http://java.dzone.com/articles/how-analyze-java-ssl-errors (shows as the handshake should look)
- need help Debugging SSL handshake in tomcat (shows how to debug ssl errors in Java)
- https://thomas-leister.de/internet/eigene-openssl-certificate-authority-ca-erstellen-und-zertifikate-signieren/ (in German, but maybe you can find an English equivalent)
- https://access.redhat.com/site/documentation/en-US/Red_Hat_JBoss_Fuse/6.0/html/Web_Services_Security_Guide/files/i382674.html (continuation of the German article)
- http://www.webfarmr.eu/2010/04/import-pkcs12-private-keys-into-jks-keystores-using-java-keytool/ (how to create keystore and truststore)
After creating an own CA, a server and client certificate and after creating the keystore and truststore for both, the attribute was set now:
- Here15_1: javax.servlet.request.X509Certificate
- Here16_2: class [Ljava.security.cert.X509Certificate;
- Here16_3: [Ljava.security.cert.X509Certificate;#43b8f002
The server code is able to extract the client certificate information now, too.
I was a bit envious of our Ruby on Rails team who needs to consume the same web services requiring HTTPS request be signed with a certificate. They grab a gem, sign the request with a single line of code, dishes are done...
Java, however, requires that we import the cert to our keystore (trying to avoid this), or create one in memory...that's fine. But, once I do that, it seems like the only option to actually signing requests is using the HttpUrlConnection class. HttpUrlConnection example
I'd like to be able to sign requests using my existing code, which uses apache's DefaultHttpClient client = new DefaultHttpClient(); - but I don't see a way to have the Http client sign requests using an in memory keystore.
Anyone faced this?
Have a look at AuthSSLProtocolSocketFactory.java. You should be able to perform SSL client authentication like this (untested code);
Protocol.registerProtocol("https",
new Protocol("https", new AuthSSLProtocolSocketFactory(keystoreUrl, keystorePassword, truststoreUrl, truststorePassword), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
Your client certificate goes into keystore pointed by keystoreUrl. Read more about this on HttpClient SSL Guide.
In the company where I work I'm developing a web app on a WebSphere 6.1 server. The web application I'm writing has to connect to an external company by using an SSL connection with mutual authentication.
First thing to say: I'm kind of a noob with such things so sorry if I'll say something stupid :)
I have both public and private certificate. I've added the private certificate to the NodeDefaultKeyStore and the public certificate chain to the NodeDefaultTrustStore. Then I've seen that the server has an SSL configuration that encapsulates both KS and TS, and this configuration is linked to the node I'm running my application on.
As a client library, I'm using HttpClient 4.2.3. I created the HttpClient like this
Security.setProperty("ssl.SocketFactory.provider", "com.ibm.jsse2.SSLSocketFactoryImpl");
Security.setProperty("ssl.ServerSocketFactory.provider", "com.ibm.jsse2.SSLServerSocketFactoryImpl");
// e) SETUP SSL
SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSystemSocketFactory();
Scheme httpsScheme = new Scheme("https", HTTPS_PORT, sslSocketFactory);
Scheme httpScheme = new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory());
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(httpScheme);
schemeRegistry.register(httpsScheme);
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry);
// f) CREAZIONE CLIENT HTTP
HttpClient httpClient = new DefaultHttpClient(sslSocketFactory);
// g) CREAZIONE DEL PROXY (possibile che venga disattivato)
Resources res = new Resources();
String proxyHost = res.get(PROXY_HOST);
int proxyPort = Integer.parseInt(res.get(PROXY_PORT));
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
System.setProperty("java.net.useSystemProxies", "false");
// ######################## ==> CHIAMATA AD INPS
HttpResponse resp = httpClient.execute(httpPost);
I've seen the SSLSocketFactory and doesn't contain the certificates I specified. I've seen that the SSLSOcketFactory.getSystemSocketFactory() just reads the javax.ssl.xxxxx properties to initialize the KS and TS to be used for the SSL connection.
So... I have to link the server configuration to my application, but I'm not sure about how to do it in a "proper" way: I could set at runtime such properties with the System.setProperty, but I think it's not a good way to do this kind of work. Is there any way to refer the SSL config (maybe via JNDI) from the application? Or the best way is to configure two URL linking to the KS and TS files and configure the SSLSocketFactory manually?
Thanks in advance for the reply!
Lorenzo
Since you've added the certificates to the NodeDefault stores, I don't think you need to do any manual SSL setup in your code at all. The only additional thing you might need to do is add your destination host to SSL certificate and key management > Dynamic outbound endpoint SSL configurations and select the client certificate alias you want to use for that destination.