Related
I have an application server running some utility commands, which is programmed in C.
I have to connect to the server through Java client program using Java SSL socket with
client authentication.
The key on the server side was created using:
openssl req -new -text -out ser.req
openssl rsa -in privkey.pem -out ser.key
openssl req -x509 -in ser.req -text -key ser.key -out ser.crt
I have been provided the server key and certificate. I have combined the key and certificate
into a PKCS12 format file:
openssl pkcs12 -inkey ser.key -in ser.crt -export -out ser.pkcs12
Then loading the resulting PKCS12 file into a JSSE keystore with keytool:
keytool -importkeystore -srckeystore ser.pkcs12 -srcstoretype PKCS12 -destkeystore ser.keystore
But when I try to connect, I get the following error:
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alert.createSSLException(Alert.java:131)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:324)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:267)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:262)
at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:377)
at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422)
at sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
at sun.security.ssl.SSLTransport.decode(SSLTransport.java:149)
at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1143)
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1054)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:394)
at SSLSocketClient.main(SSLSocketClient.java:67)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:456)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:323)
at sun.security.validator.Validator.validate(Validator.java:271)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:315)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:223)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:638)
... 11 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:451)
... 17 more
On the server side log:
SSL open_server: could not accept SSL connection: sslv3 alert certificate unknown
Running command:
java -Djavax.net.ssl.keyStore=/path/to/ser.keystore -Djavax.net.ssl.keyStorePassword=passwd SSLSocketClient <server-ip> <port>
Does anyone know the cause of this problem?
Updated the client source code:
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import java.security.cert.CertificateFactory;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.SocketFactory;
public class SSLSocketClient {
public static void main(String [] args) throws Exception {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
SSLSocketFactory sf =
(SSLSocketFactory)SSLSocketFactory.getDefault();
Socket client = new Socket(serverName, port);
System.out.println("Connected to " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outToServer));
writeData(out);
out.flush();
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
readData(in);
outToServer = client.getOutputStream();
out = new DataOutputStream(new BufferedOutputStream(outToServer));
writeData2(out);
out.flush();
Socket newClient = sf.createSocket(client, serverName, port, false);
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeData(DataOutputStream out) throws IOException {
char CMD_CHAR_U = 'U';
byte b = (byte) (0x00ff & CMD_CHAR_U);
out.writeByte(b); // <U>
}
private static void writeData2(DataOutputStream out) throws IOException {
char CMD_CHAR_S = 'S';
byte b = (byte) (0x00ff & CMD_CHAR_S);
out.writeByte(b); // <S>
}
private static void readData(DataInputStream in) throws IOException {
char sChar = (char) in.readByte();
System.out.println("<S>\t\t" + sChar);
}
}
Now creating the truststore as shown in the link:
https://jdbc.postgresql.org/documentation/head/ssl-client.html
Steps to create:
openssl x509 -in server.crt -out server.crt.der -outform der
keytool -keystore mystore -alias clientstore -import -file server.crt.der
java -Djavax.net.ssl.trustStore=mystore -Djavax.net.ssl.trustStorePassword=mypassword com.mycompany.MyApp
Note - The server side is using TLSv1 protocol
But still not able to make it through. What am I doing wrong?
What I want is the server to authenticate the crt of the client.
The login protocol with server; the SSL we use is only to authenticate
not to secure the transmission:
-------------------------------------------------------------
client server
-------------------------------------------------------------
sock = connect() sock = accept()
<U><LOGIN_SSL=501>
--------------------------------->
'S'|'E'
<---------------------------------
'S'
--------------------------------->
SSL_connect(sock) SSL_accept(sock)
<R><LOGIN_SSL>
<---------------------------------
I think you have several problems with your setup.
To configure properly the SSL connection with JSSE you need several things depending if you need to authenticate the server, the client, or to perform mutual authentication.
Let's suppose the later and more complete use case of mutual authentication.
The objective is to configure a SSLSocketFactory that you can use to contact your server.
To configure a SSLSocketFactory, you need a SSLContext.
This element in turn with require at least two elements for the mutual authentication use case, a KeyManagerFactory, required for client side SSL authentication, i.e., the server to trust the client, and TrustManagerFactory, required for configuring the client to trust the server.
Both KeyManagerFactory and TrustManagerFactory require a properly configured keystore with the necessary cryptographic material.
So, the first step will consist on generating this cryptographic material.
You already created a keystore with the server certificate:
keytool -keystore serverpublic.keystore -alias clientstore -import -file server.crt.der -storepass yourserverpublickeystorepassword
Please, be aware that, in a similar way as in the server case, you also need to create a public and private key pair for your client, of course, different than the server one.
The related code you provided with OpenSSL and keytool looks appropriate. Please, repeat the process for the client side:
openssl req -new -text -out client.csr
openssl rsa -in clientpriv.pem -out client.key
openssl req -x509 -in client.csr -text -key client.key -out client.crt
// You can use PKCS12 also with Java but it is also ok on this way
openssl pkcs12 -inkey client.key -in client.crt -export -out client.pkcs12
// Do not bother yourself and, in this use case, use always the same password for the key and keystore
keytool -importkeystore -srckeystore client.pkcs12 -srcstoretype PKCS12 -destkeystore client.keystore -storepass "yourclientkeystorepassword"
With the right keystores in place, try something like the following to interact with your server:
// First, let's configure the SSL for client authentication
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(
new FileInputStream("/path/to/client.keystore"),
"yourclientkeystorepassword".toCharArray()
);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); // SunX509
kmf.init(clientKeyStore, "yourclientkeystorepassword".toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
// Now, let's configure the client to trust the server
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(
new FileInputStream("/path/to/serverpublic.keystore"),
"yourserverpublickeystorepassword".toCharArray()
);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // SunX509
tmf.init(serverKeyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null); // You can provide SecureRandom also if you wish
// Create the SSL socket factory and establish the connection
SSLSocketFactory sf = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket)sf.createSocket(serverName, port);
// Interact with your server. Place your code here
// Please, consider the following link for alternatives approaches on how to
// interchange information with the server:
// https://web.mit.edu/java_v1.5.0_22/distrib/share/docs/guide/security/jsse/samples/sockets/client/SSLSocketClient.java
// It also suggest the use of startHandshake explicitly if your are using PrintWriter for the reason explained in the example an in the docs:
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLSocket.html
//...
// Close the socket
socket.close();
The described approach can be extended to use, instead of sockets, higher level of abstraction components like HttpsURLConnection and HTTP clients - with the exception of Apache HttpClient that handles SSL differently - like OkHttp which, under the hood, use SSLSocketFactory and related stuff.
Please, also consider review this great article from IBM's DeveloperWorks, in addition to explain many of the point aforementioned will provide you great guidance with the generation of keystores for your client an server if necessary.
Please, also be aware that, depending on your server code, you may need to configure it to trust the provided client certificate.
According to your comments you are using a server side code similar to the one provided by Postgresql 8.1. Please, see the relevant documentation for configuring SSL in that database, if you are using some similar server side code it maybe could be of help.
Probably the best approach will be to generate a client certificate derived from the root certificate trusted by your server instead of using a self signed one.
I think that it will be also relevant for your server side SSL certificate an associated private key: first, create a root self signed certificate, your CA certificate, configure your server side C code to trust it, and then derive both client and server side SSL cryptographic material from that CA: probably it will simplify your setup and make everything work properly.
I have a Spring Boot application from which I am trying to connect to a MySQL on AWS RDS.
However I am having issues with the following error:
localhost-startStop-1, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
After enabling debug for the ssl handshake, I see that for the CertificateRequest step the Cert Authorities: are empty.
Based on my understanding this is the part where the client (spring boot app) needs to present a cert to the server (mysql db).
Certificate Request step - The server will issue a certificate request from the client.
The next step would be *** Certificate chain, which is the certificate the client is sending to the server. In this case for me, it is sending the content of keyStore_cert.jks.
What I think the issue so far:
The server (mySQL db) does not know about this certificate (my keyStore_cert.jks) that the client (my app) is sending.
But, I was under the impression that the client certs are not required unless you set REQUIRE X509 for the user.
The questions:
Is there something else that I can look in order to find the exact problem?
If the above is the issues, how can I fix it? How can I disable client verification?
Or how can I keep client verification but have it work?
Do I need add anything to the keyStore_cert.jks/trustStore_cert.jks?
These are what my settings and what I have tried.
RDS Engine version: 5.7.22
mysql-connector-java v8.0.13
connection URL: jdbc:mysql://<host>:<port>/<db_name>?useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true
for the user that I am using I executed the following on the DB:
ALTER USER '<my_db_user>'#'%' require SSL;
GRANT USAGE ON <db_name>.* TO '<my_db_user>'#'%' REQUIRE SSL;
Based on the AWS documentation: I imported those certs (root and intermediate) into the trustStore. Doing something: keytool -import -keystore trustStore_cert.jks -storepass <trustStore_password> -alias "awsrds-us-east1" -file rds-ca-2015-us-east-1.pem
I use our own keyStore. Nothing was done to the keyStore at this time.
application trustStore and keyStore are passed as JVM param like the following:
-Djavax.net.ssl.keyStore=path/keyStore_cert.jks
-Djavax.net.ssl.keyStorePassword=<keyStore_password>
-Djavax.net.ssl.trustStore=path/trustStore_cert.jks
-Djavax.net.ssl.trustStorePassword=<trustStore_password>
For the specific user, when I use MySQL Workbench and in the connection setting for Use SSL I specify: If available or Require. It connects with this message:
Host: <host>
Port: <port>
User: <user>
SSL: enabled with DHE-RSA-AES128-SHA
However if I specify, Require and Verify CA or Require and Verify Identity it gives this message: SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY. Which makes sense since i am not specifying anything for the CA file.
Steps for the handshake (will omit some logs.):
*** ClientHello, TLSv1.1 (seems okay)
***
*** ServerHello, TLSv1.1 (seems okay)
***
*** Certificate chain (has the root key)
chain [0] = [
[
Version: V3
Subject: C=US, ST=Washington, L=Seattle, O=Amazon.com, OU=RDS, CN=**<my_rds_name>abcd.us-east-1.rds.amazonaws.com**
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
chain [1] = [ (has the us-east-1 key)
[
Version: V3
Subject: CN=Amazon RDS us-east-1 CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
***
Found trusted certificate:
[
[
Version: V3
Subject: CN=Amazon RDS us-east-1 CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
...
...
*** CertificateRequest (the Authorities are empty)
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
<Empty>
*** ServerHelloDone
matching alias: <my keyStoreAlias>
*** Certificate chain (seems entries from my own key Store)
chain [0] = [
[
Version: V3
Subject: CN=<the CN on my keyStore>, OU=Web Servers, O=Company , C=US
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
***
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1.1 (seems okay)
*** CertificateVerify (seem okay)
*** Finished
verify_data: { 50, 89, 33, 202, 193, 158, 226, 114, 128, 50, 198, 250 }
***
After working with AWS RDS Support I got an explanation why the issue and how to fix it.
Just to clarify, this issue is mainly presented only if you pass your own keystore as part of JVM arguments.
Something like this:
-Djavax.net.ssl.keyStore=path/keyStore_cert.jks
-Djavax.net.ssl.keyStorePassword=<keyStore_password>
RDS is a managed service and they do not have a way (currently) to load specific client certificates into the server.
Meaning they can’t pass a specific certificates in DB configuration level. Usually this would be possible if you were to standup your own MY SQL server. In the config file for that server you can specify the client/server certs.
So, RDS can’t verify the certificate provided by client.
If you were to pass a keystore, which has a key-pair entry (as JVM argument or otherwise), during SSL handshake, the client authentications step would fail. This is expected behavior in database. RDS can’t restrict itself to verify (or not verify) the file loaded in client field for the client during the initial connection. So, if a keystore is passed, server will try to match the certificate keys with existing CA file and if doesn’t match it will not allow the connections.
The solution is to either not pass a keystore at all or pass a blank keystore (one that does not have a key-pair with just Trusted Certificate Entry or one that is just blank).
if you choose to not pass a keystore, then do not specify these properties
-Djavax.net.ssl.keyStore= & -Djavax.net.ssl.keyStorePassword=. And construct the DB connection URL like this:
-Ddb_jdbc_url=jdbc:mysql://<host>:<port>/<db_name>?useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true. You still need to provide the truststore. See at the bottom for more.
Pass a black keystore or (or pass the trust store in the keystore field)
You can pass what ever you want for the keystore and trustore JVM params.
And for the URL you construct it like this:
-Ddb_jdbc_url=jdbc:mysql://<host>:<port>/<db_name>?
useLegacyDatetimeCode=false&verifyServerCertificate=true&useSSL=true&requireSSL=true
&clientCertificateKeyStoreUrl=file:/user/documents/projects/trust-store-rds.jks
&clientCertificateKeyStorePassword=<password>
&clientCertificateKeyStoreType=JKS
&trustCertificateKeyStoreUrl=file:/user/documents/projects/trust-store-rds.jks
&trustCertificateKeyStorePassword=<password>
&trustCertificateKeyStoreType=JKS
Note that for both trustCertificateKeyStoreUrl and clientCertificateKeyStoreUrl I am passing the same file.
Note, you need to also configure all the previous steps:
enable SSL for a user on the DB.
CREATE USER 'my_user'#'%' IDENTIFIED BY 'my_password';
ALTER USER 'my_user'#'%' REQUIRE SSL;
GRANT USAGE ON *.* TO 'my_user'#'%' REQUIRE SSL ;
You need to import the AWS Root and Regional certs into your truststore;
Something like this:
keytool -import -keystore trust-store-rds.jks -storepass changeit -noprompt alias "aws-rds-root" -file rds-ca-2015-root.pem
keytool -import -keystore trust-store-rds.jks -storepass changeit -noprompt -alias "aws-rds-us-east-1" -file rds-ca-2015-us-east-1.pem
Build the URL as above.
Since version 8.0.22 of the MySQL Connector/J driver you can now use the fallbackToSystemKeyStore property set to false to ignore any system level keystore rather than having to create a dummy one.
I've used this method successfully against an RDS MySQL 5.7 instance and where I initially saw the same "unknown_ca" error it now connects perfectly.
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-security.html
We encountered this issue - "Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca" while making connection with AWS RDS 8.X
This error came only when keystore was set at the JVM level. If only trustore is set at the JVM level and not the keystore, this issue do not pop up because truststore trusts RDS certificate already
-Djavax.net.ssl.keyStore=/path/to/your/keystore.jks
-Djavax.net.ssl.keyStorePassword=keystore_password
As RDS is shared service, there is no option in RDS to trust certificates of individual client. If keystore is passed, RDS thinks that the request is for 2 way SSL and it fails. On the other hand, we couldn't get rid of keystore at the JVM level because 2 way SSL was enabled with other backends such as API Gateway
Solution: we provided dummy keystore (trustore itself as it do not contain any private key) to the jdbc to override the keystore that was provided at the JVM level and it worked like a charm!!
public class MySQLSSLTest {
private static final String DB_USER = "username";
private static final String DB_PASSWORD = "password";
// This key store has only the prod root ca.
private static final String KEY_STORE_FILE_PATH = "file-path-to-keystore";
private static final String KEY_STORE_PASS = "keystore-password";
public static void test(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
System.setProperty("javax.net.ssl.trustStore", KEY_STORE_FILE_PATH);
System.setProperty("javax.net.ssl.trustStorePassword", KEY_STORE_PASS);
Properties properties = new Properties();
properties.put("user", DB_USER);
properties.put("password", DB_PASSWORD);
properties.put("sslMode", "DISABLED");
properties.put("useSSL", "false");
properties.put("clientCertificateKeyStoreUrl", "file:/path/to/your/trust/store");
properties.put("clientCertificateKeyStorePassword", "truststore_password");
properties.put("clientCertificateKeyStoreType", "jks");
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://mydatabase.123456789012.us-east-1.rds.amazonaws.com:3306",properties);
stmt = connection.createStatement();
rs=stmt.executeQuery("SELECT 1 from dual");
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return;
}
}
Hopefully this will help you!!!
I am developing a webservice client to access a HTTPS webservice that sits behind a HTTPS load balancer. But am getting an exception.
com.sun.xml.internal.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names present while opening stream from https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names present while opening stream from https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl?wsdl
The HOST and PORT is the IP address and port of Load balancer.
I am able to access this URL from browser - https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl
But not this - https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl?wsdl
I am ignoring the certificates using this code -
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType){}
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
;
}
This is full stack trace:
[ERROR ] SRVE0777E: Exception thrown by application class 'com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex:196'
com.sun.xml.internal.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names present while opening stream from https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names present while opening stream from https://HOST:PORT/itim/services/WSSessionService/WEB-INF/wsdl/WSSessionService.wsdl?wsdl
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:196)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:168)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:133)
at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:272)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:235)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(WSServiceDelegate.java:183)
at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:101)
at javax.xml.ws.Service.<init>(Service.java:89)
at com.ibm.itim.ws.services.WSSessionService_Service.<init>(WSSessionService_Service.java:50)
at examples.ws.GenericWSClient.getSessionService(GenericWSClient.java:150)
at examples.ws.MyServlet.doGet(MyServlet.java:59)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:575)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1240)
at [internal classes]
Create a self signed certificate, like that : keytool -genkey -alias YOUR_ALIAS -keyalg RSA -keystore YOUR_PATH_KS
When it asking you for "first and last name" put your HOST_NAME
Export the certificate from your freshly created key store :
keytool -exportcert -alias -file YOUR_PATH_CERTIFICATE -keystore YOUR_PATH_KS
Import your certificate into the "Trusted Ca Certs"
cd %JAVA_HOME%\jre\lib\security
keytool -import -trustcacerts -keystore cacerts -alias YOUR_HOSTNAME -file YOUR_PATH_CERTIFICATE
Shouldn't your SSLCOntext be initialized with "SSL" instead of "TLS" ?
SSLContext sc = SSLContext.getInstance("SSL");
Also, please try addding a hostname verifier :
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession arg1) {
if (hostname.equals("your host ip")
return true;
return false;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
Note that this is a workaround. What needs to happen is that the certificate has to be generated with CN and DNS name of network with Subject Alternative Name entry i.e. san=ip:your host. which could be the actual solution.
Thanks for your answers, but I was able to fix this issue by simply adding an entry in the hosts file.
ip_address example.com
where example.com is the CN of the certificate
I have a Java web service client, which consumes a web service via HTTPS.
import javax.xml.ws.Service;
#WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
extends Service
{
public ISomeService() {
super(__getWsdlLocation(), ISOMESERVICE_QNAME);
}
When I connect to the service URL (https://AAA.BBB.CCC.DDD:9443/ISomeService ), I get the exception java.security.cert.CertificateException: No subject alternative names present.
To fix it, I first ran openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt and got following content in file certs.txt:
CONNECTED(00000003)
---
Certificate chain
0 s:/CN=someSubdomain.someorganisation.com
i:/CN=someSubdomain.someorganisation.com
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=someSubdomain.someorganisation.com
issuer=/CN=someSubdomain.someorganisation.com
---
No client certificate CA names sent
---
SSL handshake has read 489 bytes and written 236 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 512 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-MD5
Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Session-ID-ctx:
Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Key-Arg : None
Start Time: 1382521838
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---
AFAIK, now I need to
extract the part of certs.txt between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----,
modify it so that the certificate name is equal to AAA.BBB.CCC.DDD and
then import the result using keytool -importcert -file fileWithModifiedCertificate (where fileWithModifiedCertificate is the result of operations 1 and 2).
Is this correct?
If so, how exactly can I make the certificate from step 1 work with IP-based adddress (AAA.BBB.CCC.DDD) ?
Update 1 (23.10.2013 15:37 MSK): In an answer to a similar question, I read the following:
If you're not in control of that server, use its host name (provided
that there is at least a CN matching that host name in the existing
cert).
What exactly does "use" mean?
I fixed the problem by disabling HTTPS checks using the approach presented here:
I put following code into the the ISomeService class:
static {
disableSslVerification();
}
private static void disableSslVerification() {
try
{
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
Since I'm using the https://AAA.BBB.CCC.DDD:9443/ISomeService for testing purposes only, it's a good enough solution, but do not do this in production.
Note that you can also disable SSL for "one connection at a time" ex:
// don't call disableSslVerification but use its internal code:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setHostnameVerifier(allHostsValid);
httpsConn.setSSLSocketFactory(sc.getSocketFactory());
}
This is an old question, yet I had the same problem when moving from JDK 1.8.0_144 to jdk 1.8.0_191
We found a hint in the changelog:
Changelog
we added the following additional system property, which helped in our case to solve this issue:
-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
I've the same problem and solved with this code.
I put this code before the first call to my webservices.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return hostname.equals("localhost"); // or return true
}
});
It's simple and works fine.
Here is the original source.
The verification of the certificate identity is performed against what the client requests.
When your client uses https://xxx.xxx.xxx.xxx/something (where xxx.xxx.xxx.xxx is an IP address), the certificate identity is checked against this IP address (in theory, only using an IP SAN extension).
If your certificate has no IP SAN, but DNS SANs (or if no DNS SAN, a Common Name in the Subject DN), you can get this to work by making your client use a URL with that host name instead (or a host name for which the cert would be valid, if there are multiple possible values). For example, if you cert has a name for www.example.com, use https://www.example.com/something.
Of course, you'll need that host name to resolve to that IP address.
In addition, if there are any DNS SANs, the CN in the Subject DN will be ignored, so use a name that matches one of the DNS SANs in this case.
To import the cert:
Extract the cert from the server, e.g. openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt This will extract certs in PEM format.
Convert the cert into DER format as this is what keytool expects, e.g. openssl x509 -in certs.txt -out certs.der -outform DER
Now you want to import this cert into the system default 'cacert' file. Locate the system default 'cacerts' file for your Java installation. Take a look at How to obtain the location of cacerts of the default java installation?
Import the certs into that cacerts file: sudo keytool -importcert -file certs.der -keystore <path-to-cacerts> Default cacerts password is 'changeit'.
If the cert is issued for an FQDN and you're trying to connect by IP address in your Java code, then this should probably be fixed in your code rather than messing with certificate itself. Change your code to connect by FQDN. If FQDN is not resolvable on your dev machine, simply add it to your hosts file, or configure your machine with DNS server that can resolve this FQDN.
I fixed this issue in a right way by adding the subject alt names in certificate rather than making any changes in code or disabling SSL unlike what other answers suggest here. If you see clearly the exception says the "Subject alt names are missing" so the right way should be to add them
Please look at this link to understand step by step.
The above error means that your JKS file is missing the required domain on which you are trying to access the application.You will need to Use Open SSL and the key tool to add multiple domains
Copy the openssl.cnf into a current directory
echo '[ subject_alt_name ]' >> openssl.cnf
echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster#example.com' -days 365
Export the public key (.pem) file to PKS12 format. This will prompt you for password
openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
self-signed.pem -inkey private.key -name myalias -out keystore.p12
Create a.JKS from self-signed PEM (Keystore)
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
Generate a Certificate from above Keystore or JKS file
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
Since the above certificate is Self Signed and is not validated by CA, it needs to be added in Truststore(Cacerts file in below location for MAC, for Windows, find out where your JDK is installed.)
sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
Original answer posted on this link here.
You may not want to disable all ssl Verificatication and so you can just disable the hostName verification via this which is a bit less scary than the alternative:
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[EDIT]
As mentioned by conapart3 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER is now deprecated, so it may be removed in a later version, so you may be forced in the future to roll your own, although I would still say I would steer away from any solutions where all verification is turned off.
my problem with getting this error was resolved by using the full URL "qatest.ourCompany.com/webService" instead of just "qatest/webService". Reason was that our security certificate had a wildcard i.e. "*.ourCompany.com". Once I put in the full address the exception went away. Hope this helps.
As some one pointed before, I added the following code (with lambda) just before creating the RestTemplate object, and it works fine. IT is only for my internal testing class, so I will work around with a better solution for the production code.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
(hostname, sslSession) -> true);
We faced a similar issue recently "No subject alternative DNS name matching found", it was a nightmare because we were able to reproduce it only in Production servers, were access to debug is near to zero. The rest of environments were just working fine. Our stack was JDK 1.8.x+, JBoss EAP 7+, Java Spring Boot app and Okta as identity provider (the SSL handshake was failing when recovering the well-known configuration from Okta, where okta is available in AWS Cloud - virtual servers).
Finally, we discover that (no one knows why) the JBoss EAP application server that we were using it was having an additional JVM System Property:
jsse.enableSNIExtension = false
This was preventing to establish TLS connection and we were able to reproduce the issue by adding that same system property/value in other environments. So the solution was simple to remove that undesired property and value.
As per Java Security Doc, this property is set by default to true for Java 7+ (refer to https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization)
jsse.enableSNIExtension system property. Server Name Indication (SNI) is a TLS extension, defined in RFC 4366. It enables TLS connections to virtual servers, in which multiple servers for different network names are hosted at a single underlying network address.
Some very old SSL/TLS vendors may not be able handle SSL/TLS extensions. In this case, set this property to false to disable the SNI extension.
Have answered it already in https://stackoverflow.com/a/53491151/1909708.
This fails because neither the certificate common name (CN in certification Subject) nor any of the alternate names (Subject Alternative Name in the certificate) match with the target hostname or IP adress.
For e.g., from a JVM, when trying to connect to an IP address (WW.XX.YY.ZZ) and not the DNS name (https://stackoverflow.com), the HTTPS connection will fail because the certificate stored in the java truststore cacerts expects common name (or certificate alternate name like stackexchange.com or *.stackoverflow.com etc.) to match the target address.
Please check: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#HostnameVerifier
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL("https://WW.XX.YY.ZZ/api/verify").openConnection();
urlConnection.setSSLSocketFactory(socketFactory());
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setUseCaches(false);
urlConnection.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
});
urlConnection.getOutputStream();
Above, passed an implemented HostnameVerifier object which is always returns true:
new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
For Spring Boot RestTemplate:
add org.apache.httpcomponents.httpcore dependency
use NoopHostnameVerifier for SSL factory:
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(new URL("file:pathToServerKeyStore"), storePassword)
// .loadKeyMaterial(new URL("file:pathToClientKeyStore"), storePassword, storePassword)
.build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
RestTemplate restTemplate = new RestTemplate(factory);
This code will work like charm and use the restTemple object for rest of the code.
RestTemplate restTemplate = new RestTemplate();
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {
return true;
}
};
SSLContext sslContext = null;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate.setRequestFactory(requestFactory);
}
I also faced the same issue with a self signed certificate . By referring to few of the above solutions , i tried regenerating the certificate with the correct CN i.e the IP Address of the server .But still it didn't work for me .
Finally i tried regenerating the certificate by adding the SAN address to it via the below mentioned command
**keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -alias <IP_ADDRESS> -ext san=ip:<IP_ADDRESS>**
After that i started my server and downloaded the client certificates via the below mentioned openssl command
**openssl s_client -showcerts -connect <IP_ADDRESS>:443 < /dev/null | openssl x509 -outform PEM > myCert.pem**
Then i imported this client certificate to the java default keystore file (cacerts) of my client machine by the below mentioned command
**keytool -import -trustcacerts -keystore /home/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.el7.x86_64/jre/lib/security/cacerts -alias <IP_ADDRESS> -file ./mycert.pem**
I got to this question after if got this same error message. However in my case we had two URL's with different subdomains (http://example1.xxx.com/someservice and http://example2.yyy.com/someservice) which were directed to the same server. This server was having only one wildcard certificate for the *.xxx.com domain. When using the service via the second domain, the found certicate (*.xxx.com) does not match with the requested domain (*.yyy.com) and the error occurs.
In this case we should not try to fix such an errormessage by lowering SSL security, but should check the server and certificates on it.
I was going through 2 way SSL in springboot. I have made all correct configuration service tomcat server and service caller RestTemplate. but I was getting error as "java.security.cert.CertificateException: No subject alternative names present"
After going through solutions, I found, JVM needs this certificate otherwise it gives handshaking error.
Now, how to add this to JVM.
go to jre/lib/security/cacerts file. we need to add our server certificate file to this cacerts file of jvm.
Command to add server cert to cacerts file via command line in windows.
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -import -noprompt -trustcacerts -alias sslserver -file E:\spring_cloud_sachin\ssl_keys\sslserver.cer -keystore cacerts -storepass changeit
Check server cert is installed or not:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -list -keystore cacerts
you can see list of certificates installed:
for more details: https://sachin4java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html
add the host entry with the ip corresponding to the CN in the certificate
CN=someSubdomain.someorganisation.com
now update the ip with the CN name where you are trying to access the url.
It worked for me.
When you have a certificate with both CN and Subject Alternative Names (SAN), if you make your request based on the CN content, then that particular content must also be present under SAN, otherwise it will fail with the error in question.
In my case CN had something, SAN had something else. I had to use SAN URL, and then it worked just fine.
I have resolved the said
MqttException (0) - javax.net.ssl.SSLHandshakeException: No
subjectAltNames on the certificate match
error by adding one (can add multiple) alternative subject name in the server certificate (having CN=example.com) which after prints the part of certificate as below:
Subject Alternative Name:
DNS: example.com
I used KeyExplorer on windows for generating my server certificate.
You can follow this link for adding alternative subject names (follow the only part for adding it).
I was referred to animo3991's answer and tweaked it to make my Bitbucket Backup Client 3.6.0 work for backing up my Bitbucket Server when before it was also hitting No subject alternative names present error.
The first command however must use alias tomcat, otherwise Bitbucket Server would not start up properly:
keytool -genkey -keyalg RSA -sigalg SHA256withRSA -keystore keystore.jks -keysize 2048 -alias tomcat -ext san=ip:<IP_ADDRESS>
openssl s_client -showcerts -connect <IP_ADDRESS>:443 < /dev/null | openssl x509 -outform PEM > myCert.pem
keytool -import -trustcacerts -keystore /etc/pki/ca-trust/extracted/java/cacerts -alias <IP_ADDRESS> -file ./myCert.pem
public class RESTfulClientSSL {
static TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
#Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
}};
public class NullHostNameVerifier implements HostnameVerifier {
/*
* (non-Javadoc)
*
* #see javax.net.ssl.HostnameVerifier#verify(java.lang.String,
* javax.net.ssl.SSLSession)
*/
#Override
public boolean verify(String arg0, SSLSession arg1) {
// TODO Auto-generated method stub
return true;
}
}
public static void main(String[] args) {
HttpURLConnection connection = null;
try {
HttpsURLConnection.setDefaultHostnameVerifier(new RESTfulwalkthroughCer().new NullHostNameVerifier());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
String uriString = "https://172.20.20.12:9443/rest/hr/exposed/service";
URL url = new URL(uriString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
//connection.setRequestMethod("POST");
BASE64Encoder encoder = new BASE64Encoder();
String username = "admin";
String password = "admin";
String encodedCredential = encoder.encode((username + ":" + password).getBytes());
connection.setRequestProperty("Authorization", "Basic " + encodedCredential);
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
String content = stringBuffer.toString();
System.out.println(content);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
Add your IP address in the hosts file.which is in the folder of C:\Windows\System32\drivers\etc.
Also add IP and Domain Name of the IP address.
example:
aaa.bbb.ccc.ddd abc#def.com
I'm building a small program that acts as an XMPP client and I am using the Smack library. Now, the server I am connecting to requires SSL (in Pidgin I have to check "Force old (port 5223) SSL"). I'm having trouble getting Smack to connect to this server. Is it possible?
Take a look at this thread.
http://www.igniterealtime.org/community/thread/37678
Essentially, you need to add these two lines to your code:
connConfig.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
connConfig.setSocketFactory(new DummySSLSocketFactory());
where connConfig is your ConnectionConfiguration object. Get the DummySSLSocketFactory from the Spark source code repository. All it does is accept virtually any certificate. This seemed to work for me. Good luck!
You can achieve this by the following:
Storing the CA Certificate in Keystore
To store the certificate in a Keystore follow these steps.
Step 1: Download the bouncycastle JAR file. It can be downloaded from the here: Bouncy Castle JAVA Releases
Step 2: Use the following command to store the certificate in keystore
keytool -importcert -v -trustcacerts -file "<certificate_file_with_path>" -alias "<some_name_for_certificate>" -keystore "<file_name_for_the_output_keystore>" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "<bouncy_castle_jar_file_with_path>" -storetype BKS -storepass "<password_for_the_keystore>"
Step 3: Verify the keystore file
keytool -importcert -v -list -keystore "<file_name_for_the_keystore_with_path>" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "<bouncy_castle_jar_file_with_path>" -storetype BKS -storepass "<password_for_the_keystore>"
This shall list us the certificate included in the keystore.
We have a keystore which we can use in our code.
Using the keystore
After generating this keystore, save it in the raw folder of your application. The use the below code to get the certificate handshake with the openfire server.
To create a connection with openfire using XMPP, you may need to get the config. For the same, use the below method:
public ConnectionConfiguration getConfigForXMPPCon(Context context) {
ConnectionConfiguration config = new ConnectionConfiguration(URLConstants.XMPP_HOST, URLConstants.XMPP_PORT);
config.setSASLAuthenticationEnabled(false);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
config.setCompressionEnabled(false);
SSLContext sslContext = null;
try {
sslContext = createSSLContext(context);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}
config.setCustomSSLContext(sslContext);
config.setSocketFactory(sslContext.getSocketFactory());
return config;
}
private SSLContext createSSLContext(Context context) throws KeyStoreException,
NoSuchAlgorithmException, KeyManagementException, IOException, CertificateException {
KeyStore trustStore;
InputStream in = null;
trustStore = KeyStore.getInstance("BKS");
if (StringConstants.DEV_SERVER_IP.equals(URLConstants.XMPP_HOST) || StringConstants.TEST_SERVER_IP.equals(URLConstants.XMPP_HOST))
in = context.getResources().openRawResource(R.raw.ssl_keystore_dev_test);
else if(StringConstants.STAGE_SERVER_IP.equals(URLConstants.XMPP_HOST) || StringConstants.STAGE2_SERVER_IP.equals(URLConstants.XMPP_HOST))
in = context.getResources().openRawResource(R.raw.ssl_keystore_stage);
else if(StringConstants.PROD_SERVER_IP.equals(URLConstants.XMPP_HOST) || StringConstants.PROD1_SERVER_IP.equals(URLConstants.XMPP_HOST))
in = context.getResources().openRawResource(R.raw.ssl_keystore_prod);
trustStore.load(in, "<keystore_password>".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(),
new SecureRandom());
return sslContext;
}
All done..!! Just connect.. Now your connection is secured.
All follow the same in my blog at smackssl.blogspot.in
Yes, it's quite easy to achieve. Take a look at the ConnectionConfiguration class, and in particular the setSecurityMode method which accepts a ConnectionConfiguration.SecurityMode enum as a parameter. Setting this to "required" forces Smack to use TLS.
from the Javadoc:
Securirty via TLS encryption is
required in order to connect. If the
server does not offer TLS or if the
TLS negotiaton fails, the connection
to the server will fail.