Weblogic REST Client with Jersey HTTPS: Handshake failure - rest

Setup: WL 9.2 + Jersey 1.1.5.1 on WL's Jrockit.
Picked Jersey 1.1.5.1 because newer versions require Java 6, I believe.
Weblogic EJB acts as REST Client and keeps getting this error:
ClientHandlerException: javax.net.ssl.SSLKeyException: [Security:090477]Certificate chain received from svcpoint.restprovider.com - xx.xxx.xxx.xx was not trusted causing SSL handshake failure.
As this just a POC implementation, Weblogic is setup with various flags to ignore cert verification just to make this error go away:
-Dweblogic.security.SSL.ignoreHostnameVerification=true -Dweblogic.security.SSL.enforceConstraints=off -Dweblogic.webservice.client.ssl.strictcertchecking=false
Also, the Jersey config setup includes this bit:
SSLContext ctx = SSLContext.getInstance("SSL");
HTTPSProperties prop = new HTTPSProperties(
new HostnameVerifier () {
public boolean verify(String hostname, SSLSession session) {
System.out.println("\n\nFAKE_Verifier: " + hostname+"\n\n");
return true;
}
}, ctx);
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, prop);
Finally, the sole WL server, technically the admin srv, was configured in the admin console SSL.Advanced settings to not use Hostname Verification.
Now, I'm pretty sure my fake validator setup for Jersey is not actually involved, as I see this error from SSL debug:
<SecuritySSL> <000000> <weblogic user specified trustmanager validation status 16>
<Security> <BEA-090477> <Certificate chain received from svcpoint.restprovider.com - xx.xxx.xxx.xx was not trusted causing SSL handshake failure.>
<SecuritySSL> <000000> <Validation error = 16>
<SecuritySSL> <000000> <Certificate chain is untrusted>
<SecuritySSL> <000000> <SSLTrustValidator returns: 16>
<SecuritySSL> <000000> <Trust status (16): CERT_CHAIN_UNTRUSTED>
<SecuritySSL> <000000> <NEW ALERT with Severity: FATAL, Type: 42
java.lang.Exception: New alert stack
at com.certicom.tls.record.alert.Alert.<init>(Unknown Source)
I've googled and looked at other similar issues here on SO, but I'm probably missing something. Also, from what I can judge the cert seems valid, showing it's for CN=*.restprovider.com, expiring in Nov 2011.

The certificate is untrusted. I think the best solution would be adding it to the Weblogic's trust store using the keytool:
keytool -importcert -trustcacerts ...
You can also do it in code:
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance("SunX509");
trustManagerFactory.init(trustStore);
trustManagers = trustManagerFactory.getTrustManagers();
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagers, trustManagers, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
trustStore - is a keystore containing the certificate

Related

Which cert is used in SSL connection if there are two valid certs in client certificates in socket handshake

There is already a valid cert in my keystore and handshake process with server is working. When another cert with different CN is added in the same keystore and connect with server, get the error from server "Access denied, invalid endpoint". I think when connecting with server, the second cert is used in ssl connection when there are two valid certs for client certs usage (the existing one and the newly imported one). What I want to know is which one is used if there are one than more valid certs in keystore. Is it related with certs alias?
The following is code snippet for socket connection.
try {
SSLSocketFactory factory = null;
try {
SSLContext ctx;
KeyManagerFactory kmf;
KeyStore ks;
char[] passphrase = "*****".toCharArray();
ctx = SSLContext.getInstance("TLSv1.2");
kmf = KeyManagerFactory.getInstance("SunX509");
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("testkeys"), passphrase);
kmf.init(ks, passphrase);
ctx.init(kmf.getKeyManagers(), null, null);
factory = ctx.getSocketFactory();
} catch (Exception e) {
throw new IOException(e.getMessage());
}
SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
String[] cipherSuites = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites( cipherSuites );
socket.setNeedClientAuth(false);
socket.startHandshake();
Similar Which key and certificate from keystore and truststore is used when there are many? for server.
For SunX509 KeyManagerFactory:
at initialization it creates a HashMap containing all the privatekey entries from the keystore, keyed by alias;
handshake calls chooseClientAlias which, for each <=1.2 certificate_type or 1.3 sigalg specified/requested by the server (either called keyType in the code) checks each HashMap entry in HashIterator order (which is determined by a variable number of low bits of a value derived from the hashcode of the alias) to see if the leaf cert has that keytype, and any cert in the chain is issued by one of the CAs specified by the server unless the server left the CA list empty in which case this part of the check is skipped.
If you're getting the wrong cert-and-key selected, check if the server is specifying a correct CA-list in its CertificateRequest message. You can do this on the Java client side by running with sysprop javax.net.debug=ssl:handshake, or except in 1.3 a network level tool like wireshark or tcpdump.

MailKit gets an SslHandshakeException with LetsEncrypt SSL certificates

I have a server (Centos 7) setup to be used as mail server. Using postfix/dovecot/opendkim/opendmarc..
It works as it should, users are able to connect their emails using gmail for example. Able to send and receive mail.
Also when I use MailKit and test my .NET Core application from my home pc MailKit connects fine and the emails are send.
However, when I deploy the application to my server MailKit fails to connect.
If I look in the logs I see the following
postfix/submission/smtpd[4486]: match_hostname: unknown ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostname: unknown ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: lost connection after STARTTLS from unknown[MY_SERVER_IP]
But if I look a bit higher in the logs I see
Anonymous TLS connection established from unknown[MY_SERVER_IP]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
My MailKit (which works fine from outside of the server):
using (SmtpClient emailClient = new SmtpClient())
{
await emailClient.ConnectAsync(emailConfiguration.SmtpServer, emailConfiguration.SmtpPort, SecureSocketOptions.StartTls);
emailClient.AuthenticationMechanisms.Remove("XOAUTH2");
await emailClient.AuthenticateAsync(emailConfiguration.SmtpUsername, emailConfiguration.SmtpPassword);
await emailClient.SendAsync(message);
await emailClient.DisconnectAsync(true);
}
edit:
The exception from MailKit (certificate is proper and not self-signed):
MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
May 19 16:07:37 domain.com NETCoreApp[4452]: The server's SSL certificate could not be validated for the following reasons:
May 19 16:07:37 domain.com NETCoreApp[4452]: • The server certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • The root certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get local issuer certificate
May 19 16:07:37 domain.com NETCoreApp[4452]: ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
The unable to get certificate CRL error sounds like SslStream was unable to get the CRL, perhaps because the CRL server is unreachable for some reason.
You could try adding emailClient.CheckCertificateRevocation = false; before the ConnectAsync to check if that's the issue.
The other error, unable to get local issuer certificate, might be because the server that MailKit is running on doesn't have the Root CA certificate in its X509Store but your home PC does.
Update:
The problem is that LetsEncrypt SSL certificates do not include a CRL location which means that certificate revocation checks will fail.
To bypass this, you need to set client.CheckCertificateRevocation = false; before connecting.
I found an answer which works but isn't my preferred method since I wanted to be able to use MailKit for more that just my own server (make it configurable from within the app itself)
I came to the solution because I thought it had to do with some internal traffic going wrong..
By using the old SmtpClient from System.Net.Mail I was able to use the DefaultCredentials.
using (SmtpClient client = new SmtpClient("127.0.0.1"))
{
client.UseDefaultCredentials = true;
MailAddress from = new MailAddress(emailMessage.FromAddress.Address, emailMessage.FromAddress.Name);
foreach (IEmailAddress emailAddress in emailMessage.ToAddresses)
{
MailAddress to = new MailAddress(emailAddress.Address, emailAddress.Name);
MailMessage email = new MailMessage(from, to)
{
Subject = emailMessage.Subject,
Body = emailMessage.Content
};
await client.SendMailAsync(email);
}
}
I have the same problem on ubuntu 20.04 with .NET core 3.1
and after 3 hours of trial and error, I finally found the solution.
I've just ignored the Certificate Validation CallBack.
using var client = new SmtpClient(new ProtocolLogger("smtp.log"));
client.CheckCertificateRevocation = false;
client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
client.Connect("your.smtp.host", 587, SecureSocketOptions.StartTls);
I hope this would be helpful :)

SSL certificate related issue while calling rest servcies

From client (eg: https://localhost:8080/) we are passing the certificate related values and calling the rest services ( hosted on different port - https://localhost:446/serviceName).
The issue is like, when we try to pass the certificate , SSL handshake is happening correctly (no error on debug) , but the certificate value is not passed to the service hosted on another port. Certificate value is accessed in server code by referring to (X509Certificate)httpReq.getAttribute("javax.servlet.request.X509Certificate");
Note : We use Spring boot application which intenally runs on tomcat server.And desired CA authorised certificates, keystore and truststore are present in resource path in both the projects (client and service hosted). In rest service project config file, the client-auth is set to false.
Sample code snippet used to call rest service:
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(restserviceTruststore)
.loadKeyMaterial(restserviceKeyStore, password).build();
HttpClient client = HttpClients.custom() .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.setSslcontext(sslContext).build();
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(client));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String> request = new HttpEntity<>(XML, headers);
response = restTemplate.postForObject(endpointURL, request, String.class);
Question:
1) From client what keystore and trustore should we need to pass to SSLContext? Is it server's keystore /truststore or clients?
2)What are the exact steps to be followed to resolve this issue.

Spring Security X.509 Preauth

I'm using Spring Security 2.x's Preauthentication with X.509 certificates.
I get the certificateText via HttpServletRequest.getAttribute("CERTIFICATE").
Sometimes, the above call returns "" (empty). I believe it occurs when the HTTP session has expired.
What would explain why HttpServletRequest.getAttribute("CERT") returns empty?
EDIT In Kerberos, for example, the ticket is available in every HTTP request. Is the cert not always in X.509 HTTP requests?
Please access to certificate using this code:
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
Certificate is always populated to request after successful client certificate authentication.
Ensure your support long certificate chain:
Add the max_packet_size propery to the worker.properties file
worker.ajp13w.max_packet_size=65536
Add the packetSize propery to the configuration of Ajp connector in the Tomcat configuration \conf\server.xml
<Connector port="8089"
enableLookups="false" redirectPort="8443" protocol="AJP/1.3" packetSize="65536"/>
Apache logs:
http://httpd.apache.org/docs/2.2/logs.html#accesslog
http://httpd.apache.org/docs/2.2/logs.html#errorlog
http://httpd.apache.org/docs/2.2/mod/core.html#loglevel

Does RESTeasy client support TLS/SSL?

I'm using several RESTful webservice in JAVA based web-application. I'm using the RESTeasy client to access my webservice. Here all communication between the client and service is through XML(JAX-B xml annotated detail classes). Here are the following codes
String serviceURL = "https://service.company.com/Service/getService"
ServiceRequestDetail serviceRequestDetail = getServiceRequestAsDetailClass();
ServiceResponseDetail serviceResponseDetail = new ServiceResponseDetail();
ClientRequest clientRequest = new ClientRequest(serviceURL);
clientRequest.accept(MediaType.APPLICATION_XML);
clientRequest.body(MediaType.APPLICATION_XML, serviceRequestDetail);
ClientResponse<ServiceRequestDetail> response =
clientRequest.post(ServiceRequestDetail.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " +
response.getStatus());
}
ServiceResponseDetail serviceResponseDetail =
response.getEntity(ServiceResponseDetail.class);
and when I try to access my service I get the "Peer not Authenticated" error
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
...
Is there any way to add the SSL configuration details in the RESTeasy client? any other suggestions for solving this issue is also welcome
Thanks in advance
I found out the answer but I'm really sorry for the late response.
To answer my question, RESTeasy client does support TLS/SSL. Infact the problem was I missed to install the certificate into the JVM.
keytool -import -alias <Replace certificate Alias name> -keystore $JAVA_HOME\jre\lib\security\cacerts -file <Replace your Certificate file location>
This solved the issue of "Peer Not Authenticated". Hope it helps. Kudos
If you don't want to add certificate to JVM and keep this cert separate. You can load the cert as part of your code like below.
// load the certificate
InputStream fis = this.getClass().getResourceAsStream("file/path/to/your/certificate.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(fis);
// load the keystore that includes self-signed cert as a "trusted" entry
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyStore.setCertificateEntry("cert-alias", cert);
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);`
then attach to rest easy builder like
resteasyClientBuilder.sslContext(sslContext)