Problem Connecting a Java Client (JMS) to a IBM MQ - java

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.

Related

Loading pk12 file within the application in Wildfly 16

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

NoHostAvailableException :: Java to Cassandra SSL Cluster connection failed

I am trying to connect to a 3 node Cassandra cluster from a Java client program, with Cassandra cluster configured with Client to node encryption enabled. I deployed three self signed certificate in all three nodes, and imported the public certificates to each of other nodes as per the document Client-to-node encryption. When I run the client program I am getting the following exception.
Exception in thread "main" com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: clm-pun-swpry4/10.133.181.157:9042 (com.datastax.driver.core.exceptions.TransportException: [clm-pun-swpry4/10.133.181.157:9042] Channel has been closed), clm-pun-swpryf/10.133.181.156:9042 (com.datastax.driver.core.exceptions.TransportException: [clm-pun-swpryf/10.133.181.156:9042] Channel has been closed), clm-pun-sqbgda/10.133.172.70:9042 (com.datastax.driver.core.exceptions.TransportException: [clm-pun-sqbgda/10.133.172.70:9042] Channel has been closed))
at com.datastax.driver.core.ControlConnection.reconnectInternal(ControlConnection.java:233)
at com.datastax.driver.core.ControlConnection.connect(ControlConnection.java:79)
at com.datastax.driver.core.Cluster$Manager.init(Cluster.java:1483)
at com.datastax.driver.core.Cluster.init(Cluster.java:159)
at com.datastax.driver.core.SessionManager.initAsync(SessionManager.java:78)
at com.datastax.driver.core.SessionManager.executeAsync(SessionManager.java:139)
at com.datastax.driver.core.AbstractSession.execute(AbstractSession.java:68)
at com.datastax.driver.core.AbstractSession.execute(AbstractSession.java:43)
at clm.bmc.saas.incubator.ClientToNodeExample.main(ClientToNodeExample.java:28)
My Cassandra configuration in cassandra.yaml (individual self signed certificate for each of the 3 nodes):
client_encryption_options:
enabled: true
# If enabled and optional is set to true encrypted and unencrypted connections are handled.
optional: false
keystore: /opt/secure/keystore.clm-pun-swpry4
keystore_password: changeit
# require_client_auth: false
# Set trustore and truststore_password if require_client_auth is true
# truststore: conf/.truststore
# truststore_password: cassandra
# More advanced defaults below:
protocol: TLSv1.2
algorithm: SunX509
store_type: JKS
cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
My Java program:
public class ClientToNodeExample {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientToNodeExample.class);
public static void main(String[] args) {
ClientToNodeExample example = new ClientToNodeExample();
Session session = example.getCluster("C:\\install\\ssl\\cassandraCluster.ks", "changeit",
new String[]{"clm-pun-sqbgda", "clm-pun-swpryf", "clm-pun-swpry4"}, 9042).newSession();
ResultSet results = session.execute("SELECT * FROM entity_space.mo;");
LOGGER.info("NumberOfRows:" + results.all().size());
session.close();
}
private Cluster getCluster(String trustStoreLocation, String trustStorePassword, String[] host, int port) {
Cluster cluster;
SSLContext sslcontext = null;
try {
InputStream is = ClientToNodeExample.class.getResourceAsStream(trustStoreLocation);
KeyStore keystore = KeyStore.getInstance("jks");
char[] pwd = trustStorePassword.toCharArray();
keystore.load(is, pwd);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
TrustManager[] tm = tmf.getTrustManagers();
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, tm, null);
} catch (Exception e) {
LOGGER.error("ERROR", e);
}
JdkSSLOptions sslOptions = JdkSSLOptions.builder().withSSLContext(sslcontext).build();
cluster = Cluster.builder().addContactPoints(host).withPort(port).withSSL(sslOptions).build();
return cluster;
}
}
However if I try to check the certificate from each node with the following keytool command, I get the certificate:
keytool -printcert -sslserver clm-pun-sqbgda:9042 -rfc
Can anyone please help where I am going wrong?
Cassandra version:3.11.0
cassandra-driver-core : 3.1.4
First I would try to connect via cqlsh --ssl (you need to do some extra steps):
convert server certificate to PKCS12 format
convert PKCS12 to PEM
modify cqlshrc file in order to use the PEM certificate.
If this is ok, then the setup is correct and I would remove the sslOptions and provide
-Djavax.net.ssl.trustStore
-Djavax.net.ssl.trustStorePassword
-Djavax.net.debug=ssl.
If this is works also, then the issue is in your getCluster method.
My guess is that your keystore (cassandraCluster.ks) is not loaded correctly.
I successfully executed your code as maven project, with the keystore in the resources folder and loaded with
cluster = getCluster("/client-truststore.jks", "changeit", new String[]{"127.0.0.1"}, 9042);

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

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

Example of using ssl with org.apache.http.impl.bootstrap.HttpServer from Apache HttpCore 4.4.3

I have successfully created an embedded HttpServer using the example at https://hc.apache.org.httpcomponents-core-ga/httpcore/examples/org/apache/http/examples/HttpFileServer.java, which handles http: traffic just fine. Now I would like to extend this to support TLS.
I copied some likely looking code from here: https://hc.apache.org/httpcomponents-core-ga/tutorial/html/blocking-io.html#d5e455
But some parts of it are showing up as deprecated, which makes me wonder whether this example is out of date and whether the tree I'm at is the one where I should be barking. Even if that weren't the case, I am having trouble finding the relationship between HttpServer and DefaultBHttpClientConnection (mentioned in the example). I suspect that I should be using DefaultBHttpServerConnection but I am so far unable to find that either.
Is there a newer example of this anywhere?
Michael D. Spence
Mockingbird Data Systems, Inc.
I am not sure understand the problem you are having. All you need is to provide a correctly initialized SSLContext instance to ServerBoostrap . HttpCore ships with SSLContextBuilder specifically designed to simplify the process of SSLContext initialization.
The example included in HttpCore distribution pretty much shows every step required to set up SSL/TLS transport layer.
SSLContext sslcontext = null;
if (port == 8443) {
// Initialize SSL context
URL url = HttpFileServer.class.getResource("/my.keystore");
if (url == null) {
System.out.println("Keystore not found");
System.exit(1);
}
sslcontext = SSLContexts.custom()
.loadKeyMaterial(url, "secret".toCharArray(), "secret".toCharArray())
.build();
}
SocketConfig socketConfig = SocketConfig.custom()
.setSoTimeout(15000)
.setTcpNoDelay(true)
.build();
final HttpServer server = ServerBootstrap.bootstrap()
.setListenerPort(port)
.setServerInfo("Test/1.1")
.setSocketConfig(socketConfig)
.setSslContext(sslcontext)
.setExceptionLogger(new StdErrorExceptionLogger())
.registerHandler("*", new HttpFileHandler(docRoot))
.create();
server.start();

SSL handshake alert: unrecognized_name error since upgrade to Java 1.7.0

I upgraded from Java 1.6 to Java 1.7 today.
Since then an error occur when I try to establish a connection to my webserver over SSL:
javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
at sun.security.ssl.ClientHandshaker.handshakeAlert(ClientHandshaker.java:1288)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1904)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1027)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1262)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1289)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1273)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at java.net.URL.openStream(URL.java:1035)
Here is the code:
SAXBuilder builder = new SAXBuilder();
Document document = null;
try {
url = new URL(https://some url);
document = (Document) builder.build(url.openStream());
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(DownloadLoadiciousComputer.class.getName()).log(Level.SEVERE, null, ex);
}
Its only a test project thats why I allow and use untrusted certificates with the code:
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
Logger.getLogger(DownloadManager.class.getName()).log(Level.SEVERE, null, e);
}
I sucessfully tried to connect to https://google.com.
where is my fault?
Thanks.
Java 7 introduced SNI support which is enabled by default. I have found out that certain misconfigured servers send an "Unrecognized Name" warning in the SSL handshake which is ignored by most clients... except for Java. As #Bob Kerns mentioned, the Oracle engineers refuse to "fix" this bug/feature.
As workaround, they suggest to set the jsse.enableSNIExtension property. To allow your programs to work without re-compiling, run your app as:
java -Djsse.enableSNIExtension=false yourClass
The property can also be set in the Java code, but it must be set before any SSL actions. Once the SSL library has loaded, you can change the property, but it won't have any effect on the SNI status. To disable SNI on runtime (with the aforementioned limitations), use:
System.setProperty("jsse.enableSNIExtension", "false");
The disadvantage of setting this flag is that SNI is disabled everywhere in the application. In order to make use of SNI and still support misconfigured servers:
Create a SSLSocket with the host name you want to connect to. Let's name this sslsock.
Try to run sslsock.startHandshake(). This will block until it is done or throw an exception on error. Whenever an error occurred in startHandshake(), get the exception message. If it equals to handshake alert: unrecognized_name, then you have found a misconfigured server.
When you have received the unrecognized_name warning (fatal in Java), retry opening a SSLSocket, but this time without a host name. This effectively disables SNI (after all, the SNI extension is about adding a host name to the ClientHello message).
For the Webscarab SSL proxy, this commit implements the fall-back setup.
I had what I believe the same issue is.
I found that I needed to adjust the Apache configuration to include a ServerName or ServerAlias for the host.
This code failed:
public class a {
public static void main(String [] a) throws Exception {
java.net.URLConnection c = new java.net.URL("https://mydomain.com/").openConnection();
c.setDoOutput(true);
c.getOutputStream();
}
}
And this code worked:
public class a {
public static void main(String [] a) throws Exception {
java.net.URLConnection c = new java.net.URL("https://google.com/").openConnection();
c.setDoOutput(true);
c.getOutputStream();
}
}
Wireshark revealed that during the TSL/SSL Hello the warning
Alert (Level: Warning, Description: Unrecognized Name), Server Hello
Was being sent from the server to the client.
It was only a warning, however, Java 7.1 then responded immediately back with a "Fatal, Description: Unexpected Message", which I assume means the Java SSL libraries don't like to see the warning of unrecognized name.
From the Wiki on Transport Layer Security (TLS):
112 Unrecognized name warning TLS only; client's Server Name Indicator specified a hostname not supported by the server
This led me to look at my Apache config files and I found that if I added a ServerName or ServerAlias for the name sent from the client/java side, it worked correctly without any errors.
<VirtualHost mydomain.com:443>
ServerName mydomain.com
ServerAlias www.mydomain.com
You can disable sending SNI records with the System property jsse.enableSNIExtension=false.
If you can change the code it helps to use SSLCocketFactory#createSocket() (with no host parameter or with a connected socket). In this case it will not send a server_name indication.
We also ran into this error on a new Apache server build.
The fix in our case was to define a ServerAlias in the httpd.conf that corresponded to the host name that Java was trying to connect to. Our ServerName was set to the internal host name. Our SSL cert was using the external host name, but that was not sufficient to avoid the warning.
To help debug, you can use this ssl command:
openssl s_client -servername <hostname> -connect <hostname>:443 -state
If there is a problem with that hostname, then it will print this message near the top of the output:
SSL3 alert read: warning:unrecognized name
I should also note that we did not get that error when using that command to connect to the internal host name, even though it did not match the SSL cert.
Instead of relying on the default virtual host mechanism in apache, you can define one last catchall virtualhost that uses an arbitrary ServerName and a wildcard ServerAlias, e.g.
ServerName catchall.mydomain.com
ServerAlias *.mydomain.com
In that way you can use SNI and apache will not send back the SSL warning.
Of course, this only works if you can describe all of your domains easily using a wildcard syntax.
It should be useful. To retry on a SNI error in Apache HttpClient 4.4 - the easiest way we came up with (see HTTPCLIENT-1522):
public class SniHttpClientConnectionOperator extends DefaultHttpClientConnectionOperator {
public SniHttpClientConnectionOperator(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
super(socketFactoryRegistry, null, null);
}
#Override
public void connect(
final ManagedHttpClientConnection conn,
final HttpHost host,
final InetSocketAddress localAddress,
final int connectTimeout,
final SocketConfig socketConfig,
final HttpContext context) throws IOException {
try {
super.connect(conn, host, localAddress, connectTimeout, socketConfig, context);
} catch (SSLProtocolException e) {
Boolean enableSniValue = (Boolean) context.getAttribute(SniSSLSocketFactory.ENABLE_SNI);
boolean enableSni = enableSniValue == null || enableSniValue;
if (enableSni && e.getMessage() != null && e.getMessage().equals("handshake alert: unrecognized_name")) {
TimesLoggers.httpworker.warn("Server received saw wrong SNI host, retrying without SNI");
context.setAttribute(SniSSLSocketFactory.ENABLE_SNI, false);
super.connect(conn, host, localAddress, connectTimeout, socketConfig, context);
} else {
throw e;
}
}
}
}
and
public class SniSSLSocketFactory extends SSLConnectionSocketFactory {
public static final String ENABLE_SNI = "__enable_sni__";
/*
* Implement any constructor you need for your particular application -
* SSLConnectionSocketFactory has many variants
*/
public SniSSLSocketFactory(final SSLContext sslContext, final HostnameVerifier verifier) {
super(sslContext, verifier);
}
#Override
public Socket createLayeredSocket(
final Socket socket,
final String target,
final int port,
final HttpContext context) throws IOException {
Boolean enableSniValue = (Boolean) context.getAttribute(ENABLE_SNI);
boolean enableSni = enableSniValue == null || enableSniValue;
return super.createLayeredSocket(socket, enableSni ? target : "", port, context);
}
}
and
cm = new PoolingHttpClientConnectionManager(new SniHttpClientConnectionOperator(socketFactoryRegistry), null, -1, TimeUnit.MILLISECONDS);
Use:
System.setProperty("jsse.enableSNIExtension", "false");
Restart your Tomcat (important)
Ran into this issue with spring boot and jvm 1.7 and 1.8. On AWS, we did not have the option to change the ServerName and ServerAlias to match (they are different) so we did the following:
In build.gradle we added the following:
System.setProperty("jsse.enableSNIExtension", "false")
bootRun.systemProperties = System.properties
That allowed us to bypass the issue with the "Unrecognized Name".
You cannot supply system properties to the jarsigner.exe tool, unfortunately.
I have submitted defect 7177232, referencing #eckes' defect 7127374 and explaining why it was closed in error.
My defect is specifically about the impact on the jarsigner tool, but perhaps it will lead them to reopening the other defect and addressing the issue properly.
UPDATE: Actually, it turns out that you CAN supply system properties to the Jarsigner tool, it's just not in the help message. Use jarsigner -J-Djsse.enableSNIExtension=false
I hit the same problem and it turned out that reverse dns was not setup correct, it pointed to wrong hostname for the IP. After I correct reverse dns and restart httpd, the warning is gone.
(if I don't correct reverse dns, adding ServerName did the trick for me as well)
My VirtualHost's ServerName was commented out by default. It worked after uncommenting.
If you are building a client with Resttemplate, you can only set the endpoint like this: https://IP/path_to_service and set the requestFactory.
With this solution you don't need to RESTART your TOMCAT or Apache:
public static HttpComponentsClientHttpRequestFactory requestFactory(CloseableHttpClient httpClient) {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = null;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
final SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,hostnameVerifier);
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", csf)
.build();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(100);
httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.setConnectionManager(cm)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
I have also come across this issue whilst upgrading from Java 1.6_29 to 1.7.
Alarmingly, my customer has discovered a setting in the Java control panel which resolves this.
In the Advanced Tab you can check 'Use SSL 2.0 compatible ClientHello format'.
This seems to resolve the issue.
We are using Java applets in an Internet Explorer browser.
Hope this helps.
Here is solution for Appache httpclient 4.5.11. I had problem with cert which has subject wildcarded *.hostname.com. It returned me same exception, but I musn't use disabling by property System.setProperty("jsse.enableSNIExtension", "false"); because it made error in Google location client.
I found simple solution (only modifying socket):
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import javax.inject.Named;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.util.List;
#Factory
public class BeanFactory {
#Bean
#Named("without_verify")
public HttpClient provideHttpClient() {
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(SSLContexts.createDefault(), NoopHostnameVerifier.INSTANCE) {
#Override
protected void prepareSocket(SSLSocket socket) throws IOException {
SSLParameters parameters = socket.getSSLParameters();
parameters.setServerNames(List.of());
socket.setSSLParameters(parameters);
super.prepareSocket(socket);
}
};
return HttpClients.custom()
.setSSLSocketFactory(connectionSocketFactory)
.build();
}
}
I had the same problem with an Ubuntu Linux server running subversion when accessed via Eclipse.
It has shown that the problem had to do with a warning when Apache (re)started:
[Mon Jun 30 22:27:10 2014] [warn] NameVirtualHost *:80 has no VirtualHosts
... waiting [Mon Jun 30 22:27:11 2014] [warn] NameVirtualHost *:80 has no VirtualHosts
This has been due to a new entry in ports.conf, where another NameVirtualHost directive was entered alongside the directive in sites-enabled/000-default.
After removing the directive in ports.conf, the problem had vanished (after restarting Apache, naturally)
Just to add a solution here. This might help for LAMP users
Options +FollowSymLinks -SymLinksIfOwnerMatch
The above mentioned line in the virtual host configuration was the culprit.
Virtual Host Configuration when error
<VirtualHost *:80>
DocumentRoot /var/www/html/load/web
ServerName dev.load.com
<Directory "/var/www/html/load/web">
Options +FollowSymLinks -SymLinksIfOwnerMatch
AllowOverride All
Require all granted
Order Allow,Deny
Allow from All
</Directory>
RewriteEngine on
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [NC,R=301,L]
</VirtualHost>
Working Configuration
<VirtualHost *:80>
DocumentRoot /var/www/html/load/web
ServerName dev.load.com
<Directory "/var/www/html/load/web">
AllowOverride All
Options All
Order Allow,Deny
Allow from All
</Directory>
# To allow authorization header
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
# RewriteCond %{SERVER_PORT} !^443$
# RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [NC,R=301,L]
</VirtualHost>
There is an easier way where you can just use your own HostnameVerifier to implicitly trust certain connections. The issue comes with Java 1.7 where SNI extensions have been added and your error is due to a server misconfiguration.
You can either use "-Djsse.enableSNIExtension=false" to disable SNI across the whole JVM or read my blog where I explain how to implement a custom verifier on top of a URL connection.

Categories

Resources