Related
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
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
I want to check whether the current LDAP server is up or down before doing an authentication. I am using UnboundID LDAP SDK. IS there any possible way to do this?
Check if the port is up and running. It's pur Java. No SSL Factory or SSL Context or Credentials needed. Has a time out in milliseconds, if the package is dropped by a firewall.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
public static boolean isPortReachable(String host, int port, int timeOutMS) {
try (Socket serverSocket = new Socket();) {
serverSocket.connect(new InetSocketAddress(host, port), timeOutMS);
return true;
} catch (final IOException e) { /* Ignore, Port not reachable */ }
return false;
}
Usage for LDAP or LDAPS:
if (isPortReachable(hostname, 389, 200)) { ... }
if (isPortReachable(hostname, 636, 200)) { ... }
There is a method isConnected():
public boolean isConnected()
Indicates whether this connection is currently established.
Returns:
true if this connection is currently established, or false if it is not.
So a simple example something like:
LDAPConnection ldc = new LDAPConnection()
ldc.connect(...);
if (ldc.isConnected())
{
do good stuff
}
else
{
getLDAPConnection(...);
}
Should do the trick.
The best way to test whether any resource is available is simply to try to use it in the normal course of your application, and cope with the errors as they arise.
Otherwise you're trying to predict the future.
We can do this with SSUtil. Here an authenticated entry will not be needed and we can check whether the server is up or down before doing any authentication.
SSLUtil su = new SSLUtil(new TrustAllTrustManager());
SSLSocketFactory sf = su.createSSLSocketFactory();
LDAPConnection connection = new LDAPConnection(sf,"localhost", 10636);
Looking for pros and cons for this solution.
I've developed a Java CFX tag to be used in ColdFusion 10.
There is a Java class which gets an URL as param und should give back content of the website behind that URL.
We're using https protocol and I need to ignore certificate errors. Therefor I've implemented the solution being suggested here: HTTPS hostname wrong: should be . What causes this?
I've tested my methods in the way Adobe recommends it here: Approaches to debugging Java CFX tags
Everything works fine.
But when I attach my CFX tag to my ColdFusion Instance and try to use it from ColdFusion I'll become an error like this:
java.io.IOException: HTTPS hostname wrong
My question is, why? Debugging my CFX Tag shows no error, but using it in ColdFusion brings that error. For me it looks like ColdFusion overwrites some of my class declarations on runtime. Does anyone have some ideas? Is this phenomenon known? Has anybody else experienced that weird behaviour? Or am I getting something wrong?
To understand my problem here are some more facts:
OS: Suse Linux Enterprise Server
CF: ColdFusion 10 Multiserver
Dev-Environment for Java: NetBeans 7.1.2
Dev-Environment for CF: ColdFusion Builder 2.1
Some source code for demonstration purposes:
...
url = new URL("https://domainname/path/file.type");
HostnameVerifier hv = new HostnameVerifier() {
#Override
public boolean verify(String urlHostName, SSLSession ssls) {
System.out.println("Warning: URL HOST: " + urlHostName + " vs. " + ssls.getPeerHost());
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
URLConnection conn = url.openConnection();
...(Do something)...
Problem:
Debbuging with main Method works fine
Using CFX Tag in ColdFusion in the same way fails.
Okay, finally I've managed to get it working - here's how:
First I've rebuild some code, as follows.
SSLContext sc = SSLContext.getInstance("SSLv3");
sc.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(sc);
URL url = new URL(URLStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
/**
* Create our own hostname verifier which always returns true
* to guarantee that our certificates are accepted
*/
conn.setHostnameVerifier(new HostnameVerifier(){
#Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
DefaultTrustManager reads as follows:
private static class DefaultTrustManager implements X509TrustManager {
#Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
As you can see, I've set my own HostnameVerifier not as default but on the concrete connection (conn.setHostNameVerifier()). In addition I have to
cast url.openConnection to HttpsURLConnection.
So far. And then again - Debugger workes like a charm, Cfx deployed on CF-Server - nothing happens, but strange Error in Log:
"Error","ajp-bio-8013-exec-1","12/07/12","16:53:07","sys_bdr","com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection The specific sequence of files included or processed is: /path/to/test/file/test.cfm, line: 61 "
java.lang.ClassCastException: com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection
This last problem could be fixed by using a solution described here: http://danwatt.org/2012/06/making-java-coldfusion-tomcat-and-payflowpro-play-nicely/
ColdFusion needs to be started with another JVM parameter:
-Djava.protocol.handler.pkgs=javax.net.ssl
So in conclusion: some coding problems here, which could be fixed and in addition a missing ColdFusion JVM parameter.
I'm trying to get an application running on top of Tomcat 6 to connect to an LDAP server over SSL.
I imported certificate of the server to keystore using:
C:\Program Files\Java\jdk1.6.0_32\jre\lib\security>keytool -importcert -trustcacerts -file mycert -alias ca_alias -keystore "c:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts"
When I start Tomcat with SSL debugging turned on, according to logs Tomcat is using the correct certificate file:
trustStore is: C:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts
However, Tomcat does not add the cert I just imported - all other certs in the cacerts file are printed to the log - and connection fails:
handling exception: 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
Restarting Tomcat does not help. I have verified with keytool -list command that the new cert indeed exists on the file.
Why Tomcat keeps on ignoring my new cert?
EDIT:
Seems that the issue was caused by Windows 7 VirtualStore. Keytool created a new copy of the cacert file, and Tomcat used the original file.
JVM needs restart after importing certs to the keystore.
Check to see whether there is a key with the same CN information but a different alias.
I have had similar problems before when I tried to import a newer version of a certificate but left the older version in the keystore. My Java programs would simply find the first matching CN key in the keystore (which was the old expired one) and try to use that, even though there was a newer one which also matched the CN.
Also ensure that the authenticating Root certificate (and Intermediate certificate if applicable) exist in the keystore. If you're authenticating against one of the major security providers such as Verisign or Globalsign, they will usually provide you with the root and intermediate certificates. If these certificates exist in the keystore already, ensure they are still in validity. You need to have all the certificates from your personal certificate all the way down the authentication chain to the root, existing in your keystore, so that it understands how to validate your credentials.
What you described is exactly what I´ve been getting when using cmd.exe and a regular user although member of administrative group on a Windows Server. You have to start cmd.exe in administration mode to apply the changes in to cacerts files. At least on the Win2k8 OS´s.
If you do not do this carets will show you in the keytool.exe -list view the newly added certs but Tomcat won´t see them. Not sure why so. But when you do add it with cmd.exe started as Administrator Tomcat is fine with the newly added certs.
You can also use Djavax.net.debug="ssl,handshake" to see what Tomcat reads from cacerts file.
In my case I looked through so many things before I figured out what was wrong... I added the certificate to different keystores, I added all certificates in the chain (which is pointless btw), I downloaded the cert again for my own sanity and checked the serial number, and even inspected the downloaded cert to make sure it had all the correct information.
I ended up writing a TLS verifying client app in order to debug the issue. Not only did the remote server I was connecting to support only TLS 1.2 (disabled by default in my version of Java 7), the server also supported none of the ciphers that were enabled in my client. It turns out Java 7 had fewer than half of its supported ciphers enabled, many of them being really insecure and some of the most secure ones were disabled.
After some cross-checking, I came up with the following ordered list of TLS 1.2-supported secure ciphers:
new String[] {
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
"TLS_DHE_RSA_WITH_AES_256_CCM",
"TLS_DHE_RSA_WITH_AES_128_CCM",
"TLS_DHE_PSK_WITH_AES_256_CCM",
"TLS_DHE_PSK_WITH_AES_128_CCM",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_AES_128_CCM_SHA256"
}
If there are any crypto experts around, feel free to update this list. I used Qualys SSL Labs, this Information Security SE answer, and IANA as my sources.
For those who want a sample of the source code I used, see below. I was using Apache Commons HttpClient 3.0, so you'll probably need to download the following binaries:
https://archive.apache.org/dist/httpcomponents/commons-httpclient/3.0/binary/commons-httpclient-3.0.1.zip
https://archive.apache.org/dist/commons/logging/binaries/commons-logging-1.0.4.zip
https://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.3.zip
https://archive.apache.org/dist/commons/lang/binaries/commons-lang-2.6-bin.zip
TLS12SocketFactory.java
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.*;
import org.apache.commons.lang.StringUtils;
public class TLS12SocketFactory implements SecureProtocolSocketFactory {
private final SecureProtocolSocketFactory base;
public TLS12SocketFactory()
{
this.base = (SecureProtocolSocketFactory)Protocol.getProtocol("https").getSocketFactory();
}
private Socket acceptOnlyTLS12(Socket socket)
{
if(socket instanceof javax.net.ssl.SSLSocket) {
final javax.net.ssl.SSLSocket s = (javax.net.ssl.SSLSocket)socket;
// Set TLS 1.2
s.setEnabledProtocols(new String[]{ "TLSv1.2" });
// Using recommended ciphers from https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4
List<String> recommended = new ArrayList(Arrays.asList(new String[]{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_CCM", "TLS_DHE_RSA_WITH_AES_128_CCM", "TLS_DHE_PSK_WITH_AES_256_CCM", "TLS_DHE_PSK_WITH_AES_128_CCM", "TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_SHA256" }));
recommended.retainAll(Arrays.asList(s.getSupportedCipherSuites()));
if(recommended.size() == 0) {
System.err.println("No supported modern ciphers. Update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
} else if(recommended.size() < 3) {
System.out.println("Few supported modern ciphers. It's recommended to update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
}
s.setEnabledCipherSuites(recommended.toArray(new String[0]));
// Log matched cipher and cert
s.addHandshakeCompletedListener(new javax.net.ssl.HandshakeCompletedListener() {
#Override
public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent hce) {
String print = s.getInetAddress().getHostName() + System.lineSeparator() + hce.getCipherSuite() + System.lineSeparator();
try {
for(java.security.cert.Certificate cert : hce.getPeerCertificates()) {
List<String> certStrings = Arrays.asList(cert.toString().split("\r?\n"));
for(int line = 0; line < certStrings.size(); line++) {
if(certStrings.get(line).startsWith("Certificate Extensions:")) {
print += System.lineSeparator() + StringUtils.join(certStrings.subList(2, line-1), System.lineSeparator()) + System.lineSeparator();
break;
}
}
}
} catch (javax.net.ssl.SSLPeerUnverifiedException ex) {
print += "Non-certificate based cipher used" + System.lineSeparator();
}
System.out.println(print);
}
});
}
return socket;
}
#Override
public Socket createSocket(String host, int port) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort));
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort, params));
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
{
return acceptOnlyTLS12(base.createSocket(socket, host, port, autoClose));
}
}
Main.java
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.HttpClientParams;
public class Main {
public static void main(String[] args) {
List<java.net.URI> locations = new ArrayList<>();
for(String arg : args) {
java.net.URI location = java.net.URI.create(arg);
if(location.isAbsolute() && location.getScheme().equals("https")) {
locations.add(location);
} else {
System.out.println("Skipping invalid URL: " + arg);
}
}
System.out.println("Connecting to URL's");
System.out.println();
System.out.println("-------------------------");
TLS12SocketFactory tls12factory = new TLS12SocketFactory();
Protocol.registerProtocol("httpss", new Protocol("httpss", tls12factory, 443));
for(java.net.URI location : locations) {
System.out.println();
try {
// Form request
String tls12url = location.toString().replaceFirst("^https:", "httpss:");
HttpMethod method = new HeadMethod(tls12url);
// Send request
HttpClientParams params = new HttpClientParams();
params.setParameter(HttpClientParams.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
new HttpClient(params).executeMethod(method);
method.setFollowRedirects(true);
// Print response
System.out.println(location.toString());
System.out.println(method.getStatusLine().toString());
} catch (javax.net.ssl.SSLHandshakeException ex) {
System.out.println("There was an error making a secure connection to " + location.getHost());
ex.printStackTrace(System.out);
} catch (HttpException ex) {
System.out.println("There was an error with the external webpage");
ex.printStackTrace(System.out);
} catch (Exception ex) {
System.out.println("Could not complete request");
ex.printStackTrace(System.out);
}
}
System.out.println();
System.out.println("-------------------------");
System.out.println();
try {
// Load supported SSL Ciphers
System.out.println("Supported ciphers");
System.out.println();
System.out.println("-------------------------");
System.out.println();
javax.net.ssl.SSLSocket socket = (javax.net.ssl.SSLSocket)tls12factory.createSocket("www.google.com", 443);
for(String cipher : socket.getSupportedCipherSuites()) {
System.out.println(cipher);
}
System.out.println();
System.out.println("-------------------------");
System.out.println();
// Load enabled SSL Ciphers
System.out.println("Enabled ciphers");
System.out.println();
System.out.println("-------------------------");
System.out.println();
for(String cipher : socket.getEnabledCipherSuites()) {
System.out.println(cipher);
}
System.out.println();
System.out.println("-------------------------");
System.out.println();
// Load the JDK's cacerts keystore file
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
System.out.println("Loading keystore");
System.out.println(filename);
System.out.println();
System.out.println("-------------------------");
System.out.println();
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
// This class retrieves the most-trusted CAs from the keystore
PKIXParameters params = new PKIXParameters(keystore);
// Get the set of trust anchors, which contain the most-trusted CA certificates
for (TrustAnchor ta : params.getTrustAnchors()) {
// Print certificate
System.out.println(ta.getTrustedCert());
}
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | IOException ex) {
System.out.println("Could not load keystore");
ex.printStackTrace(System.out);
}
}
}