Apache Camel CXF: add TlsClientParameters programmatically - apache

I am using Apache Camel CXF as producer to call a SOAP Webservice. I do not use Spring configuration but do everything programmatically (I am a beginner and wanted to prevent having to learn both Spring and Apache Camel). The Webservice uses SSL with a self signed certificate. I added it to a truststore and hoped to be able to add that to the CxfEndpoint similar to how I did it with https4:
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("src/main/resources/truststore.jks");
ksp.setPassword("...");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("...");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
CamelContext context = new DefaultCamelContext();
context.addRoutes(routeBuilder);
HttpComponent httpComponent = context.getComponent("https4", HttpComponent.class);
httpComponent.setSslContextParameters(scp);
– but that does not seem to work with the CxfComponent. I found a lot of documentation about adding TlsClientParameters using Spring and configuring the CxfEndpoint, for example here: apache camel cxf https not working
and here Calling secure webservice using CXF and Camel. However I do not find any hint on how to simply add a truststore to the component as I did with https4 or even in the route definition, which is:
from(ENDPOINT_URI)
.setProperty(SecurityConstants.PASSWORD, constant(PASSWORD))
.setProperty(SecurityConstants.USERNAME, constant(USERNAME))
.to("cxf://" + SERVICE_URL + "?" +
"wsdlURL=" + WSDL_URL + "&" +
"serviceName=" + SERVICE_NAME + "&" +
"portName=" + PORT_NAME + "&" +
"dataFormat=CXF_MESSAGE&" +
"synchronous=true&" +
"defaultOperationName=" + DEFAULT_OPERATION_NAME)
.streamCaching();
I think this must be a very simple problem, so I still expect there is some neat way to simply add the truststore (or even accepting any certificate, since its not really relevant in our use case). I would be really happy if there was a simple programmatic way. Does anyone know?

I solved the issue by adding the certificate to the JVMs truststore in jre/lib/cacerts. That is feasable since I have access to the JVM on the machine the application will be running on. It seems to be the simplest solution.
Update
If anyone is interested in a more proper solution: CxfEndpoint provides a means to influence the HTTPConduit and its TLS Parameters. This is the revised code:
add "cxfEndpointConfigurer=SageEndpointConfigurer" to the cxf endpoint parameters
when creating the endpoint "SageEndpointConfigurer" will be resolved using TypeConverters
add a TypeConverter to the TypeConverter Registry of the context, i.e. directly in the RouteBuilder
getContext().getTypeConverterRegistry().addTypeConverter(CxfEndpointConfigurer.class, String.class, new SageEndpointConfigurerConverter());
configure TLSParameters and simply return the CxfEndpointConfigurer from the TypeConverter
private class SageEndpointConfigurerConverter extends TypeConverterSupport {
#Override
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {
CxfEndpointConfigurer configurer = new CxfEndpointConfigurer() {
#Override
public void configure(AbstractWSDLBasedEndpointFactory factoryBean) {
// do nothing
}
#Override
public void configureClient(Client client) {
URLConnectionHTTPConduit conduit = (URLConnectionHTTPConduit) client.getConduit();
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setDisableCNCheck(true);
tlsParams.setTrustManagers(new TrustManager[]{new TrustAllTrustManager()});
conduit.setTlsClientParameters(tlsParams);
}
#Override
public void configureServer(Server server) {
//do nothing
}
};
return (T) configurer;
}
}
the TrustAllManager is implemented like that
public class TrustAllTrustManager implements X509TrustManager {
private static Logger LOG = LoggerFactory.getLogger(TrustAllTrustManager.class);
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
//do nothing, trust all certificates
logMessage(x509Certificates, authType);
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
//do nothing, trust all certificates
logMessage(x509Certificates, authType);
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
private void logMessage(X509Certificate[] x509Certificates, String authType) {
StringBuilder message = new StringBuilder();
String lineSeparator = System.getProperty("line.separator");
message.append("Trusted following certificates for authentication type '").append(authType).append("'").append(lineSeparator);
for (X509Certificate certificate : x509Certificates) {
message.append(certificate).append(lineSeparator);
}
LOG.trace(message.toString());
}
}

Related

How can I provide my custom OAuth2LoginAuthenticationProvider in spring boot

I am trying to setup OAuth2 login in spring boot, every thing is working good in my dev environment, however when I deploy it on production behind the Apache Http server using proxy configuration, authentication fails on Invalid Redirect URI check.
As suggested in some other post I already tried
1. use-forward-headers: true
2. Rewriting the request redirect URI
with no luck.
So I decided to hook my own custom OAuth2LoginAuthenticationProvider so that I can override this check.
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Value("${baseUrl}")
private String domainPath;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new MyCustomFilter(domainPath), OAuth2LoginAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated().and()
.logout().logoutSuccessUrl("/")
.and()
.oauth2Login().userInfoEndpoint().customUserType(CmmOAuth2User.class, "custom");
}
#Bean
public MyOAuth2AuthenticationProvider authenticationProvider() {
MyOAuth2AuthenticationProvider authProvider
= new MyOAuth2AuthenticationProvider();
return authProvider;
}
#Autowired
public void configureProviderManager(ProviderManager providerManager) {
providerManager.getProviders().add(0,authenticationProvider());
}
#Bean
public ProviderManager authenticationManager() {
List<AuthenticationProvider> authProviderList = new ArrayList<AuthenticationProvider>();
authProviderList.add(0,authenticationProvider());
ProviderManager providerManager = new ProviderManager(authProviderList);
return providerManager;
}
}
My custom MyOAuth2AuthenticationProvider is not being called.

Enable SSL certificate revocation checking in OpenJDK 11

Is there some quick "declarative" way in Java 11, instead of a tedious manual implementation, to enable checking if a certificate is revoked?
I tried to use properties from this answer:
Check X509 certificate revocation status in Spring-Security before authenticating
with this dummy revoked certificate: https://revoked.badssl.com
but the code always accepts the certificate. Am I doing something wrong or these properties are no more actual for Java 11? If so, do we have any alternatives?
Below is my code:
public static void validateOnCertificateRevocation(boolean check) {
if (check) {
System.setProperty("com.sun.net.ssl.checkRevocation", "true");
System.setProperty("com.sun.security.enableCRLDP", "true");
Security.setProperty("ocsp.enable", "true");
}
try {
new URL("https://revoked.badssl.com").openConnection().connect();
} catch (IOException e) {
e.printStackTrace();
}
}
It seems like those options have to be set before the first request has been performed.
Therefore the following code as standalone Java program throws an CertPathValidatorException: Certificate has been revoked (tested using OpenJDK 11.0.2 x64 on Windows):
public static void main(String[] args) {
validateOnCertificateRevocation(true); // throws CertPathValidatorException
}
However the following code does not cause any errors/Exceptions:
public static void main(String[] args) {
validateOnCertificateRevocation(false);
validateOnCertificateRevocation(true); // nothing happens
}
You can see the changing the options after the first request has been processed isn't effective. I assume that those options are processed in a static { ... } block of some certificate validation related class.
If you still want to enable/disable certificate revocation checking on a per-request base you can do so by implementing your own X509TrustManager that uses CertPathValidator (for which you can enable/disable certificate revocation checking via PKIXParameters.setRevocationEnabled(boolean).
Alternatively there is the solution to globally enable certificate revocation checking and explicitly handle the CertificateRevokedException:
private boolean checkOnCertificateRevocation;
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
try {
getDefaultTrustManager().checkServerTrusted(certs, authType);
} catch (CertificateException e) {
if (checkOnCertificateRevocation) {
if (getRootCause(e) instanceof CertificateRevokedException) {
throw e;
}
}
}
}

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.NETWORK

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.NETWORK
Hi i got this error while i am calling one API service from retrofit , i am searching a lot and found answer like
private static void setupRestClient() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(ROOT)
//.setClient(new OkClient(new com.squareup.okhttp.OkHttpClient()))
//.setClient(getOkClient())
.setClient(setSSLFactoryForClient(new com.squareup.okhttp.OkHttpClient()))
.setRequestInterceptor(new SessionRequestInterceptor())
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog(NetworkUtil.APP_TAG))
.build();
REST_CLIENT = restAdapter.create(Restapi.class);
}
// SET SSL
public static OkClient setSSLFactoryForClient(OkHttpClient client) {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
client.setSslSocketFactory(sslSocketFactory);
client.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
return new OkClient(client);
}
After using setSSLFactoryForClient method it work fine but i couldn't understand whats going wrong and what this method does i know the problem is related to SSL Certificate Authentication but Can any one explain me this in brief Please
This is disabling the security of SSL. This is ok for local testing but not appropriate for use with real users.
If you run your local dev server with a self signed cert then this is how you can tell it to connect to it with minimal pain.
More generally any user agent (Firefox on Windows, Safari on Mac, Android) will have a list of root CAs it trusts to verify a sites certificates. Some newer services like let's encrypt will not be trusted on older platforms so you can add your own certificates that you know ahead of time.
The hostname verification means that the cert it serves could be for a different site even.
For real traffic this code means your users are susceptible to man in the middle attacks.

Specifying trust store information in spring boot application.properties

I am using springBootVersion 1.2.0.RELEASE.
I'm trying to have my keystore and truststore configured through application.properties.
When I add the following settings, I can get the keystore to work, but not the truststore.
server.ssl.key-store=classpath:foo.jks
server.ssl.key-store-password=password
server.ssl.key-password=password
server.ssl.trust-store=classpath:foo.jks
server.ssl.trust-store-password=password
However, if I add the truststore through gradle:
bootRun {
jvmArgs = [ "-Djavax.net.ssl.trustStore=c://foo.jks", "-Djavax.net.ssl.trustStorePassword=password"]
}
it works just fine.
Has anyone used the application.properties for trust stores?
In case if you need to make a REST call you can use the next way.
This will work for outgoing calls through RestTemplate.
Declare the RestTemplate bean like this.
#Configuration
public class SslConfiguration {
#Value("${http.client.ssl.trust-store}")
private Resource keyStore;
#Value("${http.client.ssl.trust-store-password}")
private String keyStorePassword;
#Bean
RestTemplate restTemplate() throws Exception {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(
keyStore.getURL(),
keyStorePassword.toCharArray()
).build();
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory).build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
}
Where http.client.ssl.trust-store and http.client.ssl.trust-store-password points to truststore in JKS format and the password for the specified truststore.
This will override the RestTemplate bean provided with Spring Boot and make it use the trust store you need.
I had the same problem with Spring Boot, Spring Cloud (microservices) and a self-signed SSL certificate. Keystore worked out of the box from application properties, and Truststore didn't.
I ended up keeping both keystore and trustore configuration in application.properties, and adding a separate configuration bean for configuring truststore properties with the System.
#Configuration
public class SSLConfig {
#Autowired
private Environment env;
#PostConstruct
private void configureSSL() {
//set to TLSv1.1 or TLSv1.2
System.setProperty("https.protocols", "TLSv1.1");
//load the 'javax.net.ssl.trustStore' and
//'javax.net.ssl.trustStorePassword' from application.properties
System.setProperty("javax.net.ssl.trustStore", env.getProperty("server.ssl.trust-store"));
System.setProperty("javax.net.ssl.trustStorePassword",env.getProperty("server.ssl.trust-store-password"));
}
}
I have the same problem, I'll try to explain it a bit more in detail.
I'm using spring-boot 1.2.2-RELEASE and tried it on both Tomcat and Undertow with the same result.
Defining the trust-store in application.yml like:
server:
ssl:
trust-store: path-to-truststore...
trust-store-password: my-secret-password...
Doesn't work, while:
$ java -Djavax.net.debug=ssl -Djavax.net.ssl.trustStore=path-to-truststore... -Djavax.net.ssl.trustStorePassword=my-secret-password... -jar build/libs/*.jar
works perfectly fine.
The easiest way to see the difference at rutime is to enable ssl-debug in the client. When working (i.e. using -D flags) something like the following is written to the console (during processing of the first request):
trustStore is: path-to-truststore...
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: C=..., ST=..., O=..., OU=..., CN=...
Issuer: C=..., ST=..., O=..., OU=..., CN=...
Algorithm: RSA; Serial number: 0x4d2
Valid from Wed Oct 16 17:58:35 CEST 2013 until Tue Oct 11 17:58:35 CEST 2033
Without the -D flags I get:
trustStore is: /Library/Java/JavaVirtualMachines/jdk1.8.0_11.jdk/Contents/Home/jre/lib/security/cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert: ... (one for each CA-cert in the defult truststore)
...and when performing a request I get the exception:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Hope it helps to understand the issue better!
java properties "javax.net.ssl.trustStore" and "javax.net.ssl.trustStorePassword" do not correspond to "server.ssl.trust-store" and "server.ssl.trust-store-password" from Spring boot "application.properties" ("application.yml")
so you can not set "javax.net.ssl.trustStore" and "javax.net.ssl.trustStorePassword" simply by setting "server.ssl.trust-store" and "server.ssl.trust-store-password" in "application.properties" ("application.yml")
an alternative of setting "javax.net.ssl.trustStore" and "javax.net.ssl.trustStorePassword" is by Spring boot Externalized Configuration
below are excerpts of my implementation :
Params class holds the external settings
#Component
#ConfigurationProperties("params")
public class Params{
//default values, can be override by external settings
public static String trustStorePath = "config/client-truststore.jks";
public static String trustStorePassword = "wso2carbon";
public static String keyStorePath = "config/wso2carbon.jks";
public static String keyStorePassword = "wso2carbon";
public static String defaultType = "JKS";
public void setTrustStorePath(String trustStorePath){
Params.trustStorePath = trustStorePath;
}
public void settrustStorePassword(String trustStorePassword){
Params.trustStorePassword=trustStorePassword;
}
public void setKeyStorePath(String keyStorePath){
Params.keyStorePath = keyStorePath;
}
public void setkeyStorePassword(String keyStorePassword){
Params.keyStorePassword = keyStorePassword;
}
public void setDefaultType(String defaultType){
Params.defaultType = defaultType;
}
KeyStoreUtil class undertakes the settings of "javax.net.ssl.trustStore" and "javax.net.ssl.trustStorePassword"
public class KeyStoreUtil {
public static void setTrustStoreParams() {
File filePath = new File( Params.trustStorePath);
String tsp = filePath.getAbsolutePath();
System.setProperty("javax.net.ssl.trustStore", tsp);
System.setProperty("javax.net.ssl.trustStorePassword", Params.trustStorePassword);
System.setProperty("javax.net.ssl.keyStoreType", Params.defaultType);
}
public static void setKeyStoreParams() {
File filePath = new File(Params.keyStorePath);
String ksp = filePath.getAbsolutePath();
System.setProperty("Security.KeyStore.Location", ksp);
System.setProperty("Security.KeyStore.Password", Params.keyStorePassword);
}
}
you get the setters executed within the startup function
#SpringBootApplication
#ComponentScan("com.myapp.profiles")
public class ProfilesApplication {
public static void main(String[] args) {
KeyStoreUtil.setKeyStoreParams();
KeyStoreUtil.setTrustStoreParams();
SpringApplication.run(ProfilesApplication.class, args);
}
}
Edited on 2018-10-03
you may also want to adopt the annotation "PostConstruct" as as an alternative to execute the setters
import javax.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages={"com.xxx"})
public class GateApplication {
public static void main(String[] args) {
SpringApplication.run(GateApplication.class, args);
}
#PostConstruct
void postConstruct(){
setTrustStoreParams();
setKeyStoreParams();
}
private static void setTrustStoreParams() {
File filePath = new File( Params.trustStorePath);
String tsp = filePath.getAbsolutePath();
System.setProperty("javax.net.ssl.trustStore", tsp);
System.setProperty("javax.net.ssl.trustStorePassword", Params.trustStorePassword);
System.setProperty("javax.net.ssl.keyStoreType", Params.defaultType);
}
private static void setKeyStoreParams() {
File filePath = new File(Params.keyStorePath);
String ksp = filePath.getAbsolutePath();
System.setProperty("Security.KeyStore.Location", ksp);
System.setProperty("Security.KeyStore.Password", Params.keyStorePassword);
}
}
the application.yml
---
params:
trustStorePath: config/client-truststore.jks
trustStorePassword: wso2carbon
keyStorePath: config/wso2carbon.jks
keyStorePassword: wso2carbon
defaultType: JKS
---
finally, within the running environment(deployment server), you create a folder named "config" under the same folder where the jar archive is stored .
within the "config" folder, you store "application.yml", "client-truststore.jks", and "wso2carbon.jks". done!
Update on 2018-11-27 about Spring boot 2.x.x
starting from spring boot 2.x.x, static properties are no longer supported, please see here. I personally do not think it a good move, because complex changes have to be made along the reference chain...
anyway, an implementation excerpt might look like this
the 'Params' class
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
/**
* Params class represent all config parameters that can
* be external set by spring xml file
*/
#Component
#ConfigurationProperties("params")
#Data
public class Params{
//default values, can be override by external settings
public String trustStorePath = "config/client-truststore.jks";
public String trustStorePassword = "wso2carbon";
public String keyStorePath = "config/wso2carbon.jks";
public String keyStorePassword = "wso2carbon";
public String defaultType = "JKS";
}
the 'Springboot application class' (with 'PostConstruct')
import java.io.File;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages={"com.xx.xx"})
public class BillingApplication {
#Autowired
Params params;
public static void main(String[] args) {
SpringApplication.run(BillingApplication.class, args);
}
#PostConstruct
void postConstruct() {
// set TrustStoreParams
File trustStoreFilePath = new File(params.trustStorePath);
String tsp = trustStoreFilePath.getAbsolutePath();
System.setProperty("javax.net.ssl.trustStore", tsp);
System.setProperty("javax.net.ssl.trustStorePassword", params.trustStorePassword);
System.setProperty("javax.net.ssl.keyStoreType", params.defaultType);
// set KeyStoreParams
File keyStoreFilePath = new File(params.keyStorePath);
String ksp = keyStoreFilePath.getAbsolutePath();
System.setProperty("Security.KeyStore.Location", ksp);
System.setProperty("Security.KeyStore.Password", params.keyStorePassword);
}
}
I was also having the same issue with Spring Boot and embedded Tomcat.
From what I understand these properties only set the Tomcat configuration parameters. According to the Tomcat documentation this is only used for Client authentication (i.e. for two-way SSL) and not for verifying remote certificates:
truststoreFile - The trust store file to use to validate client certificates.
https://tomcat.apache.org/tomcat-8.0-doc/config/http.html
In order to configure the trust store for HttpClient it largely depends on the HttpClient implementation you use. For instance for RestTemplate by default Spring Boot uses a SimpleClientHttpRequestFactory based on standard J2SE classes like java.net.HttpURLConnection.
I've come up with a solution based on the Apache HttpClient docs and these posts:
http://vincentdevillers.blogspot.pt/2013/02/configure-best-spring-resttemplate.html
http://literatejava.com/networks/ignore-ssl-certificate-errors-apache-httpclient-4-4/
Basically this allows for a RestTemplate bean that only trusts certificates signed by the root CA in the configured truststore.
#Configuration
public class RestClientConfig {
// e.g. Add http.client.ssl.trust-store=classpath:ssl/truststore.jks to application.properties
#Value("${http.client.ssl.trust-store}")
private Resource trustStore;
#Value("${http.client.ssl.trust-store-password}")
private char[] trustStorePassword;
#Value("${http.client.maxPoolSize}")
private Integer maxPoolSize;
#Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
#Bean
public HttpClient httpClient() {
// Trust own CA and all child certs
Registry<ConnectionSocketFactory> socketFactoryRegistry = null;
try {
SSLContext sslContext = SSLContexts
.custom()
.loadTrustMaterial(trustStore.getFile(),
trustStorePassword)
.build();
// Since only our own certs are trusted, hostname verification is probably safe to bypass
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
new HostnameVerifier() {
#Override
public boolean verify(final String hostname,
final SSLSession session) {
return true;
}
});
socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build();
} catch (Exception e) {
//TODO: handle exceptions
e.printStackTrace();
}
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(maxPoolSize);
// This client is for internal connections so only one route is expected
connectionManager.setDefaultMaxPerRoute(maxPoolSize);
return HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.disableCookieManagement()
.disableAuthCaching()
.build();
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(httpRequestFactory());
return restTemplate;
}
}
And then you can use this custom Rest client whenever you need to, e.g.:
#Autowired
private RestTemplate restTemplate;
restTemplate.getForEntity(...)
This assumes your trying to connect to a Rest endpoint, but you can also use the above HttpClient bean for whatever you want.
If you execute your Spring Boot application as a linux service (e.g. init.d script or similar), then you have the following option as well:
Create a file called yourApplication.conf and put it next to your executable war/jar file. It's content should be something similar:
JAVA_OPTS="
-Djavax.net.ssl.trustStore=path-to-your-trustStore-file
-Djavax.net.ssl.trustStorePassword=yourCrazyPassword
"
Although I am commenting late. But I have used this method to do the job. Here when I am running my spring application I am providing the application yaml file via -Dspring.config.location=file:/location-to-file/config-server-vault-application.yml which contains all of my properties
config-server-vault-application.yml
***********************************
server:
port: 8888
ssl:
trust-store: /trust-store/config-server-trust-store.jks
trust-store-password: config-server
trust-store-type: pkcs12
************************************
Java Code
************************************
#SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) throws IOException {
setUpTrustStoreForApplication();
SpringApplication.run(ConfigServerApplication.class, args);
}
private static void setUpTrustStoreForApplication() throws IOException {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
List<PropertySource<?>> applicationYamlPropertySource = loader.load(
"config-application-properties", new UrlResource(System.getProperty("spring.config.location")));
Map<String, Object> source = ((MapPropertySource) applicationYamlPropertySource.get(0)).getSource();
System.setProperty("javax.net.ssl.trustStore", source.get("server.ssl.trust-store").toString());
System.setProperty("javax.net.ssl.trustStorePassword", source.get("server.ssl.trust-store-password").toString());
}
}
Here my extended version of Oleksandr Shpota's answer, including the imports. The package org.apache.http.* can be found in org.apache.httpcomponents:httpclient. I've commented the changes:
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
#Value("${http.client.ssl.key-store}")
private Resource keyStore;
#Value("${http.client.ssl.trust-store}")
private Resource trustStore;
// I use the same pw for both keystores:
#Value("${http.client.ssl.trust-store-password}")
private String keyStorePassword;
// wasn't able to provide this as a #Bean...:
private RestTemplate getRestTemplate() {
try {
SSLContext sslContext = SSLContexts.custom()
// keystore wasn't within the question's scope, yet it might be handy:
.loadKeyMaterial(
keyStore.getFile(),
keyStorePassword.toCharArray(),
keyStorePassword.toCharArray())
.loadTrustMaterial(
trustStore.getURL(),
keyStorePassword.toCharArray(),
// use this for self-signed certificates only:
new TrustSelfSignedStrategy())
.build();
HttpClient httpClient = HttpClients.custom()
// use NoopHostnameVerifier with caution, see https://stackoverflow.com/a/22901289/3890673
.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()))
.build();
return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
If you are in Spring, try just add properties for it (use needed properties), and it should work for total JVM
javax:
net:
ssl:
key-store-password: ${KEYSTORE_SECRET}
key-store-type: PKCS12
trust-store-password: ${TRUSTSTORE_SECRET}
trust-store-type: PKCS12
In a microservice infrastructure (does not fit the problem, I know ;)) you must not use:
server:
ssl:
trust-store: path-to-truststore...
trust-store-password: my-secret-password...
Instead the ribbon loadbalancer can be configuered:
ribbon:
TrustStore: keystore.jks
TrustStorePassword : example
ReadTimeout: 60000
IsSecure: true
MaxAutoRetries: 1
Here https://github.com/rajaramkushwaha/https-zuul-proxy-spring-boot-app you can find a working sample. There was also a github discussion about that, but I didn't find it anymore.

Certificate not found in test with EjbContainer and GlassFish

I have an EJB that communicates with a site over https. The logic is to send a xml file in request and receive another in response. This works fine in development environment after adding the site certificate to cacerts inside GlassFish domain. The problem appears when the communication happens in test environment with EJBContainer. Even with org.glassfish.ejb.embedded.glassfish.installation.root and org.glassfish.ejb.embedded.glassfish.instance.root properties defined and certificate added to cacerts, the test execution ends with:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My EJB is implemented as follows:
#Stateless
#LocalBean
public class CommunicationService {
public String communicate() {
try {
URL url = new URL("https://www.comprasnet.gov.br/XML/treinamento/consultamatserv.asp");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.flush();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
StringBuilder resposta = new StringBuilder();
while ((line = reader.readLine()) != null) {
resposta.append(line);
}
writer.close();
reader.close();
return resposta.toString();
} catch (Exception e) {
return null;
}
}
}
My test configuration uses TestNG 6.8.5, GlassFish 3.1.2.2 and EJBContainer. The configuration starts creating another domain in GlassFish to avoid port conflicts with the default domain if it running. To do that, I've run the command:
asadmin create-domain --portbase 9100 domain-test
I've defined a super class with an annotated method with #BeforeSuite that starts the embedded container with the following content:
public abstract class GlassfishEmbeddedBaseTest {
protected Context ic;
protected UserTransaction tx;
private static EJBContainer ejbContainer;
#BeforeSuite
protected void beforeSuite() throws Exception {
String glassfishHome = System.getenv("GLASSFISH_HOME");
Properties properties = new Properties();
properties.put(EJBContainer.MODULES, new File[] { new File(
"target/classes") });
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
glassfishHome + "/glassfish");
properties.put("org.glassfish.ejb.embedded.glassfish.instance.root",
glassfishHome + "/glassfish/domains/domain-test");
properties.put(EJBContainer.APP_NAME, "app-name");
ejbContainer = EJBContainer.createEJBContainer(properties);
}
#BeforeClass
protected void load() throws Exception {
ic = ejbContainer.getContext();
}
#BeforeMethod
protected void beforeMethod() throws Exception {
tx = (UserTransaction) ic.lookup("java:comp/UserTransaction");
tx.begin();
}
#AfterMethod
protected void rollBack() throws Exception {
tx.rollback();
}
}
In the test class, I've did a look up for my EJB and calls the logic that communicates with the site over https:
public class CommunicationServiceTest extends GlassfishEmbeddedBaseTest {
private CommunicationService communicationService;
#BeforeClass
public void init() throws NamingException {
communicationService = (CommunicationService) ic
.lookup("java:global/app-name/classes/CommunicationService");
}
#Test
public void testCommunicate() {
String response = communicationService.communicate();
Assert.assertNotNull(response);
}
}
I found a bug related to this problem in GlassFish Jira:
https://java.net/jira/browse/GLASSFISH-17179, and as the EJBContainer is based on domain-test and the certified is installed in cacerts from this domain, I think that can be a problem of copy the cacerts defined on instance root property to the temporary directory created on embedded container start time.
How can I lead with that?
EJBContainer offers a property named org.glassfish.ejb.embedded.glassfish.instance.reuse that determine:
If true, no changes are made to the existing configuration file, and a temporary server
instance is not created for the embedded run. Instead, execution happens against the existing server instance. Do not use this option if the reused server instance could be in use by the running GlassFish Server.
Adding this property with value true, before create EJBContainer, solved the issue, since the certificate was already added to the cacerts in domain domain-test and nothing is copied to the temporary folder anymore.