I am trying to connect to an IIS6 box running a godaddy 256bit SSL cert, and I am getting the error :
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Been trying to determine what could be causing that, but drawing blanks right now.
Here is how I am connecting :
HttpsURLConnection conn;
conn = (HttpsURLConnection) (new URL(mURL)).openConnection();
conn.setConnectTimeout(20000);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
String tempString = toString(conn.getInputStream());
Contrary to the accepted answer you do not need a custom trust manager, you need to fix your server configuration!
I hit the same problem while connecting to an Apache server with an incorrectly installed dynadot/alphassl certificate. I'm connecting using HttpsUrlConnection (Java/Android), which was throwing -
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.
The actual problem is a server misconfiguration - test it with http://www.digicert.com/help/ or similar, and it will even tell you the solution:
"The certificate is not signed by a trusted authority (checking against Mozilla's root store). If you bought the certificate from a trusted authority, you probably just need to install one or more Intermediate certificates. Contact your certificate provider for assistance doing this for your server platform."
You can also check the certificate with openssl:
openssl s_client -debug -connect www.thedomaintocheck.com:443
You'll probably see:
Verify return code: 21 (unable to verify the first certificate)
and, earlier in the output:
depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=21:unable to verify the first certificate`
The certificate chain will only contain 1 element (your certificate):
Certificate chain
0 s:/OU=Domain Control Validated/CN=www.thedomaintocheck.com
i:/O=AlphaSSL/CN=AlphaSSL CA - G2
... but should reference the signing authorities in a chain back to one which is trusted by Android (Verisign, GlobalSign, etc):
Certificate chain
0 s:/OU=Domain Control Validated/CN=www.thedomaintocheck.com
i:/O=AlphaSSL/CN=AlphaSSL CA - G2
1 s:/O=AlphaSSL/CN=AlphaSSL CA - G2
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
2 s:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
Instructions (and the intermediate certificates) for configuring your server are usually provided by the authority that issued your certificate, for example: http://www.alphassl.com/support/install-root-certificate.html
After installing the intermediate certificates provided by my certificate issuer I now have no errors when connecting using HttpsUrlConnection.
The solution of #Chrispix is dangerous! Trusting all certificates allows anybody to do a man in the middle attack! Just send ANY certificate to the client and it will accept it!
Add your certificate(s) to a custom trust manager like described in this post: Trusting all certificates using HttpClient over HTTPS
Although it is a bit more complex to establish a secure connection with a custom certificate, it will bring you the wanted ssl encryption security without the danger of man in the middle attack!
If you use retrofit, you need to customize your OkHttpClient.
retrofit = new Retrofit.Builder()
.baseUrl(ApplicationData.FINAL_URL)
.client(getUnsafeOkHttpClient().build())
.addConverterFactory(GsonConverterFactory.create())
.build();
Full code are as below.
public class RestAdapter {
private static Retrofit retrofit = null;
private static ApiInterface apiInterface;
public static OkHttpClient.Builder getUnsafeOkHttpClient() {
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 new java.security.cert.X509Certificate[]{};
}
}
};
// 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();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static ApiInterface getApiClient() {
if (apiInterface == null) {
try {
retrofit = new Retrofit.Builder()
.baseUrl(ApplicationData.FINAL_URL)
.client(getUnsafeOkHttpClient().build())
.addConverterFactory(GsonConverterFactory.create())
.build();
} catch (Exception e) {
e.printStackTrace();
}
apiInterface = retrofit.create(ApiInterface.class);
}
return apiInterface;
}
}
You can trust particular certificate at runtime.
Just download it from server, put in assets and load like this using ssl-utils-android:
OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());
In the example above I used OkHttpClient but SSLContext can be used with any client in Java.
If you have any questions feel free to ask. I'm the author of this small library.
Update based on latest Android documentation (March 2017):
When you get this type of error:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
the issue could be one of the following:
The CA that issued the server certificate was unknown
The server certificate wasn't signed by a CA, but was self signed
The server configuration is missing an intermediate CA
The solution is to teach HttpsURLConnection to trust a specific set of CAs. How? Please check https://developer.android.com/training/articles/security-ssl.html#CommonProblems
Others who are using AsyncHTTPClient from com.loopj.android:android-async-http library, please check Setup AsyncHttpClient to use HTTPS.
Replying to very old post. But maybe it will help some newbie and if non of the above works out.
Explanation: I know nobody wants explanation crap; rather the solution. But in one liner, you are trying to access a service from your local machine to a remote machine which does not trust your machine. You request need to gain the trust from remote server.
Solution: The following solution assumes that you have the following conditions met
Trying to access a remote api from your local machine.
You are building for Android app
Your remote server is under proxy filtration (you use proxy in your browser setting to access the remote api service, typically a staging or dev server)
You are testing on real device
Steps:
You need a .keystore extension file to signup your app. If you don't know how to create .keystore file; then follow along with the following section Create .keystore file or otherwise skip to next section Sign Apk File
Create .keystore file
Open Android Studio. Click top menu Build > Generate Signed APK. In the next window click the Create new... button. In the new window, please input in data in all fields. Remember the two Password field i recommend should have the same password; don't use different password; and also remember the save path at top most field Key store path:. After you input all the field click OK button.
Sign Apk File
Now you need to build a signed app with the .keystore file you just created. Follow these steps
Build > Clean Project, wait till it finish cleaning
Build > Generate Signed APK
Click Choose existing... button
Select the .keystore file we just created in the Create .keystore file section
Enter the same password you created while creating in Create .keystore file section. Use same password for Key store password and Key password fields. Also enter the alias
Click Next button
In the next screen; which might be different based on your settings in build.gradle files, you need to select Build Types and Flavors.
For the Build Types choose release from the dropdown
For Flavors however it will depends on your settings in build.gradle file. Choose staging from this field. I used the following settings in the build.gradle, you can use the same as mine, but make sure you change the applicationId to your package name
productFlavors {
staging {
applicationId "com.yourapplication.package"
manifestPlaceholders = [icon: "#drawable/ic_launcher"]
buildConfigField "boolean", "CATALYST_DEBUG", "true"
buildConfigField "boolean", "ALLOW_INVALID_CERTIFICATE", "true"
}
production {
buildConfigField "boolean", "CATALYST_DEBUG", "false"
buildConfigField "boolean", "ALLOW_INVALID_CERTIFICATE", "false"
}
}
Click the bottom two Signature Versions checkboxes and click Finish button.
Almost There:
All the hardwork is done, now the movement of truth. Inorder to access the Staging server backed-up by proxy, you need to make some setting in your real testing Android devices.
Proxy Setting in Android Device:
Click the Setting inside Android phone and then wi-fi
Long press on the connected wifi and select Modify network
Click the Advanced options if you can't see the Proxy Hostname field
In the Proxy Hostname enter the host IP or name you want to connect. A typical staging server will be named as stg.api.mygoodcompany.com
For the port enter the four digit port number for example 9502
Hit the Save button
One Last Stop:
Remember we generated the signed apk file in Sign APK File section. Now is the time to install that APK file.
Open a terminal and changed to the signed apk file folder
Connect your Android device to your machine
Remove any previous installed apk file from the Android device
Run adb install name of the apk file
If for some reason the above command return with adb command not found. Enter the full path as C:\Users\shah\AppData\Local\Android\sdk\platform-tools\adb.exe install name of the apk file
I hope the problem might be solved. If not please leave me a comments.
Salam!
Use https://www.ssllabs.com/ssltest/ to test a domain.
The solution of Shihab Uddin in Kotlin.
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
import javax.security.cert.CertificateException
object {
val okHttpClient: OkHttpClient
val gson: Gson
val retrofit: Retrofit
init {
okHttpClient = getOkHttpBuilder()
// Other parameters like connectTimeout(15, TimeUnit.SECONDS)
.build()
gson = GsonBuilder().setLenient().create()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
fun getOkHttpBuilder(): OkHttpClient.Builder =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
OkHttpClient().newBuilder()
} else {
// Workaround for the error "Caused by: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate: Certificate expired at".
getUnsafeOkHttpClient()
}
private fun getUnsafeOkHttpClient(): OkHttpClient.Builder =
try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts: Array<TrustManager> = arrayOf(
object : X509TrustManager {
#Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate?>?,
authType: String?) = Unit
#Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate?>?,
authType: String?) = Unit
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
}
)
// Install the all-trusting trust manager
val sslContext: SSLContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory: SSLSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory,
trustAllCerts[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
builder
} catch (e: Exception) {
throw RuntimeException(e)
}
}
The same error will also appear if you use Glide, images won't show. To overcome it see Glide - javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found and How to set OkHttpClient for glide.
#GlideModule
class MyAppGlideModule : AppGlideModule() {
val okHttpClient = Api.getOkHttpBuilder().build() // Api is the class written above.
// It is better to create okHttpClient here and not use Api.okHttpClient,
// because their settings may differ. For instance, it can use its own
// `addInterceptor` and `addNetworkInterceptor` that can affect on a read JSON.
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
registry.replace(GlideUrl::class.java, InputStream::class.java,
OkHttpUrlLoader.Factory(okHttpClient))
}
}
build.gradle:
// Glide.
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
UPDATE
I also got another error on API 16 emulator:
routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
(external/openssl/ssl/s23_clnt.c:741'.
Reading 1 and 2, I changed code so:
okHttpClient = getOkHttpBuilder().build()
private fun getOkHttpBuilder(): OkHttpClient.Builder {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Security.insertProviderAt(Conscrypt.newProvider(), 1)
}
return OkHttpClient().newBuilder()
}
// build.gradle:
implementation 'org.conscrypt:conscrypt-android:2.5.1'
I also removed these lines from MyApplication:
try {
ProviderInstaller.installIfNeeded(applicationContext)
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(null, null, null)
sslContext.createSSLEngine()
} catch (e: GooglePlayServicesRepairableException) {
Timber.e(e.stackTraceToString())
// Prompt the user to install/update/enable Google Play services.
GoogleApiAvailability.getInstance().showErrorNotification(this, e.connectionStatusCode)
} catch (e: GooglePlayServicesNotAvailableException) {
Timber.e(e.stackTraceToString())
// Prompt the user to install/update/enable Google Play services.
// GoogleApiAvailability.getInstance().showErrorNotification(this, e.errorCode)
} catch (e: NoSuchAlgorithmException) {
Timber.e(e.stackTraceToString())
} catch (e: KeyManagementException) {
Timber.e(e.stackTraceToString())
}
But the library adds 3.4 Mb to apk.
I had the same problem what i found was that the certificate .crt file i provided missing an intermediate certificate. So I asked all .crt files from my server admin, then concatinated them in reverse order.
Ex.
1. Root.crt
2. Inter.crt
3. myCrt.crt
in windows i executed
copy Inter.crt + Root.crt newCertificate.crt
(Here i ignored myCrt.crt)
Then i provided newCertificate.crt file into code via inputstream.
Work done.
The error message I was getting was similar but the reason was that the self signed certificate had expired.
When the openssl client was attempted, it gave me the reason which was overlooked when I was checking the certificate dialog from firefox.
So in general, if the certificate is there in the keystore and its "VALID", this error will go off.
I had the same problem while connecting from Android client to Kurento server.
Kurento server use jks certificates, so I had to convert pem to it.
As input for conversion I used cert.pem file and it lead to such errors.
But if use fullchain.pem instead of cert.pem - all is OK.
I know that you don't need to trust all certificates but in my case I had problems with some debugging environments where we had self-signed certificates and I needed a dirty solution.
All I had to do was to change the initialization of the sslContext
mySSLContext.init(null, trustAllCerts, null);
where trustAllCerts was created like this:
private final TrustManager[] trustAllCerts= new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
Hope that this will come in handy.
In my case, the root & intermediate certificates was successfully installed but I still got "Trust anchor for certification path not found." exception!. After digging the android document, found out that by default, secure connections (using protocols like TLS and HTTPS) from all apps trust the pre-installed system CAs, and apps targeting Android 6.0 (API level 23) and lower also trust the user-added CA store by default. If your app running on a OS with api level higher than 23 you should explicitly allow the app to trust user-added CA by adding its address to network_security_config like bellow:
<domain-config>
<domain includeSubdomains="true">PUT_YOUR_SERVER_ADDERESS</domain>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</domain-config>
The Trust anchor error can happen for a lot of reasons. For me it was simply that I was trying to access https://example.com/ instead of https://www.example.com/.
So you might want to double-check your URLs before starting to build your own Trust Manager (like I did).
In Gingerbread phones, I always get this error: Trust Anchor not found for Android SSL Connection, even if I setup to rely on my certificate.
Here is the code I use (in Scala language):
object Security {
private def createCtxSsl(ctx: Context) = {
val cer = {
val is = ctx.getAssets.open("mycertificate.crt")
try
CertificateFactory.getInstance("X.509").generateCertificate(is)
finally
is.close()
}
val key = KeyStore.getInstance(KeyStore.getDefaultType)
key.load(null, null)
key.setCertificateEntry("ca", cer)
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
tmf.init(key)
val c = SSLContext.getInstance("TLS")
c.init(null, tmf.getTrustManagers, null)
c
}
def prepare(url: HttpURLConnection)(implicit ctx: Context) {
url match {
case https: HttpsURLConnection ⇒
val cSsl = ctxSsl match {
case None ⇒
val res = createCtxSsl(ctx)
ctxSsl = Some(res)
res
case Some(c) ⇒ c
}
https.setSSLSocketFactory(cSsl.getSocketFactory)
case _ ⇒
}
}
def noSecurity(url: HttpURLConnection) {
url match {
case https: HttpsURLConnection ⇒
https.setHostnameVerifier(new HostnameVerifier {
override def verify(hostname: String, session: SSLSession) = true
})
case _ ⇒
}
}
}
and here is the connection code:
def connect(securize: HttpURLConnection ⇒ Unit) {
val conn = url.openConnection().asInstanceOf[HttpURLConnection]
securize(conn)
conn.connect();
....
}
try {
connect(Security.prepare)
} catch {
case ex: SSLHandshakeException /*if ex.getMessage != null && ex.getMessage.contains("Trust anchor for certification path not found")*/ ⇒
connect(Security.noSecurity)
}
Basically, I setup to trust on my custom certificate. If that fails, then I disable security. This is not the best option, but the only choice I know with old and buggy phones.
This sample code, can be easily translated into Java.
I know this is a very old article, but I came across this article when trying to solve my trust anchor issues. I have posted how I fixed it. If you have pre-installed your Root CA you need to add a configuration to the manifest.
https://stackoverflow.com/a/60102517/114265
In my case this was happening after update to Android 8.0. The self-signed certificate Android was set to trust was using signature algorithm SHA1withRSA. Switching to a new cert, using signature algorithm SHA256withRSA fixed the problem.
I have had a similar problem and I have completely ruled out the strategy of trusting all sources.
I share here my solution applied to an application implemented in Kotlin
I would first recommend using the following website to obtain information about the certificate and its validity
If it does not appear as an 'Accepted Issuers' in the Android default trust store, we must get that certificate and incorporate it into the application to create a custom trust store
The ideal solution in my case was to create a high-level Trust Manager that combines the custom and the Android default trust store
Here he exposes the high level code used to configure the OkHttpClient that he used with Retrofit.
override fun onBuildHttpClient(httpClientBuild: OkHttpClient.Builder) {
val trustManagerWrapper = createX509TrustManagerWrapper(
arrayOf(
getCustomX509TrustManager(),
getDefaultX509TrustManager()
)
)
printX509TrustManagerAcceptedIssuers(trustManagerWrapper)
val sslSocketFactory = createSocketFactory(trustManagerWrapper)
httpClientBuild.sslSocketFactory(sslSocketFactory, trustManagerWrapper)
}
In this way, I could communicate with the server with a self-signed certificate and with other servers with a certificate issued by a trusted certification entity
This is it, I hope it can help someone.
Sometimes it happens when admins setup the certificate incorrectly
Check URL here
https://www.sslshopper.com/ssl-checker.html
In my case, there was an error
The certificate is not trusted in all web browsers. You may need to install an Intermediate/chain certificate to link it to a trusted root certificate. Learn more about this error. You can fix this by following GlobalSign's Certificate Installation Instructions for your server platform. Pay attention to the parts about Intermediate certificates.
I use these methods that one of them is in solutions above works for me :
First:
public okhttp3.OkHttpClient getUnsafeOkHttpClient() {
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 new
java.security.cert.X509Certificate[]{};
}
}
};
// 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();
okhttp3.OkHttpClient.Builder builder = new
okhttp3.OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory,
(X509TrustManager)trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession
session) {
return true;
}
});
okhttp3.OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Second:
#SuppressLint("TrulyRandom")
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new
X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
#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 ignored) {
}
}
and:
put this libraries to your classpath:
implementation 'com.squareup.okhttp:okhttp:2.3.0'
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-
core:3.3.0'
be sure that you call them in your class
In my case, the certificate in the website was correct (Issuer = GlobalSign RSA OV SSL CA 2018), but the certificate file I was downloading was wrong, because of the Antivirus that was "intercepting" the certificate and deliverying a new different certificate to download fron the browsers (Issuer = ESET SSL Filter CA) !!!
Check your certificate file has the correct issuer.
**Set proper alias name**
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509","BC");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
Relpcae your clicent Like below
var httpClient = new HttpClient(new System.Net.Http.HttpClientHandler());
Change https to http
Related
The context is that I am working on a Kubernetes project, where we use a Geode cluster, and Spring Boot, also Spring Boot Data Geode (SBDG). We have developed an app with it, a ClientCache. Also we have a proprietary internal mechanism to generate cluster-internal certificates, this mechanism automatically renews certificates according to the best practices. We convert the PEM formatted certificate in our App code to JKS, and configured Spring with #EnableSSL annotation to take them.
So the issue is, that everything works wonderfully for the first cycle, when the connections were created with the JKS file the App initially started up with, however if the certificate is renewed, say hourly (in cloud this is best practice), Geode fails to be connected with a bunch of Exceptions, sometimes SSLException (readHandshakeRecord), many times with "Unable to connect to any locators in the list" (but I debugged, and it is also a HandshakeException, just wrapper in a connection-exception instead). The locators and servers are up and running (I checked with GFSH), just the App I think tries to connect with the old SSLContext and fails in the SSL handshake.
The only way so far I have found is to restart the App completely, but we would need this system to be automatic, and highly available, so this should not be the only way around this issue.
I think this problem is affecting a lot of Spring/Java projects as I have found this issue all around (Kafka, PGSQL, etc...).
Do any of you have any method to do this?
Is there a way to:
Recreate all the connections without me restarting the App?
Invalidate the currently used connections somehow, and force the
ClientCache to create new ones, re-reading the JKS file?
Maybe let the Client App timeout the connections and destroy them, and create new ones, with refreshed SSLContext?
I did not find any possibilities for this.
EDIT: Let me add some code, to show how we do things, since we use Spring, it is dead simple:
#Configuration
#EnableGemfireRepositories(basePackages = "...")
#EnableEntityDefinedRegions(basePackages = "...")
#ClientCacheApplication
#EnableSsl(
truststore = "truststore.jks",
keystore = "keystore.jks",
truststorePassword = "pwd",
keystorePassword = "pwd"
)
public class GeodeTls {}
And that is it! We then use normal annotations for #Regions, and #Repositories, and we have our #RestControllers where we call the repository methods, most of them are just empty ones, or default ones as we use the OQL annotate method to do things with Spring. Since Geode has a property-based config, we never set KeyStores, TrustStores, I just happen to see them inside the code during debugging.
EDIT2: I have finally solved thanks to the below comments, it was this Geode ticket that helped a lot (thanks to Jen D for that): https://github.com/apache/geode/pull/2244, available since Geode 1.8.0. Also the below snippet was extremely useful about the Swappable KeyManager (thanks for Hakan54), I did a combined solution in the end. I had to be careful though, to set the default SSLContext only once as the subsequent sets were ineffective, and did not result in any failures. Now the App is stable it seems across certificate changes.
I came across your question yesterday and was working on a prototype. I think it might be possible in your case. However I just tried it out locally with a http client and a server which I was able to change the certificates at runtime without the need of restarting these applications or recreating the SSLContext.
Option 1
From your question I can understand that you are reading PEM files from somewhere and converting it to something else and at the end you are using a SSLContext. In that case I would assume you are creating a KeyManager and a TrustManager. If thats the case what you need to do is create a custom implementation of the KeyManager and TrustManager as a wrapper class to delegate the method calls to the actual KeyManager and TrustManager within the wrapper class. And also add a setter method to change the internal KeyManager and TrustManager when the certificates get updated.
In your case that would be a file-watcher which gets triggered when the PEM files have been changed. In that case you only need to regenerate the KeyManager and TrustManager with the new certificates and give it to the wrapped class by calling the setter method. Below is an example code snippet what you could use:
HotSwappableX509ExtendedKeyManager
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Objects;
public final class HotSwappableX509ExtendedKeyManager extends X509ExtendedKeyManager {
private X509ExtendedKeyManager keyManager;
public HotSwappableX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
this.keyManager = Objects.requireNonNull(keyManager);
}
#Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return keyManager.chooseClientAlias(keyType, issuers, socket);
}
#Override
public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine sslEngine) {
return keyManager.chooseEngineClientAlias(keyTypes, issuers, sslEngine);
}
#Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return keyManager.chooseServerAlias(keyType, issuers, socket);
}
#Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine sslEngine) {
return keyManager.chooseEngineServerAlias(keyType, issuers, sslEngine);
}
#Override
public PrivateKey getPrivateKey(String alias) {
return keyManager.getPrivateKey(alias);
}
#Override
public X509Certificate[] getCertificateChain(String alias) {
return keyManager.getCertificateChain(alias);
}
#Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return keyManager.getClientAliases(keyType, issuers);
}
#Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return keyManager.getServerAliases(keyType, issuers);
}
public void setKeyManager(X509ExtendedKeyManager keyManager) {
this.keyManager = Objects.requireNonNull(keyManager);
}
}
HotSwappableX509ExtendedTrustManager
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Objects;
public class HotSwappableX509ExtendedTrustManager extends X509ExtendedTrustManager {
private X509ExtendedTrustManager trustManager;
public HotSwappableX509ExtendedTrustManager(X509ExtendedTrustManager trustManager) {
this.trustManager = Objects.requireNonNull(trustManager);
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
trustManager.checkClientTrusted(chain, authType, socket);
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
trustManager.checkClientTrusted(chain, authType, sslEngine);
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
trustManager.checkServerTrusted(chain, authType);
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
trustManager.checkServerTrusted(chain, authType, socket);
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
trustManager.checkServerTrusted(chain, authType, sslEngine);
}
#Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] acceptedIssuers = trustManager.getAcceptedIssuers();
return Arrays.copyOf(acceptedIssuers, acceptedIssuers.length);
}
public void setTrustManager(X509ExtendedTrustManager trustManager) {
this.trustManager = Objects.requireNonNull(trustManager);
}
}
Usage
// Your key and trust manager created from the pem files
X509ExtendedKeyManager aKeyManager = ...
X509ExtendedTrustManager aTrustManager = ...
// Wrapping it into your hot swappable key and trust manager
HotSwappableX509ExtendedKeyManager swappableKeyManager = new HotSwappableX509ExtendedKeyManager(aKeyManager);
HotSwappableX509ExtendedTrustManager swappableTrustManager = new HotSwappableX509ExtendedTrustManager(aTrustManager);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[]{ swappableKeyManager }, new TrustManager[]{ swappableTrustManager })
// Give the sslContext instance to your server or client
// After some time change the KeyManager and TrustManager with the following snippet:
X509ExtendedKeyManager anotherKeyManager = ... // Created from the new pem files
X509ExtendedTrustManager anotherTrustManager = ... // Created from the new pem files
// Set your new key and trust manager into your swappable managers
swappableKeyManager.setKeyManager(anotherKeyManager)
swappableTrustManager.setTrustManager(anotherTrustManager)
So even when your SSLContext instance is cached in your server of client you can still swap in and out new keymanager and trustmanager.
The code snippets are available here:
Github - SSLContext Kickstart
HotSwappableX509ExtendedKeyManager
HotSwappableX509ExtendedTrustManager
Option 2
If you don't want to add the custom (HotSwappableKeyManager and HotSwappableTrustManager) code to your code base you can also use my library:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.4.5</version>
</dependency>
Usage
SSLFactory baseSslFactory = SSLFactory.builder()
.withDummyIdentityMaterial()
.withDummyTrustMaterial()
.withSwappableIdentityMaterial()
.withSwappableTrustMaterial()
.build();
SSLContext sslContext = sslFactory.getSslContext();
Runnable sslUpdater = () -> {
SSLFactory updatedSslFactory = SSLFactory.builder()
.withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
.withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
.build();
SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory);
};
// initial update of ssl material to replace the dummies
sslUpdater.run();
// update ssl material every hour
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);
Update #1 - example with pem files
In the comments someone requested an example with pem files, so below is an example of refreshing the ssl configuration with pem files:
First make sure you have the following library:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart-for-pem</artifactId>
<version>7.4.5</version>
</dependency>
And the code example:
SSLFactory baseSslFactory = SSLFactory.builder()
.withDummyIdentityMaterial()
.withDummyTrustMaterial()
.withSwappableIdentityMaterial()
.withSwappableTrustMaterial()
.build();
SSLContext sslContext = sslFactory.getSslContext();
Runnable sslUpdater = () -> {
X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial(Paths.get("/path/to/your/certificate-chain.pem"), Paths.get("/path/to/your/"private-key.pem"));
X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial(Paths.get("/path/to/your/"some-trusted-certificate.pem"));
SSLFactory updatedSslFactory = SSLFactory.builder()
.withIdentityMaterial(keyManager)
.withTrustMaterial(trustManager)
.build();
SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory);
};
// initial update of ssl material to replace the dummies
sslUpdater.run();
// update ssl material every hour
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);
I think what you're looking for is very similar to what the Java buildpack does when deploying apps to CloudFoundry. When an app is deployed, the buildpack injects a custom Security Provider which watches various key/trust stores for changes. This allows the certificates to be updated without needing to restart the app (https://docs.cloudfoundry.org/buildpacks/java/).
I'm not sure of the exact implementation details but the code for the Security Provider can be found here https://github.com/cloudfoundry/java-buildpack-security-provider. Hopefully this will give you some ideas on how to implement this for your own needs.
I am using flutter to connect with java java server implementation over https. I first tested it to be working using just http.
I then switched to https on the server side and pointed it at my self signed certificate I created using keytool.
Then I tried to connect to it using the http dart package. The resulted in the following exception...
Unhandled Exception: HandshakeException: Handshake error in client (OS
Error: E/flutter ( 7370): CERTIFICATE_VERIFY_FAILED: self signed
certificate(handshake.cc:354))
I am assuming I need to set my client to trust my servers self signed certificate. I have looked at the APi reference and could not figure out how to get this to happen...
My dart code in my flutter app is as follows...
void testMessage() {
var url = 'https://192.168.100.105:8443';
var response = await http.post(url, body: "{\"message_name\": \"TestMessage\", \"contents\": { \"field1\":\"blah\", \"field2\":\"blah\" }}");
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
}
While Pascal's answer works, it only applies to the dart:io HttpClient.
To apply the badCertificateCallback to the http package's Client instances, do the following:
Create a class that overrides HttpOverrides in the following way:
class DevHttpOverrides extends HttpOverrides {
#override
HttpClient createHttpClient(SecurityContext context) {
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}
Then in your main, instantiate your class as the global HttpOverride:
HttpOverrides.global = new DevHttpOverrides();
This should make all Client ignore bad certificates and is therefore onl;y recommended in development.
Credit goes to this issue: https://github.com/dart-lang/http/issues/458
While developing you can use the badCertificateCallback callback of HttpClient and just return true. This will accept all bad certificates.
HttpClient client = HttpClient()
..badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
To accept a specific bad certificate you may experiment with this code from here: https://github.com/dart-lang/http/issues/14#issuecomment-311184690
import 'dart:io';
import 'package:http/http.dart' as http;
bool _certificateCheck(X509Certificate cert, String host, int port) =>
host == 'local.domain.ext'; // <- change
HttpClient client = new HttpClient()
..badCertificateCallback = (_certificateCheck);
Amazing #Wecherowski, I think more safe way to do this is to check the other details and return true.
Something like:
HttpClient createHttpClient(SecurityContext? context)
{
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)
{
if (host.isNotEmpty && host == 'xyz.example.com')
{
return true;
}
else
{ return false; }
};
If you use dio library https://pub.dev/packages/dio you can make requests from http to https
Does java's TrustManager implementation ignore if a certificate has expired?
I tried the following:
- Using keytool and parameter -startdate "1970/01/01 00:00:00" I created a P12 keystore with an expired certificate.
- I exported the certificate:
Keystore type: PKCS12
Keystore provider: SunJSSE
Your keystore contains 1 entry
Alias name: fake
Creation date: 5 ╠ά± 2011
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
Issuer: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
Serial number: -1c20
Valid from: Thu Jan 01 00:00:00 EET 1970 until: Fri Jan 02 00:00:00 EET 1970
Certificate fingerprints:
MD5: A9:BE:3A:3D:45:24:1B:4F:3C:9B:2E:02:E3:57:86:11
SHA1: 21:9D:E1:04:09:CF:10:58:73:C4:62:3C:46:4C:76:A3:81:56:88:4D
Signature algorithm name: SHA1withRSA
Version: 3
*******************************************
I used this certificate as server certificate for Tomcat.
Then using an apache httpClient I connected to tomcat, but first I added the expired certificate to the client's trust-store (using a TrustManager
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
and loading the expired certificate).
I was expecting the connection to fail.
Instead the connection succeeds.
Using System.setProperty("javax.net.debug", "ssl");
I see:
***
Found trusted certificate:
[
[
Version: V3
Subject: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
modulus: 10350555024148635338735220482157687267055139906998169922552357357346372886164908067983097037540922519808845662295379579697361784480052371935565129553860304254832565723373586277732296157572040989796830623403187557540749531267846797324326299709274902019299
public exponent: 65537
Validity: [From: Thu Jan 01 00:00:00 EET 1970,
To: Fri Jan 02 00:00:00 EET 1970]
Issuer: CN=Malicious, OU=Mal, O=Mal, L=Fake, ST=GR, C=GR
SerialNumber: [ -1c20]
]
I see that in TLS handshake the expired certificate is send by Tomcat connector.
But the client (i.e. the TrustManager) does not reject the connection.
Is this the default behavior?
Am I suppose to configure the trustmanager somehow to check for expiration?
UPDATE:
I found that the actual TrustManager used is X509TrustManagerImpl. Here X509TrustManagerImpl says that this class has a minimal logic.May be I am using the wrong TrustManager?
UPDATE2:
From the javadoc X509TrustManager it is not clear if it checks for certificate expiration
void checkServerTrusted(X509Certificate[] chain,String authType)
throws CertificateException
Given the partial or complete
certificate chain provided by the
peer, build a certificate path to a
trusted root and return if it can be
validated and is trusted for server
SSL authentication based on the
authentication type.The authentication
type is the key exchange algorithm
portion of the cipher suites
represented as a String, such as
"RSA", "DHE_DSS". Note: for some
exportable cipher suites, the key
exchange algorithm is determined at
run time during the handshake. For
instance, for
TLS_RSA_EXPORT_WITH_RC4_40_MD5, the
authType should be RSA_EXPORT when an
ephemeral RSA key is used for the key
exchange, and RSA when the key from
the server certificate is used.
Checking is case-sensitive.
Thanks
I've just had a similar issue myself while overriding checkServerTrusted.
Turns out that if you need to check expiration you can call X509Certificate.checkValidity() and it will throw either a CertificateExpiredException or a CertificateNotYetValidException. Both of these extend CertificateException so they can be happily thrown by checkServerTrusted.
To solve your problem you could implement a new X509TrustManager which creates your original instance in its constructor, implements all methods as calls to the original instance, and adds a call to checkValidity for each certificate in certs[] inside checkServerTrusted.
I did not try your example, but I now I regularly have to regenerate my server certificates (for our development server) since their certificates have quite short validity times.
In our case the client does not have the server certificates themselves in the truststore, but only the certificate of our CA (with longer validity), and when the client tries to connect to the server, both sides get a SSLException (which may be wrapped in another exception in your case).
I guess that the trust manager assumes something like "if you give me expired certificates to trust in, I'll do it".
Try our approach instead (it also saves you to update the client each time the server certificate expires).
I believe IBM's JSSE checks for expiry while Sun's does not.
Very old thread but I thought I'd share some code that implements Matt Lyons' suggestion above (I think!). I looked at a lot of questions on this but didn't find any actual code examples. AFAIK, this code is ok for Java 6, 7, 8 (not sure about earlier/later versions).
This is where I create the HTTPS URL connection that checks SSL certs against a local truststore (but also checks for cert expiry)
public HttpURLConnection getHttpsUrlConnection(URL url) {
HttpURLConnection connection = null;
try {
// Create the connection
connection = (HttpsURLConnection) url.openConnection();
((HttpsURLConnection) connection).setSSLSocketFactory(getSslSocketFactoryThatChecksCertsAgainstLocalKeystore());
} catch (IOException e) {
e.printStackTrace();
}
return connection;
}
private SSLSocketFactory getSslSocketFactoryThatChecksCertsAgainstLocalKeystore() {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// I'm using a custom keystore here (useful tips about working with keystores here: https://www.baeldung.com/java-keystore)
// I think you can use the default keystore (JRE cacerts file) by passing in null here (cast to KeyStore)
trustManagerFactory.init(getLocalKeystore());
// Get the trust managers (I think there's normally only one)
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
// Create an array of MyX509TrustManager objects to extend/replace trustManagers
MyX509TrustManager myTrustManagers[] = new MyX509TrustManager[trustManagers.length];
for (int i = 0; i < myTrustManagers.length; i++) {
if (trustManagers[i] instanceof X509TrustManager) {
// For each trust manager, create a new MyX509TrustManager (which will check for expired certs!)
myTrustManagers[i] = new MyX509TrustManager((X509TrustManager) trustManagers[i]);
}
}
// Create a ssl socket factory using my trust manager(s) that will include expired cert checking
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, myTrustManagers, null);
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
This is the class that implements X509TrustManager (and contains a reference to a "real" X509TrustManager which does almost all of the processing).
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509Certificate;
public class MyX509TrustManager implements X509TrustManager {
private X509TrustManager impl;
public MyX509TrustManager(X509TrustManager impl) {
this.impl = impl;
}
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
impl.checkClientTrusted(x509Certificates, s);
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
impl.checkServerTrusted(x509Certificates, s);
checkCertExpiry(x509Certificates);
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return impl.getAcceptedIssuers();
}
private void checkCertExpiry(X509Certificate[] x509Certificates) throws CertificateException {
long currentTime = System.currentTimeMillis();
for (int i = 0; i < x509Certificates.length; i++) {
if (currentTime > x509Certificates[i].getNotAfter().getTime()) {
throw new CertificateExpiredException("Cert expired on " + x509Certificates[i].getNotAfter());
} else if (currentTime < x509Certificates[i].getNotBefore().getTime()) {
throw new CertificateExpiredException("Cert will not be valid until " + x509Certificates[i].getNotBefore());
}
}
}
}
There is probably a more elegant way to do this. Suggestions welcome!
This question already has answers here:
Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
(33 answers)
Closed 2 years ago.
I received this error while trying to start up an application:
Sun.security.validator.ValidatorException: PKIX path validation failed:
java.security.cert.CertPathValidatorException: java.net.UnknownHostException:oscp.thawte.com
The application is behind a closed network and won't ever be able to get to oscp.thawte.com. Is there a java setting that can disable this?
-Dcom.sun.net.ssl.checkRevocation=false
Not exactly a setting but you can override the default TrustManager and HostnameVerifier to accept anything. Not a safe approach but in your situation, it can be acceptable.
Complete example : Fix certificate problem in HTTPS
In addition to the answers above. You can do it programmatically by implementing the TrustManager:
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {}
}
};
SSLContext sc=null;
try {
sc = SSLContext.getInstance("SSL");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier validHosts = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
// All hosts will be valid
HttpsURLConnection.setDefaultHostnameVerifier(validHosts);
However this is not a good practice for production.
This example on How to disable SSL certificat validation in Java contains a utility class you can copy in your project.
Use cli utility keytool from java software distribution for import (and trust!) needed certificates
Sample:
From cli change dir to jre\bin
Check keystore (file found in jre\bin directory)
keytool -list -keystore ..\lib\security\cacerts
Enter keystore password: changeit
Download and save all certificates chain from needed server.
Add certificates (before need to remove "read-only" attribute on file "..\lib\security\cacerts")
keytool -alias REPLACE_TO_ANY_UNIQ_NAME -import -keystore ..\lib\security\cacerts -file "r:\root.crt"
accidentally I found such a simple tip.
Other solutions require the use of InstallCert.Java and JDK
source: http://www.java-samples.com/showtutorial.php?tutorialid=210
On my Mac that I'm sure I'm not going to allow java anyplace other than a specific site, I was able to use Preferences->Java to bring up the Java control panel and turned the checking off. If DLink ever fixes their certificate, I'll turn it back on.
In Axis webservice and if you have to disable the certificate checking then use below code:
AxisProperties.setProperty("axis.socketSecureFactory","org.apache.axis.components.net.SunFakeTrustSocketFactory");
It is very simple .In my opinion it is the best way for everyone
Unirest.config().verifySsl(false);
HttpResponse<String> response = null;
try {
Gson gson = new Gson();
response = Unirest.post("your_api_url")
.header("Authorization", "Basic " + "authkey")
.header("Content-Type", "application/json")
.body("request_body")
.asString();
System.out.println("------RESPONSE -------"+ gson.toJson(response.getBody()));
} catch (Exception e) {
System.out.println("------RESPONSE ERROR--");
e.printStackTrace();
}
}
I have a REST server made in Grizzly that uses HTTPS and works wonderfully with Firefox. Here's the code:
//Build a new Servlet Adapter.
ServletAdapter adapter=new ServletAdapter();
adapter.addInitParameter("com.sun.jersey.config.property.packages", "My.services");
adapter.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, SecurityFilter.class.getName());
adapter.setContextPath("/");
adapter.setServletInstance(new ServletContainer());
//Configure SSL (See instructions at the top of this file on how these files are generated.)
SSLConfig ssl=new SSLConfig();
String keystoreFile=Main.class.getResource("resources/keystore_server.jks").toURI().getPath();
System.out.printf("Using keystore at: %s.",keystoreFile);
ssl.setKeyStoreFile(keystoreFile);
ssl.setKeyStorePass("asdfgh");
//Build the web server.
GrizzlyWebServer webServer=new GrizzlyWebServer(getPort(9999),".",true);
//Add the servlet.
webServer.addGrizzlyAdapter(adapter, new String[]{"/"});
//Set SSL
webServer.setSSLConfig(ssl);
//Start it up.
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\n",
"https://localhost:9999/"));
webServer.start();
Now, I try to reach it in Java:
SSLContext ctx=null;
try {
ctx = SSLContext.getInstance("SSL");
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
ClientConfig config=new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null,ctx));
WebResource service=Client.create(new DefaultClientConfig()).resource("https://localhost:9999/");
//Attempt to view the user's page.
try{
service
.path("user/"+username)
.get(String.class);
}
And get:
com.sun.jersey.api.client.ClientHandlerException: 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
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:128)
at com.sun.jersey.api.client.Client.handle(Client.java:453)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:179)
From examples that I've found on the web, it seems like I would need to setup a Truststore then setup some sort of TrustManager. This seems like a lot of code and setup work for my simple little project. Is there an easier way to just say..I trust this cert and point to a .cert file?
When you say "is there an easier way to... trust this cert", that's exactly what you're doing by adding the cert to your Java trust store. And this is very, very easy to do, and there's nothing you need to do within your client app to get that trust store recognized or utilized.
On your client machine, find where your cacerts file is (that's your default Java trust store, and is, by default, located at <java-home>/lib/security/certs/cacerts.
Then, type the following:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to cacerts>
That will import the cert into your trust store, and after this, your client app will be able to connect to your Grizzly HTTPS server without issue.
If you don't want to import the cert into your default trust store -- i.e., you just want it to be available to this one client app, but not to anything else you run on your JVM on that machine -- then you can create a new trust store just for your app. Instead of passing keytool the path to the existing, default cacerts file, pass keytool the path to your new trust store file:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to new trust store>
You'll be asked to set and verify a new password for the trust store file. Then, when you start your client app, start it with the following parameters:
java -Djavax.net.ssl.trustStore=<path to new trust store> -Djavax.net.ssl.trustStorePassword=<trust store password>
Easy cheesy, really.
Here's the painful route:
SSLContext ctx = null;
try {
KeyStore trustStore;
trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("C:\\truststore_client"),
"asdfgh".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance("SunX509");
tmf.init(trustStore);
ctx = SSLContext.getInstance("SSL");
ctx.init(null, tmf.getTrustManagers(), null);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
ClientConfig config = new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new HTTPSProperties(null, ctx));
WebResource service = Client.create(config).resource(
"https://localhost:9999/");
service.addFilter(new HTTPBasicAuthFilter(username, password));
// Attempt to view the user's page.
try {
service.path("user/" + username).get(String.class);
} catch (Exception e) {
e.printStackTrace();
}
Gotta love those six different caught exceptions :). There are certainly some refactoring to simplify the code a bit. But, I like delfuego's -D options on the VM. I wish there was a javax.net.ssl.trustStore static property that I could just set. Just two lines of code and done. Anyone know where that would be?
This may be too much to ask, but, ideally the keytool would not be used. Instead, the trustedStore would be created dynamically by the code and the cert is added at runtime.
There must be a better answer.
Something to keep in mind is that this error isn't only due to self signed certs. The new Entrust CA certs fail with the same error, and the right thing to do is to update the server with the appropriate root certs, not to disable this important security feature.
Check this out: http://code.google.com/p/resting/. I could use resting to consume
HTTPS REST services.
The answer of delfuego is the simplest way to solve the certificate problem. But, in my case, one of our third party url (using https), updated their certificate every 2 months automatically. It means that I have to import the cert to our Java trust store manually every 2 months as well. Sometimes it caused production problems.
So, I made a method to solve it with SecureRestClientTrustManager to be able to consume https url without importing the cert file.
Here is the method:
public static String doPostSecureWithHeader(String url, String body, Map headers)
throws Exception {
log.info("start doPostSecureWithHeader " + url + " with param " + body);
long startTime;
long endTime;
startTime = System.currentTimeMillis();
Client client;
client = Client.create();
WebResource webResource;
webResource = null;
String output = null;
try{
SSLContext sslContext = null;
SecureRestClientTrustManager secureRestClientTrustManager = new SecureRestClientTrustManager();
sslContext = SSLContext.getInstance("SSL");
sslContext
.init(null,
new javax.net.ssl.TrustManager[] { secureRestClientTrustManager },
null);
DefaultClientConfig defaultClientConfig = new DefaultClientConfig();
defaultClientConfig
.getProperties()
.put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new com.sun.jersey.client.urlconnection.HTTPSProperties(
getHostnameVerifier(), sslContext));
client = Client.create(defaultClientConfig);
webResource = client.resource(url);
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
webResource.setProperty(entry.getKey(), entry.getValue());
}
}
WebResource.Builder builder =
webResource.accept("application/json");
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
builder.header(entry.getKey(), entry.getValue());
}
}
ClientResponse response = builder
.post(ClientResponse.class, body);
output = response.getEntity(String.class);
}
catch(Exception e){
log.error(e.getMessage(),e);
if(e.toString().contains("One or more of query value parameters are null")){
output="-1";
}
if(e.toString().contains("401 Unauthorized")){
throw e;
}
}
finally {
if (client!= null) {
client.destroy();
}
}
endTime = System.currentTimeMillis();
log.info("time hit "+ url +" selama "+ (endTime - startTime) + " milliseconds dengan output = "+output);
return output;
}