Enable SSL certificate revocation checking in OpenJDK 11 - ssl

Is there some quick "declarative" way in Java 11, instead of a tedious manual implementation, to enable checking if a certificate is revoked?
I tried to use properties from this answer:
Check X509 certificate revocation status in Spring-Security before authenticating
with this dummy revoked certificate: https://revoked.badssl.com
but the code always accepts the certificate. Am I doing something wrong or these properties are no more actual for Java 11? If so, do we have any alternatives?
Below is my code:
public static void validateOnCertificateRevocation(boolean check) {
if (check) {
System.setProperty("com.sun.net.ssl.checkRevocation", "true");
System.setProperty("com.sun.security.enableCRLDP", "true");
Security.setProperty("ocsp.enable", "true");
}
try {
new URL("https://revoked.badssl.com").openConnection().connect();
} catch (IOException e) {
e.printStackTrace();
}
}

It seems like those options have to be set before the first request has been performed.
Therefore the following code as standalone Java program throws an CertPathValidatorException: Certificate has been revoked (tested using OpenJDK 11.0.2 x64 on Windows):
public static void main(String[] args) {
validateOnCertificateRevocation(true); // throws CertPathValidatorException
}
However the following code does not cause any errors/Exceptions:
public static void main(String[] args) {
validateOnCertificateRevocation(false);
validateOnCertificateRevocation(true); // nothing happens
}
You can see the changing the options after the first request has been processed isn't effective. I assume that those options are processed in a static { ... } block of some certificate validation related class.
If you still want to enable/disable certificate revocation checking on a per-request base you can do so by implementing your own X509TrustManager that uses CertPathValidator (for which you can enable/disable certificate revocation checking via PKIXParameters.setRevocationEnabled(boolean).
Alternatively there is the solution to globally enable certificate revocation checking and explicitly handle the CertificateRevokedException:
private boolean checkOnCertificateRevocation;
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
try {
getDefaultTrustManager().checkServerTrusted(certs, authType);
} catch (CertificateException e) {
if (checkOnCertificateRevocation) {
if (getRootCause(e) instanceof CertificateRevokedException) {
throw e;
}
}
}
}

Related

Fabric Crashlytics Error javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException

This Error is related to Fabric Crashlytics, not direct RestFul API consumptions.
I'm getting this error when initiating the Fabric Crashlytics on Android Emulator
E/Fabric: Settings request failed.
io.fabric.sdk.android.services.network.HttpRequest$HttpRequestException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at io.fabric.sdk.android.services.network.HttpRequest.code(HttpRequest.java:1355)
at io.fabric.sdk.android.services.settings.DefaultSettingsSpiCall.handleResponse(DefaultSettingsSpiCall.java:104)
at io.fabric.sdk.android.services.settings.DefaultSettingsSpiCall.invoke(DefaultSettingsSpiCall.java:88)
at io.fabric.sdk.android.services.settings.DefaultSettingsController.loadSettingsData(DefaultSettingsController.java:80)
at io.fabric.sdk.android.services.settings.DefaultSettingsController.loadSettingsData(DefaultSettingsController.java:64)
at io.fabric.sdk.android.services.settings.Settings.loadSettingsData(Settings.java:153)
at io.fabric.sdk.android.Onboarding.retrieveSettingsData(Onboarding.java:126)
at io.fabric.sdk.android.Onboarding.doInBackground(Onboarding.java:99)
at io.fabric.sdk.android.Onboarding.doInBackground(Onboarding.java:45)
at io.fabric.sdk.android.InitializationTask.doInBackground(InitializationTask.java:63)
at io.fabric.sdk.android.InitializationTask.doInBackground(InitializationTask.java:28)
at io.fabric.sdk.android.services.concurrency.AsyncTask$2.call(AsyncTask.java:311)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:563)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:444)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:508)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:401)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:375)
at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:304)
at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
E/Fabric: at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178)
at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:596)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
... 30 more
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
... 42 more
E/Answers: Failed to retrieve settings
Any Idea how to resolve this, or add custom SSlSocketFactory to Fabric HttpRequest class?
For anyone else experiencing this issue: This error occurred for me when the wifi password was outdated and my device switched to a different (open) network. Try switching to another (closed) network.
Ran into the same issue, the same Crashlytics setup in Fabric and Firebase, tried with different versions of Crashlytics up to 2.9.2, it worked fine on API 22+, but not on API 21 and below. In the end, it is resolved by a workaround, not ideal and not recommended for production, which is bypassing the ssl check all together by calling the function below in the onCreate() in app's Application class.
public void trustAllCertificates() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
}
}
References: https://androidlad.blogspot.com/2017/08/how-to-trust-all-certificates-or-bypass.html

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.NETWORK

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.NETWORK
Hi i got this error while i am calling one API service from retrofit , i am searching a lot and found answer like
private static void setupRestClient() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(ROOT)
//.setClient(new OkClient(new com.squareup.okhttp.OkHttpClient()))
//.setClient(getOkClient())
.setClient(setSSLFactoryForClient(new com.squareup.okhttp.OkHttpClient()))
.setRequestInterceptor(new SessionRequestInterceptor())
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog(NetworkUtil.APP_TAG))
.build();
REST_CLIENT = restAdapter.create(Restapi.class);
}
// SET SSL
public static OkClient setSSLFactoryForClient(OkHttpClient client) {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
client.setSslSocketFactory(sslSocketFactory);
client.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
return new OkClient(client);
}
After using setSSLFactoryForClient method it work fine but i couldn't understand whats going wrong and what this method does i know the problem is related to SSL Certificate Authentication but Can any one explain me this in brief Please
This is disabling the security of SSL. This is ok for local testing but not appropriate for use with real users.
If you run your local dev server with a self signed cert then this is how you can tell it to connect to it with minimal pain.
More generally any user agent (Firefox on Windows, Safari on Mac, Android) will have a list of root CAs it trusts to verify a sites certificates. Some newer services like let's encrypt will not be trusted on older platforms so you can add your own certificates that you know ahead of time.
The hostname verification means that the cert it serves could be for a different site even.
For real traffic this code means your users are susceptible to man in the middle attacks.

Apache Camel CXF: add TlsClientParameters programmatically

I am using Apache Camel CXF as producer to call a SOAP Webservice. I do not use Spring configuration but do everything programmatically (I am a beginner and wanted to prevent having to learn both Spring and Apache Camel). The Webservice uses SSL with a self signed certificate. I added it to a truststore and hoped to be able to add that to the CxfEndpoint similar to how I did it with https4:
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("src/main/resources/truststore.jks");
ksp.setPassword("...");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("...");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
CamelContext context = new DefaultCamelContext();
context.addRoutes(routeBuilder);
HttpComponent httpComponent = context.getComponent("https4", HttpComponent.class);
httpComponent.setSslContextParameters(scp);
– but that does not seem to work with the CxfComponent. I found a lot of documentation about adding TlsClientParameters using Spring and configuring the CxfEndpoint, for example here: apache camel cxf https not working
and here Calling secure webservice using CXF and Camel. However I do not find any hint on how to simply add a truststore to the component as I did with https4 or even in the route definition, which is:
from(ENDPOINT_URI)
.setProperty(SecurityConstants.PASSWORD, constant(PASSWORD))
.setProperty(SecurityConstants.USERNAME, constant(USERNAME))
.to("cxf://" + SERVICE_URL + "?" +
"wsdlURL=" + WSDL_URL + "&" +
"serviceName=" + SERVICE_NAME + "&" +
"portName=" + PORT_NAME + "&" +
"dataFormat=CXF_MESSAGE&" +
"synchronous=true&" +
"defaultOperationName=" + DEFAULT_OPERATION_NAME)
.streamCaching();
I think this must be a very simple problem, so I still expect there is some neat way to simply add the truststore (or even accepting any certificate, since its not really relevant in our use case). I would be really happy if there was a simple programmatic way. Does anyone know?
I solved the issue by adding the certificate to the JVMs truststore in jre/lib/cacerts. That is feasable since I have access to the JVM on the machine the application will be running on. It seems to be the simplest solution.
Update
If anyone is interested in a more proper solution: CxfEndpoint provides a means to influence the HTTPConduit and its TLS Parameters. This is the revised code:
add "cxfEndpointConfigurer=SageEndpointConfigurer" to the cxf endpoint parameters
when creating the endpoint "SageEndpointConfigurer" will be resolved using TypeConverters
add a TypeConverter to the TypeConverter Registry of the context, i.e. directly in the RouteBuilder
getContext().getTypeConverterRegistry().addTypeConverter(CxfEndpointConfigurer.class, String.class, new SageEndpointConfigurerConverter());
configure TLSParameters and simply return the CxfEndpointConfigurer from the TypeConverter
private class SageEndpointConfigurerConverter extends TypeConverterSupport {
#Override
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {
CxfEndpointConfigurer configurer = new CxfEndpointConfigurer() {
#Override
public void configure(AbstractWSDLBasedEndpointFactory factoryBean) {
// do nothing
}
#Override
public void configureClient(Client client) {
URLConnectionHTTPConduit conduit = (URLConnectionHTTPConduit) client.getConduit();
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setDisableCNCheck(true);
tlsParams.setTrustManagers(new TrustManager[]{new TrustAllTrustManager()});
conduit.setTlsClientParameters(tlsParams);
}
#Override
public void configureServer(Server server) {
//do nothing
}
};
return (T) configurer;
}
}
the TrustAllManager is implemented like that
public class TrustAllTrustManager implements X509TrustManager {
private static Logger LOG = LoggerFactory.getLogger(TrustAllTrustManager.class);
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
//do nothing, trust all certificates
logMessage(x509Certificates, authType);
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
//do nothing, trust all certificates
logMessage(x509Certificates, authType);
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
private void logMessage(X509Certificate[] x509Certificates, String authType) {
StringBuilder message = new StringBuilder();
String lineSeparator = System.getProperty("line.separator");
message.append("Trusted following certificates for authentication type '").append(authType).append("'").append(lineSeparator);
for (X509Certificate certificate : x509Certificates) {
message.append(certificate).append(lineSeparator);
}
LOG.trace(message.toString());
}
}

Certificate not found in test with EjbContainer and GlassFish

I have an EJB that communicates with a site over https. The logic is to send a xml file in request and receive another in response. This works fine in development environment after adding the site certificate to cacerts inside GlassFish domain. The problem appears when the communication happens in test environment with EJBContainer. Even with org.glassfish.ejb.embedded.glassfish.installation.root and org.glassfish.ejb.embedded.glassfish.instance.root properties defined and certificate added to cacerts, the test execution ends with:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My EJB is implemented as follows:
#Stateless
#LocalBean
public class CommunicationService {
public String communicate() {
try {
URL url = new URL("https://www.comprasnet.gov.br/XML/treinamento/consultamatserv.asp");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.flush();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
StringBuilder resposta = new StringBuilder();
while ((line = reader.readLine()) != null) {
resposta.append(line);
}
writer.close();
reader.close();
return resposta.toString();
} catch (Exception e) {
return null;
}
}
}
My test configuration uses TestNG 6.8.5, GlassFish 3.1.2.2 and EJBContainer. The configuration starts creating another domain in GlassFish to avoid port conflicts with the default domain if it running. To do that, I've run the command:
asadmin create-domain --portbase 9100 domain-test
I've defined a super class with an annotated method with #BeforeSuite that starts the embedded container with the following content:
public abstract class GlassfishEmbeddedBaseTest {
protected Context ic;
protected UserTransaction tx;
private static EJBContainer ejbContainer;
#BeforeSuite
protected void beforeSuite() throws Exception {
String glassfishHome = System.getenv("GLASSFISH_HOME");
Properties properties = new Properties();
properties.put(EJBContainer.MODULES, new File[] { new File(
"target/classes") });
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
glassfishHome + "/glassfish");
properties.put("org.glassfish.ejb.embedded.glassfish.instance.root",
glassfishHome + "/glassfish/domains/domain-test");
properties.put(EJBContainer.APP_NAME, "app-name");
ejbContainer = EJBContainer.createEJBContainer(properties);
}
#BeforeClass
protected void load() throws Exception {
ic = ejbContainer.getContext();
}
#BeforeMethod
protected void beforeMethod() throws Exception {
tx = (UserTransaction) ic.lookup("java:comp/UserTransaction");
tx.begin();
}
#AfterMethod
protected void rollBack() throws Exception {
tx.rollback();
}
}
In the test class, I've did a look up for my EJB and calls the logic that communicates with the site over https:
public class CommunicationServiceTest extends GlassfishEmbeddedBaseTest {
private CommunicationService communicationService;
#BeforeClass
public void init() throws NamingException {
communicationService = (CommunicationService) ic
.lookup("java:global/app-name/classes/CommunicationService");
}
#Test
public void testCommunicate() {
String response = communicationService.communicate();
Assert.assertNotNull(response);
}
}
I found a bug related to this problem in GlassFish Jira:
https://java.net/jira/browse/GLASSFISH-17179, and as the EJBContainer is based on domain-test and the certified is installed in cacerts from this domain, I think that can be a problem of copy the cacerts defined on instance root property to the temporary directory created on embedded container start time.
How can I lead with that?
EJBContainer offers a property named org.glassfish.ejb.embedded.glassfish.instance.reuse that determine:
If true, no changes are made to the existing configuration file, and a temporary server
instance is not created for the embedded run. Instead, execution happens against the existing server instance. Do not use this option if the reused server instance could be in use by the running GlassFish Server.
Adding this property with value true, before create EJBContainer, solved the issue, since the certificate was already added to the cacerts in domain domain-test and nothing is copied to the temporary folder anymore.

Websphere pre-login to j_security_check

I am using Websphere with j_security_check and I have a working login filter with CONFIDENTIAL and SSL settings. Due to some complex requirements, I need to process the j_username variable before performing j_security_check.
From one of the answers found here, it was said that this pre-login processing cannot be done. However, I noted that the previous asker was using Tomcat, while I am using Websphere. I found that there is a solution for my problem over here but it seems that everytime I try to login, I receive a "The website cannot display the page. The website has a programming error."
I tried troubleshooting by checking if the syntax was correct. Found some inconsistencies like and corrected them as best I could. However, I still keep getting the same error.
Could someone throw me in the right direction? I can provide further info but they are mostly similar to the second link. The only protected folder is in \protected\ and the .java servlet is located in \WEB-INF\classes.
Thank you.
In case anyone is interested, the results are here:
For \WEB-INF\web.xml
<filter id="Filter_1">
<filter-name>LoginFilter</filter-name>
<filter-class>com.myloginfilter.MyLoginFilter</filter-class>
<description>Performs pre-login operation</description>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/j_security_check</url-pattern>
</filter-mapping>
For \WEB-INF\classes\com\myloginfilter\MyLoginFilter.class
public class MyLoginFilter implements Filter {
protected FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
chain.doFilter(new MyRequestWrapper((HttpServletRequest) request), response);
}
public static class MyRequestWrapper extends HttpServletRequestWrapper {
public MyRequestWrapper(HttpServletRequest request) {
super(request);
}
#Override
public String getParameter(String name) {
String username= getRequest().getParameter("j_username");
if ("j_password".equals(name) && "admin".equals(username)) {
username = "administrator";
return username;
}
return super.getParameter(name);
}
}
}
To compile it, use Javac with the command:
javac -cp servlet-api-2.3.jar MyLoginFilter.class