I am trying to implement certificate pinning on my app. As far as I understood there are 3 types of pinning available:
certificate pinning; The certificate should be "hardcoded" in the app and compared with the ones from the server. This is kind of bad because if a certificate needs to be rotated an update of the app is required.
public key pinning; From my research it seems this is deprecated
SubjectPublicKeyInfo(SPKI) pinning; this is a hash of the public key. The advantage with this approach is that if a certificates neeeds to be rotated the public key of the new certificate can remain the same for the new certificate, this way an update is not required.
Based on what I've said, it seems that the recommended way to do this is SPKI pinning.
My app can connect to multiple servers, based on the ip which the user types at login.
This is my idea of implementing but I am not sure it would work.
I would create an Intermediate Certificate signed by a CA. Then create my own public-private keys. Then each server that my app can connect would need to request from me to sign their certificate with my private key.
The hierarchy would be something of RootCertificate(self signed by CA), then my intermediate certificate(signed by CA) and then leafs certificates for every server that my app could connect signed by my private key.
On the app I would store the hash of the public key from my intermediate certificate. Upon first connection to a random server the app will get 3 certificates: the one for the server, my intermediate certificate and the Root Certificate. When checking the hashes will find a match with my public key.
What do you think? Could this be implemented? Did I understand something wrong? Is this approach prone to mitm attack?
Thanks!
My app can connect to multiple servers, based on the ip which the user types at login. This is my idea of implementing but I am not sure it would work.
I would create an Intermediate Certificate signed by a CA. Then create my own public-private keys. Then each server that my app can connect would need to request from me to sign their certificate with my private key. The hierarchy would be something of RootCertificate(self signed by CA), then my intermediate certificate(signed by CA) and then leafs certificates for every server that my app could connect signed by my private key.
On the app I would store the hash of the public key from my intermediate certificate. Upon first connection to a random server the app will get 3 certificates: the one for the server, my intermediate certificate and the Root Certificate. When checking the hashes will find a match with my public key.
I would simplify this approach and just pin against each backend server and the Mobile Certificate Pinning Generator tool can help you with extracting the pins for multiple domains/ips, while generating the proper pinning configurations to use in Android and iOS.
Let's imagine that your mobile app needs to communicate with example.com, httpbin.org and google.com.
Just configure the domains you want to pin against in the Config tab:
After you submit the form you will land in the Results tab, and then if you navigate to the Android tab:
and on the iOS tab:
Below each configuration you have instructions on how to add each configuration into your mobile project and even a link to a step by step tutorial on Github that uses the Pin Test App for Android and iOS.
The advantage of pinning against each backend certificate is that you can now have each one auto generating their certificates with LetsEncrypt that keeps the public key between renewals, thus without the need to change the pins in the mobile app, unless a compromise of the private keys occurs. You can replace LetsEncrypt it whatever allows you to rotate certificates while maintaining the same public key.
Related
My app needs to consume webservice, and I would like to authenticate app against server with certificate.
However, embedding keystore with signed key into package is considered bad practice (and explicitly warned against: https://developer.android.com/google/play/asi) as it can be extracted an decrypted.
I can generate private key with android provided keystore, and use it - but I still need it to be signed in order to verify it on server side.
In ideal case there shall be certificate chain, with trusted root authority and containing metadata of signed app package I could verify on server side.
Or is it somehow possible to use package signature in certificate generation process to prove that self signed certificate originated form untampered package?
A bad actor ("Trudy") can flash any custom Android ROM, including a ROM that removes package certificate validation during APK installation. Thus, any query that your app makes to its Android host OS is essentially a request to Trudy.
So, it might be possible to uncover the installation of a hacked APK with an unadulterated OS. But with an adulterated OS, all bets are off.
I think any solution for the client to self-validate would necessitate an authoritative validation of the host OS. Not easy.
Is it somehow possible to use package signature in certificate
generation process to prove that self signed certificate originated
form untampered package?
(1) Do you mean that each client generates a different self-signed certificate after installation and somehow cross-references this against the apk package signature in order to authenticate? Then no, this will not stop Trudy. (And it would not be useful in authentication, either.)
(2) Do you mean that the client has a universal private key embedded in the APK and that metadata on the package certificate can be used to verify against the private key? I do not know offhand if there are any available fields in the package certificate metadata in which to add this information. This is an interesting approach that you suggest. However, since Trudy might be the OS, theoretically Trudy could mock any result it wishes. I do not see this stopping Trudy.
This (SO) post by a developer for Proguard offers 5 options for handling secrets in your Android app. He notes:
Intrinsically, nothing on the client-side is unbreakable, but you can
certainly raise the bar.
I understand that you want user authentication of Webservice (API) using user's private key, and signed token would be verified at the server with user's certificate or public key already registered there at the time of user registration.
Are you looking to use external USB Token to store private key securely or you want to store the private key to be stored in Android device memory?
We have worked on such requirements on desktop (sample at https://web.signer.digital/Home) but not on Android. I know we can connect USB Cryptographic Token (like ePass2003 or others) to Android device with OTG USB and drivers for Android for some devices are available to access from Android but didn't worked on it actually. But this can be direction for you to look into.
A mobile application is public app, because an end user could possibly view and modify the app code. So, your application can't keep secrets from malicious users. That includes client certificates, which are required for mutual TLS (mTLS).
It is not safe to store any client certificate on the Android device for machine to machine (don't mix machine to machine with user to machine) mTLS authentication. It can be exported and then used on other devices by any user.
Operation systems (Android as well, some browsers, e.g. Firefox may have own) provides certificate storage, where CA certificates are stored and where client certificates can be stored.
It is a good idea to store user (issued for the user, not for the app/machine) client certificates there. Then mobile app should get an option to select which user client certs should be used for mTLS. But I want to authenticate my application without any user interaction. won't be possible. There must be at least initial user interaction: user client cert import to client storage, mTLS app configuration. That's IMHO the best and secure mTLS implementation. Real world example: Rocket Chat app.
You are asking for reverse TLS based verification ? Usually, clients verify web services because client's host OS trusts a particular CA certificate that is also used to generate the web-service TLS certificates.
The difference is:
web servers are protected and controlled by authors. Client devices are not. So, having a private key in app to sign the data sent to server is futile.
Still, You can explore Google's Licensing implementation with server side verification to enforce that a legitimately installed and licensed app can contact the server.
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.
I'm building a app that signs some data and is supposed to send the 3-tuple data, signature, certificate-chain to a server. The server then checks the signature against the certificate and checks whether the provided certificate is signed by a fixed CA. I already installed the certificate on my android device and now I want to use it in my app.
My problem is: how can I retrieve the certificate chain (no private key) to send it to the server for verification? I looked at the keychain stuff but I don't see a way.
Thanks for your help
i need to implement security measure for my android app which currently runs with http
I was reading on the ssl and found that we need root and intermediate certificates,
if i am not purchasing the certificate from outside how can i get the root and intermediate certificates ..
thanks
Do you mean you will be using a self signed or self issued certificate on the public facing server? (Neither of which I would recommend)
If you really want to try and do that then:
a self signed certificate sign's itself (it is its own CA)
a self issued certificate on windows for example use the mmc certificates tool, you can download the public part of any certificates in the trust chain (but then you'd need to get them on to a device for them to be useful and something owners of the devices would not like since I would bet you do not have proper key management in place).
A potentially better way would be to use a properly issued certificate for your production site which would allow you to validate the hostname and trust chain correctly.
But for your local dev builds have it ignore these two checks (I would also have it put up a Toast if it was running in this mode so you don't issue this by mistake).
I am looking for different ways to authenticate client like android, iphone, windows and blackberry app and which one is better and why
As per my research I know of 2 way to authenticate client
1. Private key embedded inside smartphone app which will be used to sign the message : Problem with this is its easy for hacker to get access to private key
2. Client certificate
Are there other ways to authenticate these smartphone app and which one is most secured?
Both of the options you list here are really the same. A client certificate is really just the public key part of a private/public keypair that is signed by some entity along with some identification information.
The best way to authenticate the client is to use mutually authenticated SSL. You can use self-signed certificates here so you don't need to buy any from a CA, assuming you control all of the clients that you want to allow access and you control the servers they are going to talk to. This will ensure that your clients only receives data from your legitimate server (configure the SSL system for your application to only accept the self-signed certificate that your server is using) and your server only accepts data from your authorized clients (configure your server to only access the self-signed certificates deployed in your app as a resource for client authentication). There is a complete step-by-step rundown on how to do this for Android in Application Security for the Android Platform, published by O'Reilly.
You are correct in that you need to embed some secret information (a private key) in your client application and an attacker will be able to compromise it. The best solution you have within Android right now is to put the certificate and private key in a Keystore that you include in your application APK as a resource and have your application access the Keystore when it needs to use the key. That means your application will need to have the password to the Keystore. So, how you protect that password becomes important. You can obfuscate your code to make it harder for an attacker to determine that password, but that will only slow down a determine attacker who is reverse engineering your application. However, short of requiring the user of the device to type that password in every time they want to use your application, that's the best you can do. If your client app that is running on the device needs access to something that it stores, a person with access to that device will be able to access it as well. All you can do it make it more difficult.