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.
Related
I was reading through Certificate Transparency (CT) and its capabilities to monitor use and abuse of certificates. I am wondering if CT can detect SSL Pinning bypass in Mobile application (in case of Web Application for that matter). Request you to enlighten me on this. If yes, how? if no, why?
TL;DR
CT can not detect bypass of SSL Pinning in your mobile applications
CT and SSL Pinning are two different things. In SSL Pinning you are ensuring that either certificate hash/public key hash received during TLS handshake matched with the one pinned within the application to ensure that you only trust a whitelisted certificate instead of trusting everything in the device trust store whereas through CT we perform cryptographic checks to ensure if we received a valid SCT(Signed Certificate Timestamp) and log server pushed the cert entries to a public append-only log so as to ensure that there is no rouge cert generated for our domains by malicious trusted CA or through the compromise of a CA.
Also, note that with CT we only ensure that the certificates issued by PUBLIC CAs were issued legitimately whereas while performing MITM to intercept app traffic/bypassing pinning we use the certificate of proxy server(Charles/Burp/ZAP) which is not a public CA and hence the checks are not enforced through CT
Let's say that I have an app that transmits some sensitive info to my server. I want to reduce the risk of a man-in-the-middle attack on my users, so I pin the keys used by my server in the Network Security Configuration file.
But, let's say a user of my app does not trust the CA that issued our certificate, and has removed it from the list of trusted CAs, or maybe an OS update removes the CA because it has been found to be behaving badly.
Ideally, in such a case I would like my app to refuse to connect to the server. I only want it to make the connection if the certificate presented by the server is signed with a key in the pinset AND comes from a CA that is trusted by the OS/user. Does pinning a key in the network security config file accomplish this? Or, are pinned keys trusted no matter what?
The HPKP instructs the browser to store the signature for your server certificate for the period of time you specify. Using HPKP does not replace the standard certificate validation.
In your scenario the pinned PK in the browser will be valid for the server certificate your server is presenting, but the actual certificate validation will fail since the CA is not trusted.
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.
I have a mobile app which accesses web services at https://myserver.com/mywebservice.
If I create a self signed certificate and I put it on my server, when the mobile app accesses the web service it sees the certificate but it doesn't recognize it as it is self signed. But in this case if I have a "man in the middle" attack, the attacker can create it's own certificate, so the app sees the certificate and again it doesn't recognize it.
Question: When you use a certificate like those from goDaddy, is iOS or Android programmed to trust goDaddy certificates in order to work, so the fake certificate that the attacker uses is considered invalid? So if I make the app trust my certificate, I can use my self signed certificate the same way?
Also How can I know which certificates are trusted by iOS or Android?
Generally your app or the OS needs to trust the CA that issued the server certificate for this to work. If it is issued by a CA whose certificate is pre-installed, things just work. If not, you need to either install the CA in the OS trust store or modify your app to to trust it. It doesn't really matter who created the certificate, VeriSign or goDaddy certs are no more 'special' than your self-signed ones.
As for getting a list of trusted certs, Android 4.x and later lists those in Settings->Security->Trusted Credentials. For earlier version you have to write some code to enumerate them. Don't know about iOS.
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.