Browser-like behavior on invalid/self-signed certificates - android

It feels like I searched the whole web already, but all I found are hints on how to accept an invalid or self-signed certificate automatically. (Using custom HttpClient and SSLSocketFactory - I already got that working.)
But what I want for my app is that the user gets a browser-like dialog asking something like "Do you really want to trust this server? Here, have a look at its certificate." (But only if the certificate isn't trusted by the default checks.)
Then the certificate should be put in the app's certificate store, so next time it's accepted automatically.
So what I need to know is:
How to download the certificate (chain) for a specific host/port combination (to be able to show it to the user)?
How to store the certificate in a way so I can load it in a KeyStore later?
This is my planned work flow in the app:
Send a request to the server with my custom HttpClient. Maybe the certificate is trusted by the system or already in my store (if yes, go to 4).
If the request failed due to SSL issues, show the user the certificate and ask whether to trust this connection.
If the user chose to trust, store the certificate in my store and go to 1.
Hooray, connection is ready to use.
So anyone knows how to do this?

Don't disable CA checks and catch the exception when trying to connect to a non CA certificate. When you catch the exception, launch your page for the user to accept/decline. If he accepts, then launch a new connection with CA checks disabled.

Related

Handle site certificate expiry with OkHttp Certificate pinning on Android

I have an app that has a site certificate hash pinned with OkHttp3 similar to the method mentioned here
The site certificate is about to expire soon though and I realized that I need to be able to support a new site certificate as soon as I switch update that on the site, as well as let the current one still work until then. Is there a way to pin 2 certificates for the same site so that both are supported seamlessly (i.e when the current one expires and one is no longer valid as well as the new one as soon it is updated)?
TIA
This is the documented behaviour of CertificatePinner. So just add pins for your current and old certificate.
http://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html#check-java.lang.String-java.util.List-
Confirms that at least one of the certificates pinned for hostname is
in peerCertificates. Does nothing if there are no certificates pinned
for hostname. OkHttp calls this after a successful TLS handshake, but
before the connection is used.
n.b. Because of the expiry of your certificates may happen before old clients update, it is usually advised to also pin against the CA you use also which is quite likely to be consistent across old and new certificates. This will ensure even if your current and next certificate expire or are revoked, you would be able to authenticate with a new certificate generated and older clients.
https://community.letsencrypt.org/t/hpkp-best-practices-if-you-choose-to-implement/4625

Trusting a certificate without adding it to Trust Manger

I am working on an Email app on Android. When we connect to Server which presents us a self-signed certificate, and we do not have it in our Trust manager then we show a verbose message to the user telling about the potential risk trusting this certificate like MITM.
But if user still consents to trust this certificate anyways, then we use an Insecure trust manager, that trusts any certificate for this connection.
This just increases the probability of MITM attacks.
To mitigate this (to some extent), we store the serial number and the issuer DN of the certificate locally.
Now next time when user try to connect to this server we check for the equality of serial number + issuer DN of the certificate with the one we have locally.
Is this a right approach/solution to the given problem?
Or is there something else we can do?
P.S. One solution can be that the user get the certificates and add to the default trust manager by installing it. But this will be beyond the scope of my app.

How to programmatically get server's certificate and add to the truestore, and check the certificate

In my Android app I want to use a https connection to a USER-SPECIFIED server which uses a self-signed certificate.
Because the https server is user specified, I don't know the server's certificate before, therefore I want to:
get the server's certificate dynamically
add this certificate's public key to the app's trust store
authenticate the server
I do NOT want to simply accept every self-signed certificate without the user checking the certificate
I am struggling with the first step, can anybody show me a working example with basic explications?
Any hints are appreciated. Thanks a lot
I haven't tried it but this looks promising:
https://github.com/cesarferreira/Android-Self-Signed-SSL-certificate-example

How to get a security certificate from the server and install in the device certificate list like in Browser

I need to implement an SSL connection similar to a browser implementation, I need to show a dialog if the certificate is not from a trusted source and accept the certificate and proceed with the connection. please guide me.
You can implement a custom X509TrustManager that verifies the server certificate based on the standard trusted root certificates from Android and additionally an own trusted list.
See checkServerTrusted(..) method.
If an invalid/untrusted certificate appears you can ask the user if it should be added to your own trusted list.
You can only install trusted certificates on Android 4.0 and later. You can catch the certificate error and then ask the user to install the certificate using the KeyChain API. It requires user confirmation, so you cannot do this automatically.

Android and extra validation of certificates

I want to perform extra validation for SSL connections I make in an android app.
Basically I need to be able to:
See if the certificate of the remote host I am connected to has Extended Validation (EV) status
Find out the root certificate authority for the certificate of the remote end. E.g. I want to know if it is a VeriSign certificate or not.
To elaborate a bit more, I am writing a client that needs a high level of security and our organization is using EV certificates from VeriSign on all servers. I want to prevent any compromised certificate authority, or anyone that can fool a certificate authority to forge a certificate for our domain be able to hijack the application.
Is this doable and if so, how? Is there a way to get more information about the certificate of the remote end from a URLConnection object or a HTTPClient object and so on?
First: you can't possibly 'prevent any compromised certificate authority' from issuing a certificate for your domain. If it is compromised, they can issue whatever they want. What you can do is create a trust store with a limited number of trusted CA certificates, say, VeriSign only. That way, even if an related CA is compromised and issues a cert for your domain, it wouldn't matter since you don't trust it in the first place. That would also take care of second bullet. To have additional checks you need to implement and install your own X509TrustManager. Check the JSSE reference guide for details.

Categories

Resources