We have an internal service that we would like to start making calls to over HTTPS. The service is only available internally, so I'm not worried about accepting all self-signed warnings. This is what I'm using:
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(REST_USER, REST_PASS));
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient client = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setSSLSocketFactory(sslsf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
RestTemplate restTemplate = new RestTemplate(requestFactory);
But I'm getting the following error:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost/addrgeo/standardUSAddresses": 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
I even tried creating a PoolingHttpClientConnectionManager:
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(REST_USER, REST_PASS));
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(2000);//max connection
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
But I get the same error.
We are wrapping our calls with Hystrix, but I don't know why that would have any impact on the actual call itself.
I took a look at this question:
Why can't I find the truststore for an SSL handshake?
But none of the answers seem to get me where I need to be.
What am I doing wrong?
Solution
Well, I feel like a fool...
Turns out it was a weird caching issue with IntelliJ. Maven was still using the old source code. The solution was to close IntelliJ, run a mvn clean install, opening and invalidating caches in IntelliJ, and re-importing the project worked. For good measure, I also refreshed the file status under VCS and performed a Synchronize.
Related
I am trying to reach API via https using username and password with Java and Apache HttpsClient (my framework is based on it). I have access to curl command that works and python code. Both are resolved in one line. The Python code:
request.post(url=URL, files={'mpg': open(file_name, 'rb')}, auth=(r'user', r'passwrod'),verify=True)
The curl command:
curl -u 'user:password' -v -F 'mpg=#file.m4a' https://address
Of the ways I tried in Apache library is:
URIBuilder
URIBuilder uri = new URIBuilder();
uri.setScheme("https").setHost("host").setPath("path").setUserInfo("user","password");
SSLContextBuilder (with credentials provider)
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
CredentialsProvider
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "password"));
httpClient = HttpClients.custom().setDefaultCredentialsProvider(provider).build();
Also multiple variations of the above. I am getting increasingly frustrated as to how long it takes me to try to figure it out since outside of java it can be handled in one line of code. The file submit part works as I was able to run it before authorization and https implemented.
I was able to find the answer after all. the code hat actually works is:
request.addHeader("Authorization", "Basic "+ base64.getEncoder().encodeToString("user:pasword".getBytes()));
Related post that helped to find the soultion:
Apache HTTPClient POSTs to REST service differently than cURL
I'm trying to connect to a website through a proxy, but I'm getting an error
Error Code: 407 Proxy Authentication Required. Forefront TMG requires authorization to fulfill the request. Access to the Web Proxy filter is denied. (12209)
My code is very close to the example that apache provides, https://hc.apache.org/httpcomponents-client-ga/examples.html (see the proxy authentication example). I'm definitely doing something wrong with authentication, but...what?
HttpHost proxy = new HttpHost("http-proxy", 80);
HttpHost target = new HttpHost(url, 80);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user,password));
try (CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(new SystemDefaultCredentialsProvider()).build()) {
RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
HttpGet httpget = new HttpGet("/basic-auth/user/passwd");
httpget.setConfig(config);
HttpResponse response = client.execute(target, httpget);
}
The problem seems to be that you are setting new SystemDefaultCredentialsProvider() when you are building the HTTP client. I guess you intention was to set credsProvider, to which you have just added proxy user and password.
I am trying to connect to an HTTPs URL with Apache HttpClient 4.1....
HttpGet httpget = new HttpGet("https://federation/galaxy-class/enterprise/getSheildFrequencies");
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httpget);
During the connection process, I get the below exception...
Caught: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
When I turn on debugging, I see the below..
main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110
main, handling exception: java.net.SocketException: Connection reset
So it seems like my client did the handshake in TLSv1 but then sent a client hello in SSLv2 which the server didn't like (it dropped the connection because it doesn't support SSLv2 backwards compatibility mode).
Is there any way to tell Apache HttpClient not to do that? Or is this something configured at the underlying JRE (I am using 1.6)?
UPDATE
As bmargulies suggested, I tried to make my own socket factory and configure it to only allow the protocols I want....
def supportedProtocols = new String[2]
supportedProtocols[0] = 'SSLv3'
supportedProtocols[1] = 'TLSv1'
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),supportedProtocols,
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build();
HttpResponse response = client.execute(httpget);
But this gives another exception...
javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
Use the current version of the http components
Use the HttpClientBuilder
Make your own socket factory and configure it to only allow the protocols you want.
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainSocketFactory.INSTANCE)
.register("https", new SSLSocketFactory(sslcontext, hostnameVerifier))
.build();
Remove SSLv2ClientHello from the SSLContext's enabled protocols.
I'm running HttpClient 4.3.6 in Java 6. When I run the following code, the authentication appears to succeed. The Status Code returned is 200. However, I'm getting the following error message in the console:
WARNING: NEGOTIATE authentication error: Invalid name provided (Mechanism level: Could not load configuration file C:\Windows\krb5.ini (the system cannot find the file specified))
How do I eliminate this warning?
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpContext localContext = new BasicHttpContext();
HttpGet method = new HttpGet(url);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(host, 80),
new NTCredentials(userid, password, host, login_domain));
localContext.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider);
String filePath = null;
// Execute the method.
CloseableHttpResponse clientResponse = httpclient.execute(method, localContext);
HttpEntity entity = clientResponse.getEntity();
int statusCode = clientResponse.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getRequestLine());
}
You need to pass in a set of target preferred auth schemes:
Create your httpClient like this:
PoolingHttpClientConnectionManager connPool = new PoolingHttpClientConnectionManager();
connPool.setMaxTotal(200);
connPool.setDefaultMaxPerRoute(200);
// Authentication
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(username, password, workstation, domain));
RequestConfig config = RequestConfig.custom().setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM)).build();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connPool).setDefaultRequestConfig(config).build();
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
Yes I believe that, in fact, your authentication is successful and is probably just falling back to NTLM from Kerberos. My code looks similar to yours and in my application I'm connecting to SharePoint using HttpClient 4.3.5 in Java 7. When SharePoint is configured to "Negotiate" (Attempt Kerberos and then failover to NTLM), I will see a similar error to what you reported in the HttpClient generated logging, specifically:
Selected authentication options: [NEGOTIATE, NTLM]
Executing request GET /my/personal/user2/_api/web?$select=ServerRelativeUrl HTTP/1.1
Target auth state: CHALLENGED
Generating response to an authentication challenge using Negotiate scheme
init XXX.XXX.XXX.XXX:80
NEGOTIATE authentication error: org.ietf.jgss.GSSException, major code: 11, minor code: 0
major string: General failure, unspecified at GSSAPI level
minor string: Desired initLifetime zero or less
Generating response to an authentication challenge using ntlm scheme
Following that, it will successfully authenticate via NTLM. So, I read that error message as saying "Kerberos didn't work, now we'll use NTLM". As long as you're getting a 200 response, you should be good to go.
Are you sure authentication is happening successfully, if the website is set to Negotiate (Attempt Kerbero, then failover to NTLM) BASIC authentication would probably not be successful.
I'm currently trying to do multiple HttpGet requests at the same time with CloseableHttpClient.
I googled on how to do that and the answer was to use a PoolingHttpClientConnectionManager.
At this point I got this:
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cManager)
.build();
Then I tried a HttpGet request to http://www.google.com and everything worked fine.
Then I created a truststore via cmd and imported the certificate of the targeted website, setup a SSLConnectionSocketFactory with my truststore and set the SSLSocketFactory of httpClient:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream(new File("myTrustStore.truststore"));
trustStore.load(inputStream, "nopassword".toCharArray());
inputStream.close();
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cManager)
.build();
If I try to execute a Https HttpGet then I get a PKIX path building failed exception.
If I do the same without .setConnectionManager(cManager) everything works fine.
Can anyone of you tell me how I can get this to work? (Don't worry, I don't create any ddos tool)
Thanks in advance!
P.S.: I'm using HttpComponents 4.3.1
Found the answer:
https://stackoverflow.com/a/19950935/1223253
Just had to add
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();
and pass socketFactoryRegistry as parameter to the constructor of PoolingHttpClientConnectionManager.
Now it works just fine :)