Error while calling secured webservice from Mule Project - mule

Need to call a secured web service from one of my Mule Project.For that I have created a self signed certificate and configured my projects follows:
1)Keep the keystore.jks inside the resource folder of my Project
2)Defined a https connector in the Globals.xml file
<https:connector name="movilcashHttpsConnector" doc:name="HTTP-HTTPS" clientSoTimeout="10000" cookieSpec="netscape" receiveBacklog="0" receiveBufferSize="0" sendBufferSize="0" serverSoTimeout="10000" socketSoLinger="0" validateConnections="true">
<https:tls-key-store path="keystore.jks" keyPassword="changeit" storePassword="changeit" />
<https:tls-server path="keystore.jks" storePassword="changeit" />
</https:connector>
3) While calling the https endpoint I have used the following configuration
<https:outbound-endpoint method="POST"
doc:name="HTTP" exchange-pattern="request-response" transformer-refs="Message_Properties2"
responseTimeout="90000"
address="https://partners.masmovilcash.com/TelepinWeb/TpinBulkLoading?BatchType=7&FileExt=PNG&FileAbbrv=C_R&FileType=KYC_Documents&CustomerID=#[flowVars['tpin-customerid']]&FileSequence=2"
connector-ref="movilcashHttpsConnector">
</https:outbound-endpoint>
4)Deployed the project in a Linux server where mule standlaone is running.
But unfortunately each and every time I am getting the following error:
Message : Failed to route event via endpoint: DefaultOutboundEndpoint{endpointUri=https://partners.masmovilcash.com/TelepinWeb/JSON-RPC, connector=HttpsConnector
Message payload is of type: PostMethod (org.mule.api.transport.DispatchException)
sun.security.validator.ValidatorException: No trusted certificate found
at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:384)
Am I missing something?Please help.

Since you want to perform an HTTPS request to an external service (https://partners.masmovilcash.com/TelepinWeb/JSON-RPC) what you need to make sure is that your application trusts the service certificate. That means that if you set a trust store for your HTTPS connector, said trust store needs to contain the service certificate.
Considering I've been able to access the service you need from my browser, it's probably signed by a proper certificate authority (CA) which means the default Java trust store should work. That means, try not setting any trust store/key store data so that the JVM defaults are used instead. Something like:
<https:connector name="movilcashHttpsConnector" doc:name="HTTP-HTTPS" clientSoTimeout="10000" cookieSpec="netscape" receiveBacklog="0" receiveBufferSize="0" sendBufferSize="0" serverSoTimeout="10000" socketSoLinger="0" validateConnections="true"/>

Related

Tomcat 9.x.x Client Authentication using X.509 Certificates

I’m using Tomcat 9.0.19 and trying to enable X.509 cert.-based client authentication (AKA I&A) for a particular Web application.
In summary, the Tomcat works for an application that has basic I&A enabled over one-way TLS. When accessing the Web application that has certificate-based I&A, Tomcat does not seem to request a client certificate as part of the Server Hello message, prior to sending Server Hello Done and it later fails the authentication check:
02-Jan-2020 13:00:40.371 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate Looking up certificates
02-Jan-2020 13:00:40.830 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate No certificates included with this request
Traced the TLS flow in Wireshark and saw the TLS 1.2 handshake. Shortly after encrypted data is exchanged, the Tomcat sends an “Encrypted Alert” message and the socket is closed. Trying to contact the Tomcat from the browser, doing a GET. The browser does not prompt me to select a certificate, which also seems to point to Tomcat not requesting it from the browser.
Any help will be greatly appreciated!
More Details:
We have a set of certificates for the Tomcat and the client, issued by an Intermediate CA, which is signed (issued) by a Root CA. The trust stores have been setup on both sides (client and server) as well as key stores with the right certs/keys in them. The Web application is setup to require certificate I&A (web.xml):
<security-constraint>
<web-resource-collection>
<web-resource-name>All by default</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>OTService</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate</realm-name>
</login-config>
The OTService role is setup in the Tomcat-Users.xml, along with a single user account:
Now, the Connector in server.xml is configured as follows:
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="100" SSLEnabled="true" scheme="https" secure="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/apache-tomcat-9.0.19/conf/km/keyStore.jks"
certificateKeystorePassword="PASSWORD"
certificateKeyAlias="tomcat"
type="RSA" />
truststoreFile="/apache-tomcat-9.0.19/conf/km/trust_store.jks"
truststorePass="PASSWORD"
truststoreType="JKS"
certificateVerification="required"
clientAuth="true"
protocols="TLSv1.2"
</SSLHostConfig>
</Connector>
Any ideas why Tomcat would not request a client certificate?
The first issue that I discovered was that Tomcat ignored the Connector->SSLHostConfig settings for the trust store and used the JRE default trust store anyway. The way I discovered it was to have a browser save the negotiated TLS session key to a file (Google SSLKEYLOGFILE), then configured the Wireshark to use that file, captured the browser-Tomcat session and then was able to see every message in plaintext.
Next, I discovered that Tomcat was actually asking for a client cert., but the list of accepted Root CAs it was sending was from the default JRE cacerts file, not from the file specified by the truststoreFile attribute. Can have Tomcat use a different file across the board by adding a setenv.sh file to the Tomcat bin directory with Java properties to override default trust store location.
Now, I was in business, the browser was able to complete the TLS handshake, but then the authentication and authorization steps were failing. I finally determinate that the proper way to provide the cert. subject field in tomcat_users.xml file was not "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, S=AZ, C=US", but "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, ST=AZ, C=US". Finally, I had 2-way TLS working.
One thing to keep in mind is if anything running on the Tomcat attempts to connect over TLS to another system that uses commercial CA certs, it will fail because the truststore you're using now does not have commercial Root CAs' certs. One way to remediate it is to make a copy of the default JRE cacerts file and add your system-specific CA cert(s) to it and point to it from the setenv.sh file noted above.
When you have:
<Connector ...>
<SSLHostConfig>
<Certificate A=1 B=2 C=3 />
D=4 E=5 F=6
</SSLHostConfig>
</Connector>
then A,B,C are attributes of the Certificate object but D,E,F are NOT attributes of the SSLHostConfig object -- they are XML content which is different. Attributes need to be put IN THE TAG:
<Connector ... >
<SSLHostConfig certificateVerification="required" truststoreFile=... >
<Certificate ...keystore... />
</SSLHostConfig>
</Connector>
and that does cert-request on the initial handshake as desired (for me, tested on tomcat 9.0.14).

Https soap service in mule

I have a requirement where soap based service needs to be consumed and in order to achieve that I had used WSConsumer component in Mule.
Where we give the service URL in the properties file and refer to it.
<ws:consumer-config name="Web_Service_Consumer" wsdlLocation="serviceApi.wsdl"
service="serviceAPI" port="serviceApiSoap12Port" serviceAddress="${serviceurl}"
doc:name="Web Service Consumer"/>
Now after the development we came to know that QA web service is a HTTPS web service and while hitting the service following exception is received
SSLHandshakeException: General SSLEngine problem
I'm able to hit the service with Http:request connector as following
<http:request-config name="HTTP_Request_Configuration" host="${host}" port="${port}"
doc:name="HTTP Request Configuration" protocol="HTTPS">
<tls:context>
<tls:trust-store insecure="true" />
</tls:context>
</http:request-config>
But the issue with above config is it won't work for HTTP configuration.
I should be able to connect to both HTTP & HTTPS as I have different environments.
Is there a way where I can achieve this with ws:consumer??
I assume the HTTP connector configuration shown is the one queried by your WS Consumer connector. A simple solution would be to configure the protocol of your HTTP config via property file but Mule doesn't play well with it because you may end-up with a TLS Context configured with HTTP and your connector won't work. There is a little trick you can do however: create 2 HTTP configurations - one for HTTP and one for HTTPS:
<http:listener-config name="HTTPS_Config"
protocol="HTTPS"
host="${host}"
port="${port}"
doc:name="HTTPS Config" >
<tls:context>
<tls:key-store type="${keystore.type}"
path="${keystore.path}"
keyPassword="${keystore.keyPassword}"
password="${keystore.password}"/>
</tls:context>
</http:listener-config>
<http:listener-config name="HTTP_Config"
host="${host}"
port="${port}"
doc:name="HTTP Config" >
</http:listener-config>
In a property file, define which configuration (i.e. which protocol) should be used:
host=localhost
port=443
protocol=HTTPS # or HTTP
And finally in your flow, use your property to reference the proper HTTP(S) config:
<flow name="http-testFlow">
<http:listener config-ref="${protocol}_Config" path="/test" doc:name="HTTP"/>
...
</flow>
The trick is to name your configuration ${protocol}_Config such as HTTP_Config and HTTPS_Config so the proper one is used at runtime via config-ref="${protocol}_Config". Mule will then dynamically use the proper configuration when your app is launched. This is entirely transparent for the user as only the protocol (HTTP vs. HTTPS) needs to be configured, and it can be used in any environment.
You can use this differently, the idea is to have Mule pick your HTTP or HTTPS dynamically at runtime.
EDIT: to configure your WS Consumer with HTTPS you'll need to reference a proper HTTP Requester Configuration such as:
<ws:consumer-config name="Web_Service_Consumer"
...
connectorConfig="HTTP_Request_Configuration"/>
<http:request-config name="HTTP_Request_Configuration"
...
<tls:context>
...
</tls:context>
...
/>
See Web Service Consumer documentation for details.
Note: though this solution works I would recommend using HTTPS all the time, for security reasons and to have less differences between your dev/QA/Prod/... environments - among other things.
I faced similar issues, we finally chose to have only an HTTPS config and define via property which keystore to use at runtime. In dev we would use a self-signed certificate and a proper keystore in environments requiring proper security.
Hope this helps.

SSL config for outbound connections doesn't work in websphere-liberty 17.0.0.2

I'm trying to configure websphere-liberty server to use default keystore and trustore for all outbound connections (actually REST calls) and for inbound use a custom key and trust stores. But it fails with SSLHandshakeException when try to make a call to external REST service. In logs I can see that it uses my custom truststore instead of default one.
Below is my server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="Default server">
<featureManager>
<feature>appSecurity-2.0</feature>
<feature>transportSecurity-1.0</feature>
<feature>jaxrs-2.0</feature>
<feature>json-1.0</feature>
<feature>javaMail-1.5</feature>
<!--<feature>ssl-1.0</feature>-->
</featureManager>
<sslDefault sslRef="saasSSLConfig" outboundSSLRef="outboundSSLConfig" />
<ssl id="saasSSLConfig" keyStoreRef="saasKeyStore" trustStoreRef="saasTrustStore" clientAuthentication="true" sslProtocol="TLSv1" />
<keyStore id="saasKeyStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/sbs_endpoint_keystore.jks" password="pwd" />
<keyStore id="saasTrustStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/serverTruststore.jks" password="pwd" />
<ssl id="outboundSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="defaultTrustStore" />
<basicRegistry id="basic" realm="BasicRealm">
<!-- <user name="yourUserName" password="" /> -->
</basicRegistry>
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />
<applicationManager autoExpand="true"/>
</server>
BTW if change saasSSLConfig to use defaultTrustStore instead of saasTrustStore then everything works fine.
Server version:
WebSphere Application Server 17.0.0.2 (1.0.17.cl170220170523-1818) on IBM J9 VM, version pxa6480sr4fp7-20170627_02 (SR4 FP7) (en_US)
Error:
[ERROR] CWPKI0022E: SSL HANDSHAKE FAILURE: A signer with SubjectDN CN=*.api.ibm.com, O=International Business Machines, L=Armonk, ST=New York, C=US was sent from the target host. The signer might need to be added to local trust store /opt/ibm/wlp/output/defaultServer/resources/security/serverTruststore.jks, located in SSL configuration alias saasSSLConfig. The extended error message from the SSL handshake exception is: PKIX path building failed: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.;
SSLHandshakeException invoking https://dev.api.ibm.com/scx/test/sbs/customer/222222222: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
Liberty does not load cacerts automatically. You can create a keyStore element to point to it if desired. So in your case above you can create a configuration like this.
<ssl id="outboundSSLConfig" keyStoreRef="cacertKeyStore" />
<keyStore id="cacertKeyStore" location=<fill in path to your jdk cacerts file> password="changeit" />
I am assuming you do not need a key for this configuration so I simplified to just a keyStoreRef on outboundSSLConfig. It will use what is pointed to by keyStoreRef for both key and trust.
In your configuration I do not see keyStore elements for defaultKeyStore and defaultTrustStore. If they are missing that will cause outboundSSLConfig to be an invalid SSL configuration. Can you please add them and see if things work.

Client authentication on JBoss server

I'm trying to configure client authentication for my application running on JBoss. Expected result is that application requests user for certificate and if trusted one is provided, he will be able to work with application.
I've generated certificate and added one into trustore (JBoss.keystore) and also configured standalone.xml file as follow:
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" enable-lookups="false" secure="true">
<ssl name="ssl" key-alias="ssl alias" password="password" certificate-key-file="..\standalone\configuration\JBoss.keystore" protocol="TLSv1.2" verify-client="true"/>
<virtual-server name="my-host" />
</connector>
I thought that setting secure property true will do the trick, but browser does not ask for user certificate, but immediately returns error ERR_BAD_SSL_CLIENT_AUTH_CERT. Browser is configured to ask for certificate each time, if required.
How to change server configuration into expected behavior?
I have found an answer. The solution was to import CA certificate that signed client certificate into truststore, instead of importing client certificate itself.
After importing CA certificate, each certificate that was signed by CA and imported into browser is displayed to be chosen from.

Mule 2 Way HTTPS Authentication

I have a Mule application which needs to talk an external server over HTTPS using 2 way SSL authentication.
My question is: How to enable Server Certificate verification in Mule ? It seems, by default Mule doesn't verify Server's Cert. I am using Mule v 3.3.0.
For example: '-k' option in curl disables server's cert verification.
In similar way, is there any configuration parameter by which I can enable/disable Server's cert verification ?
Thanks
Jai
For Mule versions before 3.6, the HTTP transport supports configuration of the trust store used by the HTTP outbound endpoint to determine whether the certificate presented by an HTTPS server should be trusted.
To provide a key store that contains the certificates of servers you need to trust, provide the <https:tls-server /> to the <https:connector />, and reference that connector in your <https:outbound-endpoint />:
<https:connector name="myHttpsConnector">
<https:tls-server path="truststore.jks" storePassword="supersecure" />
</https:connector>
<flow name="someFlow">
<https:outbound-endpoint host="remote-host" port="443" path="/api" connector-ref="myHttpsConnector" />
</flow>
The blog post linked in clare's answer explains this a bit, from both the server side and the client side. You can also refer to the HTTPS Transport Reference, although it doesn't explain the use of tls-server vs. tls-client.
You can check out this blogpost. It will show you how you can configure mutual authentication in Mule. HTH.
You can configure the HTTP Request connector in Mule >= 3.6.0 to use a trust store you create that contains the certificate(s) of the server(s) you need to trust using TLS Configuration.
It might look something like this:
<tls:context name="tlsContextForServiceFoo">
<tls:trust-store path="serviceFooServerCertificates.jks" password="supersecure"/>
<tls:key-store path="myClientCertificates.jks" keyPassword="extrasecure" password="ultrasecure"/>
</tls:context>
<http:request-config name="twoWayAuthServiceFooConfig"
protocol="HTTPS"
host="services.pentagon.gov"
port="443"
tlsContext-ref="tlsContextForServiceFoo" />
<flow name="useServiceFoo">
<http:request config-ref="twoWayAuthServiceFooConfig" path="/api/doStuff" method="POST" />
</flow>