I am implementing a code that generate an error I don't understand. I'm googling since three days ago unsuccessfully. Find below my code :
Main class
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Test {
public static void main(String[] args) throws IOException {
URL url = new URL("https://qosic.net:8443/QosicBridge/user/deposit");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
String json = "{\"msisdn\": \"22967307747\",\"amount\": 2000,\"transref\": 56789,\"clientid\": QOS3P001}";
OutputStream os = connection.getOutputStream();
os.write(json.getBytes());
os.flush();
if(connection.getResponseCode() != 200){
throw new RuntimeException("Failed : Http Error code "+connection.getResponseCode());
}
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String output;
System.out.println("Output from server....\n");
while((output = reader.readLine()) != null){
System.out.println(output);
}
}
}
Error
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:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
... 13 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:146)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 19 more
If someone could help understanding what I did wrong, I will be very grateful
Thanks,
unable to find valid certification path to requested target
You need to provide a keystore with a valid certificate for the host (in this case qosic.net) you are trying to connect to via https (ssl).
You can initialise your own SSL Context, see here for examples.
Or you can ignore all certificates (WHICH IS A HACK, NOT FOR PRODUCTION) with this code
#Test
public void test1() throws Exception {
CloseableHttpClient defaultHttpClient = HttpClients.createDefault();
String uri = "https://localhost/healthcheck";
try {
defaultHttpClient.execute(new HttpGet(uri));
fail();
} catch (SSLHandshakeException e) {
// do nothing
}
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build());
CloseableHttpClient customHttpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
CloseableHttpResponse response = customHttpClient.execute(new HttpGet(uri));
assertEquals(200, response.getStatusLine().getStatusCode());
}
Related
I'm trying to access my corporate Gmail account, and I am using the Java Quickstart guide. However, even after following all the directions in the guide, I keep getting the below error:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I've tried to address this issue by adding certificates for both accounts.google.com and gmail.com to cacerts, but it didn't help and the error remained the same.
Here's the code that I'm using:
/* build.gradle */
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'GmailQuickstart'
sourceCompatibility = 11
targetCompatibility = 11
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.api-client:google-api-client:2.0.0'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1'
implementation 'com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0'
}
/* GmailQuickstart.java */
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Label;
import com.google.api.services.gmail.model.ListLabelsResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
/* class to demonstrate use of Gmail list labels API */
public class GmailQuickstart {
/**
* Application name.
*/
private static final String APPLICATION_NAME = "Application_Name";
/**
* Global instance of the JSON factory.
*/
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
/**
* Directory to store authorization tokens for this application.
*/
private static final String TOKENS_DIRECTORY_PATH = "tokens";
/**
* Global instance of the scopes required by this quickstart.
* If modifying these scopes, delete your previously saved tokens/ folder.
*/
private static final List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_LABELS);
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
/**
* Creates an authorized Credential object.
*
* #param HTTP_TRANSPORT The network HTTP Transport.
* #return An authorized Credential object.
* #throws IOException If the credentials.json file cannot be found.
*/
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT)
throws IOException {
// Load client secrets.
InputStream in = GmailQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
//returns an authorized Credential object.
return credential;
}
public static void main(String... args) throws IOException, GeneralSecurityException {
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
// Print the labels in the user's account.
String user = "me";
ListLabelsResponse listResponse = service.users().labels().list(user).execute();
List<Label> labels = listResponse.getLabels();
if (labels.isEmpty()) {
System.out.println("No labels found.");
} else {
System.out.println("Labels:");
for (Label label : labels) {
System.out.printf("- %s\n", label.getName());
}
}
}
}
What can I do to fix this issue?
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. :)
Our Spring Integration application runs on Websphere. It is a client to an SSL external service.
I've imported a certificate using Retrive from port [into default trust store], giving host and 443 port. Enabled tracing on WebSphere and it seems it is looking at cacert file and not trust.p12.
[18-2-19 13:44:59:154 CET] 00000063 SystemOut O 2019-02-18 13:44:59.153 INFO 30426 --- [ver.startup : 0] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [public org.springframework.http.ResponseEntity<springfox.documentation.spring.web.json.Json> springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(java.lang.String,javax.servlet.http.HttpServletRequest)]
[18-2-19 13:44:59:826 CET] 00000063 SystemOut O keyStore is: /srv/opt/IBM/WebSphere/AppServer/java/8.0/jre/lib/security/cacerts
Code:
public class PreemptiveMessageSender extends HttpComponentsMessageSender {
#Autowired
private Environment env;
private String host;
private String userId;
private String password;
public PreemptiveMessageSender() {
super();
}
public PreemptiveMessageSender(HttpClient httpClient) {
super(httpClient);
}
#Override
protected HttpContext createContext(URI uri) {
HttpHost targetHost = new HttpHost(host, 443, "https");
String decryptedPassword = getDecryptedPassword();
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(userId, decryptedPassword));
AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
// Add AuthCache to the execution context
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
return context;
}
private String getDecryptedPassword() {
BasicTextEncryptor textEncrypt = new BasicTextEncryptor();
textEncrypt.setPassword(env.getProperty("KEY_PASSWORD"));
return textEncrypt.decrypt(password);
}
#Override
public WebServiceConnection createConnection(URI uri) throws IOException {
HttpPost httpPost = new HttpPost(uri);
if (isAcceptGzipEncoding()) {
httpPost.addHeader(HttpTransportConstants.HEADER_ACCEPT_ENCODING,
HttpTransportConstants.CONTENT_ENCODING_GZIP);
}
HttpContext httpContext = createContext(uri);
return new CustomHttpComponentsConnection(getHttpClient(), httpPost, httpContext);
}
...
}
Error:
"exception": "org.springframework.ws.client.WebServiceIOException",
"message": "I/O error: com.ibm.jsse2.util.h: PKIX path building failed: java.security.cert.CertPathBuilderException:
PKIXCertPathBuilderImpl could not build a valid CertPath.; internal
cause is: \n\tjava.security.cert.CertPathValidatorException: The
certificate issued by CN=ODC Test Root CA - G1, O=ODC Test, C=TU is
not trusted; internal cause is:
\n\tjava.security.cert.CertPathValidatorException: Certificate
chaining error; nested exception is
javax.net.ssl.SSLHandshakeException: com.ibm.jsse2.util.h: PKIX path
building failed: java.security.cert.CertPathBuilderException:
PKIXCertPathBuilderImpl could not build a valid CertPath.; internal
cause is: \n\tjava.security.cert.CertPathValidatorException: The
certificate issued by CN=ODC Test Root CA - G1, O=ODC Test, C=TU is
not trusted; internal cause is:
\n\tjava.security.cert.CertPathValidatorException: Certificate
chaining error",
Question:
Is this problem with Spring Integration using the java cacert? How to make it use the trust store of WebSphere?
I'll start with I don't know anything about Spring. But given the behavior you talk about it must be creating its own instance of the SSLContext. This will cause it to by pass WebSphere SSL settings. It must be doing something like SSLContext.getInstance() to create its own instance or it could be doing something like SSLContext.getDefault() which returns you the JDK's default SSLContext. Both will not get you a WebSphere SSLContext.
https://developer.ibm.com/answers/questions/394270/im-using-an-apache-httpclient-to-make-an-outbound/
HttpClient theClient =
HttpClientBuilder.create().useSystemProperties().addInterceptorFirst(new
RemoveSoapHeadersInterceptor()).build();
private static class RemoveSoapHeadersInterceptor implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
if (request instanceof HttpEntityEnclosingRequest) {
if (request.containsHeader(HTTP.TRANSFER_ENCODING)) {
request.removeHeaders(HTTP.TRANSFER_ENCODING);
}
if (request.containsHeader(HTTP.CONTENT_LEN)) {
request.removeHeaders(HTTP.CONTENT_LEN);
}
}
}
}
I've read many things about this problem, and I thought I had found the most simple workaround (last code example from here : http://www.baeldung.com/httpclient-ssl), but it doesn't work.
Here is how I declare my RestTemplate with hostname verification turned off (and a proxy setting) :
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setProxy(new HttpHost("10.xx.xx.xx", 3128, "http"))
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return builder.requestFactory(requestFactory).build();
//return builder.build();
}
And here is the code to create my POST request :
LoginResponse loginResponse = restTemplate.postForObject("https://interflex.svc.suezsmartsolutions.com/path/to/my/api", loginRequest, LoginResponse.class);
And here is the exception I get (just like if I had not turned off hostname verifier) :
16:15:27 ERROR org.springframework.boot.SpringApplication:771 - Application startup failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:735)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:716)
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:703)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
at hello.Application.main(Application.java:23)
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://interflex.svc.suezsmartsolutions.com/path/to/api": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:673)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:387)
at hello.Application.lambda$0(Application.java:45)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:732)
... 6 common frames omitted
Caused by: javax.net.ssl.SSLHandshakeException: 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.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1514)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.upgrade(DefaultHttpClientConnectionOperator.java:193)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.upgrade(PoolingHttpClientConnectionManager.java:375)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:416)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:89)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:659)
... 10 common frames omitted
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:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)
... 32 common frames omitted
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:382)
... 38 common frames omitted
Could someone help me get rid of this exception ?
Also I don't know why this exception appears in the first place, since the root CA used to generate the site certificate (VeriSign) is present in my truststore (cacerts) (the intermediate authority is not present though, could it be the reason ?).
Simple Fix, just skip the certificate
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder().loadTrustMaterial(null, acceptingTrustStrategy).build();
Your issue seems more related to a certificate issue than to proxy configuration.
Anyway in my projects I'm using this configuration:
#Bean
#Autowired
public RestTemplate restTemplate(ClientHttpRequestFactory factory)
{
RestTemplate result = new RestTemplate(factory);
return result;
}
#Bean
public ClientHttpRequestFactory requestFactory() throws Exception
{
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient());
return factory;
}
#Bean
public HttpClient httpClient() throws Exception
{
int timeout = new Integer(env.getProperty("web.http.client.timeout"));
CloseableHttpClient httpClient = null;
//I load a JSON where I specify the name and the PWD of keystores I want to use
String keystores = "keyStoreInfo.json";
PoolingHttpClientConnectionManager pcm = null;
if(StringUtils.hasText(keystores))
{
Resource jsonRes = new ClassPathResource(keystores);
if( jsonRes.exists() )
{
List<KeyStoreInfo> ksInfo = objectMapper().readValue(jsonRes.getInputStream(), new TypeReference<List<KeyStoreInfo>>()
{
});
SSLContext sslCtx = SSLContext.getInstance("TLS");
List<KeyManager> keymanagers = new ArrayList<KeyManager>();
for (KeyStoreInfo ksi : ksInfo)
{
String keystoreName = ksi.getNomeKeyStore();
String keyStorePwd = ksi.getPasswordKeyStore();
if( StringUtils.hasText(keystoreName) )
{
Resource keystoreRes = new ClassPathResource(keystoreName);
KeyMaterial km = new KeyMaterial(keystoreRes.getInputStream(), keyStorePwd.toCharArray());
KeyStore clientStore = km.getKeyStore();
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(clientStore, keyStorePwd != null ? keyStorePwd.toCharArray() : null);
keymanagers.addAll(Arrays.asList(kmfactory.getKeyManagers()));
}
}
if( !keymanagers.isEmpty() )
{
X509TrustManager tm = new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslCtx.init(keymanagers.toArray(new KeyManager[keymanagers.size()]), new TrustManager[]{tm}, null);
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslCtx);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslConnectionFactory).register("http", new PlainConnectionSocketFactory()).build();
pcm = new PoolingHttpClientConnectionManager(registry);
}
else
{
if( logger.isInfoEnabled() )
{
logger.info("Nessun keystore presente nel JSON di configurazione {}. Creo un PoolingHttpClientConnectionManager di default",keystores);
}
pcm = new PoolingHttpClientConnectionManager();
}
}
}
else
{
if( logger.isInfoEnabled() )
{
logger.info("Nessun keystore da caricare. Creo un PoolingHttpClientConnectionManager di default");
}
pcm = new PoolingHttpClientConnectionManager();
}
HttpClientBuilder hcb = HttpClientBuilder.create();
pcm.closeIdleConnections(timeout, TimeUnit.MILLISECONDS);
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).setConnectTimeout(timeout).build();
hcb.setDefaultRequestConfig(config);
hcb.setConnectionManager(pcm).setConnectionManagerShared(true);
boolean proxyEnable = new Boolean(env.getProperty("web.http.client.proxyEnable"));
if (proxyEnable)
{
int proxyPort = new Integer(env.getProperty("web.http.client.portProxy"));
String proxyHost = env.getProperty("web.http.client.hostProxy");
BasicCredentialsProvider credentialProvider = new BasicCredentialsProvider();
AuthScope scope = new AuthScope(proxyHost, proxyPort);
String usernameProxy = env.getProperty("web.http.client.usernameProxy");
String passwordProxy = env.getProperty("web.http.client.passwordProxy");
if (StringUtils.hasText(usernameProxy) && StringUtils.hasText(passwordProxy))
{
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(usernameProxy, passwordProxy);
credentialProvider.setCredentials(scope, credentials);
}
ProxyRoutePlanner proxyRoutPlanner = new ProxyRoutePlanner(new HttpHost(proxyHost, proxyPort), env.getProperty("web.http.client.urlNotProxy"));
hcb.setDefaultCredentialsProvider(credentialProvider).setRoutePlanner(proxyRoutPlanner);
}
WsKeepAliveStrategy cas = new WsKeepAliveStrategy();
cas.setTimeout(new Long(timeout));
hcb.setKeepAliveStrategy(cas);
httpClient = hcb.build();
return httpClient;
}
Where WsKeepAliveStrategy is:
public class WsKeepAliveStrategy implements ConnectionKeepAliveStrategy
{
private Long timeout;
#Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context)
{
return timeout;
}
public void setTimeout(Long timeout)
{
this.timeout = timeout;
}
}
and ProxyRoutePlanner is:
public class ProxyRoutePlanner extends DefaultProxyRoutePlanner
{
private List<String> urlsNotProxy = null;
private boolean useAlwaysSuper = false;
public ProxyRoutePlanner(HttpHost proxy, String urlNotProxy)
{
super(proxy);
if (!StringUtils.hasText(urlNotProxy))
this.useAlwaysSuper = true;
else
{
this.urlsNotProxy = Arrays.asList(urlNotProxy.split(","));
}
}
#Override
public HttpRoute determineRoute(HttpHost host, HttpRequest request, HttpContext context) throws HttpException
{
String hostname = host.getHostName();
if (this.useAlwaysSuper || this.urlsNotProxy.contains(hostname) == false)
return super.determineRoute(host, request, context);// Super method
// with proxy
if ("http".equals(host.getSchemeName()))
return new HttpRoute(host);// Direct Route
HttpClientContext clientContext = HttpClientContext.adapt(context);
RequestConfig config = clientContext.getRequestConfig();
InetAddress local = config.getLocalAddress();
return new HttpRoute(host, local, true);
}
}
I'm using this configuration and I'm having no issue
In any case you should check what kind of certificate is necessary to use in your rest invocation
I hope it's useful
Angelo
The short answer is "yes you need the intermediate authority in your truststore, not only the root CA".
I need to use commons-httpclient-3.1.jar library even it is now end of life.
I am trying to do a simple http GET from url http://www.apache.org/ via proxy server address "10.100.1.44" with proxy port "8080" which requires no credential whatsoever.
Below is my sample code.
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
public class HttpClientTutorial {
private static String url = "http://www.apache.org/";
public static void main(String[] args) {
// Create an instance of HttpClient.
HttpClient client = new HttpClient();
// Create a method instance.
GetMethod method = new GetMethod(url);
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try {
// setting proxy
client.getParams().setAuthenticationPreemptive(true);
client.getState().setProxyCredentials(
new AuthScope("10.100.1.44", 8080),
new UsernamePasswordCredentials("", ""));
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not binary
// data
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
// Release the connection.
method.releaseConnection();
}
}
}
However when I run the code I get exception thrown which is
14 ส.ค. 2556 13:11:28 org.apache.commons.httpclient.HttpMethodDirector authenticateHost
WARNING: Required credentials not available for BASIC <any realm>#www.apache.org:80
14 ส.ค. 2556 13:11:28 org.apache.commons.httpclient.HttpMethodDirector authenticateHost
WARNING: Preemptive authentication requested but no default credentials available
Fatal transport error: www.apache.org
java.net.UnknownHostException: www.apache.org
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at jenkins.plugin.assembla.api.HttpClientTutorial.main(HttpClientTutorial.java:38)
Could someone tell me what I did wrong as I am new to this HttpClient library however I need to use it as I have no intention to use HttpComponents library?
Now I have found a solution to this problem. Please refer to the code below.
import java.io.IOException;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
public class HttpClientTutorial {
private static String url = "http://www.apache.org/";
public static void main(String[] args) {
// Create an instance of HttpClient.
HttpClient client = new HttpClient();
// Create a method instance.
GetMethod method = new GetMethod(url);
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try {
// setting proxy
client.getHostConfiguration().setProxy("10.100.1.44", 8080);
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not binary
// data
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
// Release the connection.
method.releaseConnection();
}
}
}
There is a similar problem here with the answer.
I think it is the same case as yours:
Using HttpProxy to connect to a host with preemtive authentication