In the app I'm working on, I have to make an HTTPS connection to a web server. I was getting certificate not trusted errors and after consulting stackoverflow, I found this blog posting:
http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/
It seems like the CA for this server is not included in Android's default store. In a nutshell, I downloaded all the certificates, created a keystore with the BKS provider, imported the keys, imported the keystore into my project, subclassed the DefaultHttpClient class to force it to use my keystore.
After following the steps in the blog, it worked perfectly on the emulator. However, when I test it on a device, it fails intermittently. I think I've isolated a pattern. It seems like after some time has passed and I try to make an HTTPS connection, it will fail. Then, if I attempt the same connection again, it will succeed. If I wait a while and then try again, it fails the first time, succeeds on repeated attempts. I can probably fix it by just making multiple attempts on failure, but I would like to know what is going on. The behavior suggests some kind of cache but I don't know how to go about finding it or modifying its behavior. Does anyone have any suggestions about what is going on or know what I'm doing wrong? Any help would be appreciated.
I solved a similar problem by setting
System.setProperty("http.keepAlive", "false");
before I did my HTTP calls. There seems to be a problem with Android keep closed connections in its connection pool and trying to reuse them.
You can "skip" the certificates. Yes, you'll lose security but sometimes it's the only solution...
To do it. First, declare a TrustManager:
private TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
}
};
Second, change the SSL Context:
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
I hope help you.
Related
I would like to ask the following. We have a mobile app both for Android & iOS that exchanges data with a .NET server.
For Android the ksoap2 library is used, while for iOS the Alamofire with AEXML libraries are used.
We would like to enable encryption for the communication between the server and the apps, specifically Message Security with Mutual Certificates (https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-mutual-certificates)
I am not able to find any information how either the Android or the iOS client could encrypt/decrypt the requests/responses.
Can you please provide any relative information?
Thanks in advance!
For the iOS Part.
By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework.
While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities.
In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the ServerTrustPolicy.
ServerTrustPolicy
The ServerTrustPolicy enumeration evaluates the server trust generally provided by an URLAuthenticationChallenge when connecting to a server over a secure HTTPS connection.
let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: ServerTrustPolicy.certificates(),
validateCertificateChain: true,
validateHost: true
)
There are many different cases of server trust evaluation giving you complete control over the validation process:
performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge.
pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates.
pinPublicKeys: Uses the pinned public keys to validate the server trust.
The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys.
disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.
customEvaluation: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution.
AlamoFire documentation
For Android part, i am not experienced with but i came across someone asking about the same thing and got an answer with
what you need to do is only to install your certificate into the webserver and call the webservice URL like https://my.webservice.url/ instead of http://my.webservice.url/.
If your certificate is a self-signed certificate, means you did not
bought it from a certificate authority, you will need to set the
SSLSocketFactory. You can check the project wiki on how to do that:
http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#How_to_set_the_SSLSocketFactory_on_a_https_connection__in_order
Check Here.
This might be Helpful too
UPDATE: i've found this framework SOAPEEngine
this one.
Supports Basic, Digest and NTLM Authentication, WS-Security, Client side Certificate and custom security header.
you check its example for more clarifications too.
Message encryption with WCF is done through the WS-Security protocol, by setting the security attribute mode to Message. As you have undoubtedly realized by now, WS-Security is not exactly popular on the Android and iOS platforms, mostly due to it having been superseded by other technologies (like HTTPS,) so your choices in terms of existing libraries are not abundant. The fact that not even Microsoft-owned Xamarin supports it says a lot.
First, a word on WS-Security, This protocol provides three main means of enhancing message security:
Authentication through security tokens
Signing SOAP messages
Encryption of SOAP messages
So a conforming implementation should really provide all three of these functions, but we are mostly interested in encryption here, as from the question and comments it seems like you have the authentication part working.
Therefore, assuming we are looking for a mobile platform library providing minimal WCF compatibility with WS-Security signing and encryption:
Android
On Android, the closes to your needs is WSS-Client for Android. This project:
... implements the OASIS Web Service Security (WSS) standard for
Android platforms and makes XML Encryption and XML Signature available
for tablets and smartphones.
Note that this is GPL-licensed software. The readme says to contact the author for commercial license details. However, it seems to do what you're looking for. Once you have negotiated the key exchange with the server, to encrypt a previously constructed SOAP message using the SOAPUtil class you would do something like:
SOAPMessage message = SOAPUtil.createSOAPMessage();
//... Populate the message (possibly with another library)
SecCrypto serverCrypto = new SecCrypto(serverCertificate, null);
SecCrypto clientCrypto = new SecCrypto(clientPublicKey, clientPrivateKey);
SOAPMessage securedMessage = SOAPUtil.secureSOAPMessage(message, new HashMap<String,String>(SecConstants.REQ_ENCRYPT_SIGN, "yes"), clientCrypto, serverCrypto);
//...
SOAPMessage returnedSecuredMessage = SOAPUtil.sendSOAPMessage(context, securedMessage, endpoint, cryptoParams);
SOAPMessage returnedMessage = SOAPUtil.validateSOAPMessage(returnedSecuredMessage, new HashMap<String,String>(SecConstants.RES_DECRYPT_VERIFY, "yes", decCrypto);
Nevertheless, be prepared to do quite a bit of configuration work and debugging to make it match your server's needs.
If you are looking for a more current and actively developed product, Quasar Development offers a WS-Security implementation for Android.
iOS
Things look a lot more bleak on the iOS side. There are a few libraries claiming varying degrees of support for WSS, but none of them seem to match your needs:
At first SOAPEngine looks the most promising, as it claims
support for WS-Security. However, in a footnote it says that it has a
limitation that it only supports the WCF basicHttpBinding. This
would actually be OK if true. The binding used in the sample code you
linked to in the question is wsHttpBinding however it's important
to note that both wsHttpBinding and basicHttpBinding have support
for encryption though WS-Security. The difference is that
wsHttpBinding supports WS-Security by default (whereas it needs to
be enabled with basicHttpBinding) and it also supports
WS-ReliableMessaging and some other features you may or may not
care about. But the basicHttpBinding is the one intended for
compatibility with other technologies. So in order to have
WS-Security encryption on your WCF server and maximize compatibility
with other technologies at the same time, it would be OK to use
basicHttpBinding and enable WS-Security signing and encryption by
setting the mode security attribute to Message. With the
Message attribute, from the docs:
Security is provided using SOAP message security. By default, the body
is encrypted and signed. For this binding, the system requires that
the server certificate be provided to the client out of band. The only
valid ClientCredentialType for this binding is Certificate.
But this is of no use as SOAPEngine does not have any support for
encrypting messages (or at least I could not find any support for it
in the API). The only WS-Security function it supports is
authentication. So the claim that it supports WS-Security seems
misleading as the support is quite limited.
ServiceNow offers very limited support for WS-Security. It only
supports verifying server signatures. No encryption or signing on the
client side.
Chilkat has some rudimentary XML support and there is sample
code for WS-Security authentication. I didn't see any support or
sample code for WS-Security encryption.
Therefore for iOS, to the best of my knowledge, your two options are:
Pick one of the existing libraries that best matches your other needs
and reach out to the developer and see if you can get them to add the
WS-Security features you need.
Implement the bare minimum features
you need yourself. The spec is actually not that complicated and
there is sample code out there (for non-mobile platforms) that you
can use as guide, like WSS4J for example.
In Android:
I use kasoap2 to call the web services, but before the call, to enable mutual authentication with client certificate you need to initialize a SSLContext with the client authentication keys (KeyManager).
To do that you have to load your certificate and the corresponding password in a KeyStore object, my certificate is a *.pfx file. I cerate a "PKCS12" KeyStore instance. Then you need a KeyManagerFactory object to obtain the KeyManager array. I use a "PKIX" KeyManagerFactory instance. The KeyManager array is needed to init the SSLContext.
Here is an example:
public void enableMutualAuthentication(String filename, String password) {
try {
// InputStream to read the certificate file
FileInputStream cert = new FileInputStream(filename);
char[] pass = password.toCharArray();
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(cert ,pass);
cert.close();
KeyManagerFactory keymanagerfactory = javax.net.ssl.KeyManagerFactory.getInstance("PKIX");
keymanagerfactory.init(keystore, pass);
KeyManager[] keymanagers = keymanagerfactory.getKeyManagers();
// This is not for the mutual authentication.
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
// Install the mutual authentication manager
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(keymanagers, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
Check those links, was what help me most.
https://chariotsolutions.com/blog/post/https-with-client-certificates-on/
http://callistaenterprise.se/blogg/teknik/2011/11/24/android-tlsssl-mutual-authentication/
I am using Socket in android with https so I need to make the connection secure. So for that I am using this certificate-
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 {
}
} };
Now all is working well and the connection is also established, but I am not understanding or I don't know if using this type of certificate is secure or not?
Should I use this or not?
No, this is vulnerable to a Man-in-the-middle attack!
You are using https, so the data is encrypted while being transmitted, which is good. But there is no check that the other side is who you expect it to be, which is bad.
The above custom TrustManager does not make any checks and implicitly trusts everything. In the code it is even called trustAllCerts, which you explicitly do not want to do. You will want to check the certificate chain to see if it comes from a source that you trust.
The problem is that an attacker can intercept your https traffic by placing a proxy between you and your endpoint. The intercepting proxy can provide its own certificate and can thus decrypt the traffic and do whatever it wants with it. it can also set up an https connection with the endpoint and read the data it gets from there. Thus the attacker can read what you send, can read what you'll receive and can change what you send and will receive.
My android application connects to an URL provided by the user. In case of HTTPS connections, if the server's certificate is issued by a CA that already exists in the Android's TrustManager, everything is fine.
But if the server uses a self-signed certificate how can I obtain that certificate and store it in the TrustManager on first connection?
I am using OkHttp library for performing network tasks. The solution that I have currently forces me to add the certificate in the application's raw folder during development but this will not work for the above mentioned scenario. The code that I am using currently is as below:
private KeyStore readKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "testPass";
InputStream is = null;
try {
is = activity.getApplicationContext().getResources().openRawResource(R.raw.server_key);
ks.load(is, password.toCharArray());
} finally {
if (is != null)
is.close();
}
return ks;
}
private OkHttpClient getOkHttpClient() throws CertificateException, IOException, KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(readKeyStore());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(readKeyStore(), "testPass".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
return new OkHttpClient().setSslSocketFactory(sslContext.getSocketFactory());
}
The simple solution
Here's a code example of a trust manager callback. From the callback you can either store the self-signed certificates or just accept them right away. But you SHOULD NOT do neither. By accepting self-signed certs, you are subject to connecting to fake or malicious servers which can steal personal data, install malware, and do other nasty things.
The better-than-simple solution
If a server offers a self-signed certificate that you, the developer, trust at compile time, you can bundle the cert into the app like you're already doing. It would be much better if the server had a CA-signed certificate, but if you really need to connect to a server that offers a self-signed cert, this will (have to) do.
A nicer solution
Never accept self-signed certificates in runtime. If you want to allow your users to connect to these kind of servers, you can show a warning message like "proceed at your own risk" before accepting the self-signed cert.
You should not add it on first connection. Ideally they should just pay to get a proper certificate signed by a CA.
But even then you might find popular new CAs like Let's Encrypt that are not supported by the installed Java version.
In these cases you can either add the individual cert or the new CA manually using keytool for a JVM, or load it via standard Android mechanisms.
Or you can bundle those with the app. This example code shows how to load bundles certificates in addition to existing system certs using a Merged TrustStore https://github.com/yschimke/oksocial/blob/master/src/main/java/com/baulsupp/oksocial/security/CertificateUtils.java
Recently received a warning letter that my application security threatened.
---------- Forwarded message ----------
From: CERT Coordination Center <cert#cert.org>
Subject: SSL Vulnerability in ********* VU#582497
Cc: cert#cert.org
The letter contains the following information:
We've recently been evaluating with CERT Tapioca
http://www.cert.org/blogs/certcc/post.cfm?EntryID=203 the use of SSL
by Android apps. Through automated testing, we are logging apps that
cause traffic to be sent or received over an HTTPS connection that has
an invalid SSL certificate chain. The following application has demonstrated this incorrect behavior.
Some caveats that may affect the impact of the test results:
1) We have not yet investigated the content that is sent over HTTPS
with an invalid SSL certificate chain. If the information is not
sensitive, one might argue that the vulnerability does not really
have an impact. However, the other argument is that the use of
unvalidated SSL is a vulnerability that needs to be corrected,
regardless of the content sent or received.
2) It could be that your application itself uses SSL properly, but it
includes a third-party library that itself does improper SSL
validation. In such a case, this third-party library would need to
be updated. Or if a fix isn't available, the library's author
should be notified to let them know that they need to fix the
library.
3) Due to the UI automation used in the dynamic testing that we
performed, there is a small chance that the application or the
browser components used by the application did correctly warn the
user before proceeding. If the UI automation did happen to click
the button required to proceed despite an invalid certificate, then
this could be considered a false positive. If you believe this to
be the case, please respond and let us know.
For request, I use robospice-spring-android. ssl usage:
static {
try {
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManager[] trustManagerArray = {new NullX509TrustManager()};
sslc.init(null, trustManagerArray, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier());
} catch (Exception e) {
}
}
private static class NullX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
private static class NullHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
Can anyone suggest on this issue. What is my fault?
Can anyone suggest on this issue. What is my fault?
You effectively disable any kind of authentication built into TLS. An attacker can thus easily mount a man-in-the-middle attack or a phishing attack, that is listen to and manipulate the encrypted traffic or claim to be the real server.
Such can usually easy be done with ARP or DHCP spoofing inside the local LAN or a public WLAN, so the problem described is not a theoretical but a real problem.
In detail:
TrustManager[] trustManagerArray = {new NullX509TrustManager()};
sslc.init(null, trustManagerArray, null);
Here you disable the check if the certificate is signed by a trusted CA. The attacker can now use any self-signed certificate or a certificate signed by an untrusted CA instead of the real one.
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier());
Here you disable the check to verify the hostname inside the certificate against the host you want to access. Example:
the site you want to access is super-secure.example and you bought a certificate for it
the attacker has the site attacker.example and bought a certificate for it
Usually the client will verify that the name in the certificate matches the name the client connected to. But you explicitly disabled this check with the code above and thus the attackers certificate gets accepted.
You main fault is probably that you just copied some code from somewhere without understanding what it does. Bad idea in any case, but especially for anything related to security.
In the app I'm working on, I have to make an HTTPS connection to a web server. I was getting certificate not trusted errors and after consulting stackoverflow, I found this blog posting: http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/
It seems like the CA for this server is not included in Android's default store. In a nutshell, I downloaded all the certificates, created a keystore with the BKS provider, imported the keys, imported the keystore into my project, subclassed the DefaultHttpClient class to force it to use my keystore.
After following the steps in the blog, it worked perfectly on the emulator. However, when I test it on a device, it fails intermittently. I think I've isolated a pattern. It seems like after some time has passed and I try to make an HTTPS connection, it will fail. Then, if I attempt the same connection again, it will succeed. If I wait a while and then try again, it fails the first time, succeeds on repeated attempts. I can probably fix it by just making multiple attempts on failure, but I would like to know what is going on. The behavior suggests some kind of cache but I don't know how to go about finding it or modifying its behavior. Does anyone have any suggestions about what is going on or know what I'm doing wrong? Any help would be appreciated.
Pure speculation, but I've dealth with similar situations a while back in a Windows/IE environment where the cert was failing intermittently. In both occasions I had proxies that I didn't realize were acting as proxies interfering.
The first one was Fiddler - a web debugger, which was proxying the cert to the browser when I had it engaged.
The second time I had an issue with our corporate internet filtering solution (Web Sense) also acting as a proxy, sort of - it would allow the cert information through correctly eventually but not on the first try.
I don't know if this is your case, but that's the only time I've ever seen behavior like what you're describing.
Before open connection, you can create a TrustManager that doest not validate the certificate chains, and install it to your HttpsURLConnection. See below:
`
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
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 {
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
`