I am writing a NodeJS proxy that will replace a Java Jetty Proxy. I am using node-http-proxy. The only piece remaining is to have the original client certificate passed along to the proxied server.
From my understanding, the Java Servlet specification requires that a Servlet container pull the Client Certificate from an HTTPS request and store that as an attribute on the HttpServletRequest.
I am not sure how the Servlet Container handles the Attributes when proxying the request to a new server. I presume that it is attaching them somehow either as headers or by some other means.
Does anyone know how those attributes (specifically the javax.servlet.request.X509Certificate) are passed on a proxied HTTPS request? And two, how do I achieve the same functionality using NodeJS.
In the event that is helps someone else out... The issue turned out to be the node module I was using (node-http-proxy) wasn't reusing the HTTP server connection certificates. That is, when attempting to create a connection with the proxy server, it was using a default (generated) certificate.
To properly connect with the proxy server, I had to pass the ca, pfx, and passphrase to the proxy connector.
const ca = ...
const pfx = ...
const passphrase = ...
// proxy connection
server.web(req, res, { ca: ca, pfx: pfx, passphrase: passphrase }, function(err) {});
After doing so, the Proxy server was able to pull and validate the certificate.
Related
Is there a way to specify the connection method when using a proxy?
I'm using the below code which sends an HTTP CONNECT. That is not supported by my load balancer. A GET request would terminate the TLS connection between the proxy and the website. The CONNECT method creates a TLS connection end to end between the end user and website. Essentially I need to inspect the traffic at the proxy.
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
clientBuilder
.setConnectionManager(connectionManager)
.setProxy(proxy)
.setDefaultRequestConfig(config);
Below is what the connection looks like:
Hypertext Transfer Protocol
CONNECT xyz.com:443 HTTP/1.1\r\n
Host: xyz.com\r\n
User-Agent: Apache-HttpClient/4.5.13 (Java/19.0.1)\r\n
\r\n
For HTTPS communications through a proxy, the client will use a CONNECT request to create a connection through the proxy to the remote trusted site.
With HTTPS, the client will only communicate with a server that it trusts. The trust usually requires that the server presents an X.509 certificate that is signed by a trusted authority (not self-signed), not expired, and has a subject that matches the site the client is trying to reach.
When communicating with a proxy, the proxy is typically unable to match that criteria. However, there are proxies that can match the above criteria by generating a new cert on the fly that has a matching subject to that expected by the client.
In that case, once the client has established a TLS connection with the proxy, the proxy can then decrypt the payload of the HTTPS traffic.
For the proxy to create a certificate trusted by the client, it must be signed by a CA (certificate authority) that is trusted by the client. Now, by default, the client will not trust the cert since it will not recognize the CA. However, you may modify the client by placing a cert for the CA on the client. Once the client trusts the CA, it will trust all of the certs generated by the proxy.
That being said, here's configuration for an HTTPS proxy (written by Cisco) that provides instructions for just that:
https://www.cisco.com/c/en/us/support/docs/unified-communications/jabber/212044-Configure-Charles-Proxy-to-Capture-HTTPS.html
The configuration action you'll need for Apache (if Apache supports this) will be similar - you'll need to get a cert from the Apache proxy to install on your client.
I'm currently searching the web for similar proxies. I'm essentially looking for a key word that describes this technique of enabling your HTTPS proxy to do man-in-the-middle attacks with a dynamically generated cert matching the request. If I find the terms, I'll update the post.
EDIT: Update for squid
I found an article about configuring squid for inspecting HTTPS traffic: https://scubarda.com/2020/03/23/configure-squid-proxy-for-ssl-tls-inspection-https-interception/
In the article, the author indicates that you would have to install the X.509 Cyber Saiyan certificate in your browser (client's) certificate/authorities section (but this is just for this specific configuration).
Some good keywords to search for now are HTTPS inspection...
If this is a temporary debug solution, you can use one of the Man In The Middle (MITM) proxies that are designed just for this:
Burp
Charles
Zap
Once installed somewhere, you'll just need to import the proxy CA cert into the server making the connections (each of the above proxies has a how-to guide for this), and set the proxy address to the MITM.
I have an Java Web Application, which consumes different WebServices.
Now I have to consume one WebService that needs both way authentication.
I use SSLClientAxisEngineConfig implementation (https://github.com/linhkuivanen/axistools) that I send as parameter to my class that extends org.apache.axis.client.Service.
It worked.
But only if it is the first use of some Axis Client.
After that, if I consume a WebService with 'https' that does not require certificate, I got error "Untrusted Server Certificate Chain".
If I consume first a Webservice that does not require certificate authentication, when I try to consume the other one that need authentication, I got "Handshake failure" error.
I assume that Axis has some kind of cache that reuses the first configuration, but I cannot figure out a way to solve my problem.
Edit: I solved the problem.
I generated a new Axis Client with wsdl2java (Axis2-1.7.8), instead of using the Eclipse Web Service Client generator. Before consume the WebService, I register a new https protocol with client certificate and cacerts:
Protocol protocol = new Protocol("https", socketFactoryDinamico, 443);
Protocol.registerProtocol("https", protocol);
and after using it, I unregister the protocol:
Protocol.unregisterProtocol("https");
After this, I can consume other WebServices without certificate authentication without problems.
Edit2:
The classes generated by Eclipse:
public class NfseWSServiceLocator extends org.apache.axis.client.Service implements NfseWSService {
}
public interface NfseWSService extends javax.xml.rpc.Service {
}
And the class generated by Axis2:
public class NfseWSServiceStub extends org.apache.axis2.client.Stub {
}
"Untrusted Server Certificate Chain" means that your client doesn't trust the server certificate that is trying to establish a connection. In order to properly configure a HTTPS enabled communication scenario, both communication parties must trust each other.
The RFC 5246 defines the following:
unknown_ca
A valid certificate chain or partial chain was received, but the
certificate was not accepted because the CA certificate could not
be located or couldn't be matched with a known, trusted CA. This
message is always fatal.
Usually is the client that requires to trust the server certificate chain to establish the connection, since the server could be operating in anonymous mode not requesting a client certificate from the peer.
I am new to JAX-WS SOAP Web Service, Can any one help me how to consume soap web service over https.
Publisher has shared a ssl certificate(.cer) with me, imported this in a TrustStore using a keytool command.
Now to call saop webservice over a https secure layer I need the .jks file to set it as System Environment Variable.
I followed many resources How can i create keystore from an existing certificate (abc.crt) and abc.key files?
but it is expecting serverprivatekey.key file.
I do not have private key with me, please help how to proceed.
am I on the right way? or Something wrong in my approach?
You do not need the private key of the server. You only need to import the public key certificate.cer in a truststore. (Really you only need the root certificate). If you have already done on a JKS, configure to use it through
System.setProperty("javax.net.ssl.trustStore",path/to/your/truststore);
System.setProperty("javax.net.ssl.trustStorePassword",password;
Note: The trustmanager of the HTTPS connection will verify the identity of the server during the handshake checking that the signature perfomed by server corresponds to the public key. So the private key must be private and you should not access to it.
I am writing a java rest-client to fetch data from the web App. I am trying to use client authentication(login using client certificate) mechanism using spring's RestTemplate to get authenticated on server.
Here are the steps that I did.
Server side:
Configure the web server to allow certificate based login. Set "clientAuth"= "want" in server.xml
Create a clients x509 certificate and add it into servers truststore.
Client Side
Initialize RestTemplate with clients trustStore and keyStore. For this I used org.apache.commons.httpclient.contrib.ssl.AuthSSLProtocolSocketFactory.
AuthSSLProtocolSocketFactory is initialized with client keystore in PKCS12 format, and client truststore (jks format).
Code looks like this:
CommonsClientHttpRequestFactory factory = (CommonsClientHttpRequestFactory) restTemplate.getRequestFactory();
HttpClient client = factory.getHttpClient();
client.getState().setAuthenticationPreemptive(false);
Protocol myHttps = new Protocol(HTTPS, secureProtocolSocketFactory, DEFAULT_HTTPS_PORT);
Protocol.registerProtocol(HTTPS, myHttps);
client.getHostConfiguration().setHost("localhost", DEFAULT_HTTPS_PORT, myHttps);
When I am making rest calls into server I am expecting the request to get authenticated by certificate. But I am getting http 403-forbidden error. It seems that restTemplate is trying to authenticate the request by basicCredentials, where my intent is to authenticate using certificate.
Following is the output logged by http client:
MultiThreadedHttpConnectionManager:390 - HttpConnectionManager.getConnection: config = HostConfiguration[host=https://<server-ip>], timeout = 0
MultiThreadedHttpConnectionManager$ConnectionPool:739 - Allocating new connection, hostConfig=HostConfiguration[host=<server-ip>]
HttpMethodDirector:160 - Preemptively sending default basic credentials
HttpMethodDirector:277 - Authenticating with BASIC <any realm>#<server-ip>:443
HttpMethodParams:355 - Credential charset not configured, using HTTP element charset
HttpConnection:691 - Open connection to 10.112.253.152:443
HttpMethodBase:1235 - Adding Host request header
RestTemplate:559 - GET request for "https://<server-ip>:443/url/" resulted in 403 (Forbidden)
Is there anything else that needs to be configured on RestTemplate to get authenticated using client certificate?
I have configured the SSL configuration for my jboss.
-Djavax.net.ssl.keyStore=/opt/appserver/jboss5.1/test.jks
-Djavax.net.ssl.keyStorePassword=*********
-Djavax.net.ssl.trustStore=/opt/appserver/jboss5.1/test.jks
-Djavax.net.ssl.trustStorePassword=*******
I am using the spring integration to connect to restservice, the ssl certificate is getting passed for some request and it is not getting passed for some other request, Any idea why the certificate will not be passed for one request?
It is the servers responsibility to request the client to send a certificate during mutual authentication.
When you have time, verify that the server has the ssl 'clientAuth' attribute set 'true' for all the urls that you want mutual authentication (client authentication).