Unable to Extract trust manager on IBM JRE8 with OKHTTP3 - okhttp

Can Any one help on this please.
I am also using OKHTTP3 version 4.8.1 to write HTTP2 client . Its Working on Oracle JDK 8 but not working on IBM JRE 8.
Error message:
java.lang.IllegalStateException: Unable to extract the trust manager on okhttp3.internal.Platform#e85a0ce8, sslSocketFactory is class com.ibm.jsse2.SSLSocketFactoryImpl.
Thank you

You are relying on a long deprecated method
https://github.com/square/okhttp/blob/cd722373281202492043f4294fccfe6f691ddc01/okhttp/src/main/kotlin/okhttp3/OkHttpClient.kt#L741
It's deprecated because it had to assume a lot about the JVM, that breaks on each JVM update or across vendors. You should instead call the method with X509TrustManager as a parameter
https://github.com/square/okhttp/blob/cd722373281202492043f4294fccfe6f691ddc01/okhttp/src/main/kotlin/okhttp3/OkHttpClient.kt#L767
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();

Related

ECDSA related ciphers not working with Spring Cloud Gateway and OpenSSL

I am currently trying to make ECDSA related ciphers to work with TLS 1.2 in Spring Cloud Gateway (Spring Boot Parent 2.6.7 and Spring Cloud 2021.0.2). Here's the snippet of WebServerFactoryCustomizer
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer() {
return factory -> factory.addServerCustomizers(httpServer -> httpServer.secure(sslContextSpec -> {
try {
Ssl ssl = factory.getSsl();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] keyStorePassword = ssl.getKeyStorePassword().toCharArray();
keyStore.load(resourceLoader.getResource(ssl.getKeyStore()).getInputStream(), keyStorePassword);
KeyManagerFactory keyManagerFactory = OpenSslCachingX509KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword);
Http11SslContextSpec http11SslContextSpec = Http11SslContextSpec.forServer(keyManagerFactory)
.configure(sslContextBuilder -> {
sslContextBuilder.sslProvider(SslProvider.OPENSSL);
sslContextBuilder.ciphers(Arrays.asList(ssl.getCiphers()));
sslContextBuilder.protocols(ssl.getEnabledProtocols());
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
sslContextBuilder.clientAuth(ClientAuth.REQUIRE);
});
sslContextSpec.sslContext(http11SslContextSpec)
.handlerConfigurator(sslHandler -> {
sslHandler.setCloseNotifyReadTimeout(18000, TimeUnit.MILLISECONDS);
sslHandler.setHandshakeTimeout(19000, TimeUnit.MILLISECONDS);
SSLParameters sslParameters = sslHandler.engine().getSSLParameters();
sslParameters.setUseCipherSuitesOrder(false);
sslHandler.engine().setSSLParameters(sslParameters);
});
} catch (UnrecoverableKeyException | IOException | CertificateException | KeyStoreException |
NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}));
}
When I try to connect using openssl s_client with ECDHE-ECDSA-AES128-GCM-SHA256 cipher the server returns an error with no shared ciphers, but I do have it in the configuration as
server.ssl.ciphers=TLS_RSA_WITH_AES_128_GCM_SHA256,\
TLS_RSA_WITH_AES_256_GCM_SHA384, \
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
server.ssl.enabled-protocols=TLSv1.2
This behavior was observed when I upgraded versions from Spring Boot 2.3.3.RELEASE and Spring Cloud Hoxton.SR7. Any advice/suggestions would be of great help on fixing or correctly configuring it.

Spring-ws security header to load configurations for multiple cert

I am trying to invoke a SOAP webservice in my spring boot application using spring-ws with a keystore which has multiple certs. The configuration always defaults to single cert.
Sample code below:
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
Merlin merlin = new Merlin();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = new FileInputStream(ResourceUtils.getFile(("keystore.jks")));
keyStore.load(inputStream, "tester".toCharArray());
merlin.setKeyStore(keyStore);
wss4jSecurityInterceptor.setSecurementSignatureCrypto(merlin);
wss4jSecurityInterceptor.setSecurementUsername("test");
wss4jSecurityInterceptor.setSecurementPassword("");
webServiceTemplate.setInterceptors(new org.springframework.ws.client.support.interceptor.ClientInterceptor[]
{wss4jSecurityInterceptor});
When i checked the source code of the apache library class WSSecSignature class. I see there is a configuration for picking up multiple cert. But am not sure how to set the singleCert to be false in the wss4jSecurityInterceptor. It always goes to the else block in the below logic
if (!this.useSingleCert) {
this.secRef.addTokenType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
} else {
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
}
Is there a config i need to set while setting the keystore to Merin object, to make the useSingleCert as false?
Found a work around to override the Wss4jSecurityInterceptor, set the property to false and use the extended interceptor
class SecurityInterceptor extends Wss4jSecurityInterceptor
{
#Override
protected RequestData initializeRequestData(MessageContext messageContext) {
messageContext.setProperty(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
return super.initializeRequestData(messageContext);
}
}

Micrometer with Elasticsearch over SSL

I'm trying to use Micrometer with Elasticsearch over SSL.
I use Micrometer in version 1.8.0, Elasticsearch in version 7.16.3 and OpenJDK 11.0.2 .
Because I know that it's not possible to use a built-in configuration (link) I tried to inject a custom HttpUrlConnectionSender as in the following class SecureHttpSender:
public class SecureHttpSender extends HttpUrlConnectionSender {
...
public SecureHttpSender(ElasticProperties properties, SecureElasticProperties secureElasticProperties) {
super(properties.getConnectTimeout(), properties.getReadTimeout());
this.secureElasticProperties = secureElasticProperties;
this.sslSocketFactory = buildSslSocketFactory();
}
#Override
public Response send(Request request) throws IOException {
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) request.getUrl().openConnection();
// if the connection is an instance of the HttpsURLConnection class, the ssl configuration will always been applied.
if (httpURLConnection instanceof HttpsURLConnection) {
// - hostname verifier
if (!secureElasticProperties.isVerifyHostname()) {
logger.debug("setting the hostname verifier to: {}", NoopHostnameVerifier.INSTANCE);
((HttpsURLConnection) httpURLConnection).setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
// - trust store configuration
((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(sslSocketFactory);
}
return super.send(request);
} finally {
try {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (Exception ignore) {
}
}
}
private SSLSocketFactory buildSslSocketFactory() {
SSLSocketFactory sslSocketFactory;
try (InputStream is = getInputStream(secureElasticProperties.getTrustStorePath())) {
KeyStore truststore = KeyStore.getInstance(secureElasticProperties.getTrustStoreType());
truststore.load(is, secureElasticProperties.getTrustStorePassword().toCharArray());
SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(truststore, null);
final SSLContext sslContext = sslBuilder.build();
sslSocketFactory = sslContext.getSocketFactory();
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
String message = String.format("error while loading the security configuration from: %s", secureElasticProperties);
logger.error(message, e);
throw new RuntimeException("management.metrics.export.elastic.ssl");
}
return sslSocketFactory;
}
private InputStream getInputStream(String trustStorePathString) throws IOException {
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = pathMatchingResourcePatternResolver.getResource(trustStorePathString);
return resource.getInputStream();
}
}
that I injected with Spring Boot so I can apply the desired configuration, but I got the following error:
ERROR 10912 --- [trics-publisher] i.m.elastic.ElasticMeterRegistry : failed to send metrics to elastic
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
The server certificate and the client truststore are valid because I already used them with success.
I also tried to force a specific version of the TLS protocol during the handshake phase: TLSv1.3 and TLSv1.2 but the error still occurs.
Anyone have any suggestions on how to fix it? thanks
Check what super.send does, it creates a new connection without using the one you created. I'm not recommending using a self-signed cert and a custom truststore but you can set a default HostnameVerifier using
HttpsURLConnection.setDefaultHostnameVerifier.
Since this is static, it will work for all HttpsURLConnection instances so you don't need to inject anything into Micrometer.
The right solution would be either using a non-self-signed cert or a proper truststore (e.g.: via javax.net.ssl.trustStore).
I did a test with a simple change to the code I had posted and I solved it:
I copied all code of the super.send() method, adding the additional code to set the custom SslSocketFactory and all was OK!
so the reason was that
it creates a new connection without using the one you created
as Jonatan said... a my trivial mistake. :)

Connect to MQ using the secure keys (public & private key)

I am able to establish connection to QManager using unsecure channel and was able to do what ever I want to do.
But now I am trying to connect to the same QManage through secure channel; I have got the Security keys ( both public and private key ) generate from MQ server but I am not sure how to incorporate the key and establish MQ connection.
I googled and found some answer which suggested the below approach but it didn't work.
System.setProperty("javax.net.ssl.trustStore","path to public key");
System.setProperty("javax.net.ssl.keyStore","path to private key");
Exception I got.
com.ibm.mq.MQException : MQJE001: Completion Code '2', Reason '2537'.
I would appreciate some guidance or sample code on how to connect to QManager using security key.
#JoshMc -- please find answer to your question below
Are you using IBM MQ Classes for Java or IBM MQ Classes for JMS?
I am using IBM MQ Classes for Java
What version of IBM MQ are the jar files you are using from?
Version 7
What version of MQ is the queue manager you are attempting to connect to?
Version 8
Are you attempting to have MQ validate a client cert (Check the value of SSLCAUTH on the SVRCONN channel)
Yes. I have public and private key generated from MQ server by MQ Admin (I have no access to MQ server) and need to use it to connect to the MQ server.
Paste any errors that show up in the queue managers AMQERR01.LOG when you attempt to connect.
I don't have access to the log file.
Below is working code; I am able to connect to unsecure channel and send message
public void MQSender(){
MQQueueManager QMgr = null;
try {
MQEnvironment.hostname = "hostname";
MQEnvironment.channel = "UNSECURE";
//MQEnvironment.channel = "SECURE";
MQEnvironment.port = 8080;
QMgr = new MQQueueManager("QManager");
int openOptions = MQConstants.MQOO_OUTPUT;
MQQueue queue = QMgr.accessQueue("QNAME",openOptions);
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = MQConstants.MQPMO_LOGICAL_ORDER | MQConstants.MQPMO_SYNCPOINT;
MQMessage message = new MQMessage();
message.writeString("TEST");
queue.put(message, pmo);
QMgr.commit();
} catch (Exception e) {
if(QMgr!=null){
try {
QMgr.backout();
} catch (MQException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}
}
You are missing the option specifying the ciphersuite to use from your code:
MQEnvironment.sslCipherSuite
This knowledge center article may help: https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q031220_.htm
And do you have your trusted certs and private cert/key pair in JKS stores?
These should be more like:
System.setProperty("javax.net.ssl.trustStore","path to JKS file containing certificates required to validate server certificate");
System.setProperty("javax.net.ssl.keyStore","path to JKS file containing certificate and private key of the client");

Embedded Jetty with https : Invalid keystore format

I'm trying to get my embedded Jetty 8.1.4 (no I can go higher!) server to work with https. I generate my keystore with the following command :
keytool -genkey -alias sitename -keyalg RSA -keystore keystore -keysize 2048
My problem is that when I run this, I get a nice exception!
WARN - AbstractLifeCycle - FAILED SslContextFactory#160b6dff(null,null): java.io.IOException: Invalid keystore format
java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:633)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1183)
I tried generating the keystore following different online guides but to no avail.
Any ideas?
Many Thanks,
AW
Here's the server code :
public static void main(String[] args) throws Exception {
Server server = new Server();
server.setStopAtShutdown(true);
server.setGracefulShutdown(ALLOWED_SHUTDOWN_TIME);
Resource keystore = Resource.newClassPathResource("/keystore");
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreResource(keystore);
sslContextFactory.setKeyStorePassword("wicket");
sslContextFactory.setTrustStoreResource(keystore);
sslContextFactory.setKeyManagerPassword("wicket");
SslSelectChannelConnector connector = new SslSelectChannelConnector(sslContextFactory);
connector.setMaxIdleTime(30000);
connector.setPort(getServerPort());
connector.setAcceptors(4);
connector.setReuseAddress(false);
server.addConnector(connector);
final ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
context.setResourceBase("src/main/webapp");
context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(60 * 15);
context.getSessionHandler().getSessionManager().setSessionTrackingModes(newHashSet(SessionTrackingMode.COOKIE));
final FilterHolder openSessionInViewFilter = new FilterHolder(new OpenSessionInViewFilter());
context.addFilter(openSessionInViewFilter, "/", EnumSet.of(DispatcherType.REQUEST));
final FilterHolder wicketFilter = new FilterHolder(WicketFilter.class);
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
wicketFilter.setInitParameter("applicationFactoryClassName", "org.apache.wicket.spring.SpringWebApplicationFactory");
wicketFilter.setInitParameter("configuration", "development");
context.addFilter(wicketFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
context.getInitParams().put("contextConfigLocation", "classpath:spring/spring-config.xml");
context.addEventListener(new ContextLoaderListener());
try {
server.start();
server.join();
} catch (Exception exception) {
exception.printStackTrace();
System.exit(100);
}
}
When it comes to SSL and the web, you HAVE TO STAY CURRENT both in the JVM and the Server software. The rapid changes in what is supported in SSL in the browser, proxies, hardware, etc.. force you to.
For example, Jetty 8.1.4 was created back in the days of SSL v1, SSL v2 were popular. Today, SSL is pretty much deprecated (all the way to SSL v3), replaced with TLS with NPN/ALPN being increasingly popular.
Newer versions of Jetty have even deprecated various cipher suites that cause problems online.
Even the Certificate Authorities (CA) are moving in directions that render the older SSLs irrelevant, making it almost impossible to get a Certificate that will work with these old environments.

Resources