I want to secure a Java REST backend service with two-way SSL, to prevent unauthorized access.
An Android APK needs to be signed to work, is possible to create a "trust" between my REST service and this APK, without using a hardcoded password from APK cert in client code?
The main idea is configurate the server two-way SSL to trust connections only from APK cert.
is possible to create a "trust" between my REST service and this APK, without using a hardcoded password from APK cert in client code?
Not really. Your public key in the APK is just as "hardcoded" as a password. Anyone can go in and use that information to access your REST service.
In order to perform two-way TLS/SSL you need to have a certificate with private key on the client device. The certificate used to sign the APK will result in the public key for the certificate on the device, but not the private key. You would want to avoid placing this private key on the device as it would allow others to sign APKs as you.
Instead of using the APK signing certificate for two-way TLS/SSL, you should consider using a separate certificate possibly generated per device during an initial registration process. This certificate would be installed to the Android KeyStore, and the public key from this certificate would need to be installed on the server hosting the backend REST service. This certificate would then act as client credentials in a similar manner to a username/password pair assigned to the device.
For an example using client certificates on Android see: http://chariotsolutions.com/blog/post/https-with-client-certificates-on/
I think you can try with the answer from here and use the SHA of your key that you used to sign the apk..
It says something like this:
// Add code to print out the key hash
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.e("MY KEY HASH:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
}
} catch (NameNotFoundException e) {
} catch (NoSuchAlgorithmException e) {
}
And you can save on the server side that key.. I think that is what Facebook or Google does for example..
Related
I have this scenario where my App needs to make requests towards a secure server (NON http(s), actually it is about SIP protocol but the question should apply to any non http(s) protocol), and I need be able to tell if the server is considered trusted, based on the System Default Trusted certificates installed in my Android device's keystore.
The problem is that after checking all the APIs Android provides for certificates (like KeyStore, KeyChain, etc) I haven't been able to find a solution.
Seems that each app, even though it can gain access to the System Default keystore of the device, it can only access it's own resources, not global, even when we are talking about TrustedCertificateEntry-type entries.
Is there anything I'm missing here?
Seems like a pretty valid use case for non-https authentication
Best regards,
Antonis
Finally, managed to find a way to do this, so let me share in case this can be useful to others. Turns out Android gives access to system wide trusted certificates. The detail here (and the reason it didn't work for me previously) was the keystore 'type' identifier that I used:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
Which I believe was trying to find actual keys, which off course shouldn't be shared. So after some digging I found that there's a separate type, AndroidCAStore, which did the trick for me. So here's a working code excerpt, that just prints out certificates:
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
ks.load(null);
try {
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
Certificate cert = ks.getCertificate(aliases.nextElement());
Log.e(TAG, "Certificate: " + cert.toString());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
catch (IOException|NoSuchAlgorithmException|KeyStoreException|CertificateException e) {
e.printStackTrace();
}
We have to generate key hash from keystore and register it to facebook developer console.I want to understand the concept of key hash.
What benefits it provides for the server/client ?
We often see Invalid key hash error(i.e the key hash "***" does not match any stored key hashes) .So
How does my app know the correct key hash because I'm not storing it
in any xml or somewhere else?
Any kind of materials or thoughts would be appreciated.
Here Hash code is used to restrict the applications so that only valid applications (which have this particular hash code corresponds to the given certificate) can access facebook services. Because all applications are signed with particular certificates, so all the downloaded applications(say 1000 user downloads it) under same certificate must have the same hashcode and facebook is able to track which certified application used its services
We can easily find hash code of the certificate by the following code:
try {
PackageInfo info = getPackageManager().getPackageInfo(
"com.facebook.samples.hellofacebook",
PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.d("KeyHash:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
}
} catch (NameNotFoundException e) {
} catch (NoSuchAlgorithmException e) {
}
Above we are using SHA(Secure Hash Algorithm) to generate Hash code of the certificate.
SHA (Secure Hash Algorithm ) is message-digest algorithm, which takes an input message of any length and produces a 160-bit output as the message digest.
SHA is called secure because it is computationally infeasible to find a message which corresponds to a given message digest, or to find two different messages which produce the same message digest.
So before making any real request to Facebook server , first hash key
of certificate is compared with the stored hash key(i.e development
hash key or debug hash key) on the server and if they match only then
we can proceed further.
I have been trying to implement facebook share from my android application. I have gone through this documentation.
https://developers.facebook.com/docs/android/share.
I have successfully integrated my development key hashes for once and successfully shared to facebook from my application. The problem is when I am trying to implement the same steps for my another application. I have successfully added my key hashes and linked up my application with facebook but when I hit "POST", it is showing that Key hashes does not match and the key hash that is coming with the error message is showing the previous application's key hash.
I have double checked my key hash using this.
try {
PackageInfo info = getPackageManager().getPackageInfo(
"My Project",
PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.d("KeyHash:", Base64.encodeToString(md.digest(),Base64.DEFAULT));
Toast.makeText(this, Base64.encodeToString(md.digest(), Base64.DEFAULT), Toast.LENGTH_LONG).show();
}
} catch (NameNotFoundException e) {
} catch (NoSuchAlgorithmException e) {
}
Any solution ?
That's because you have 2 Key hashes.
One for Debugging and the second is for release, when you publish your application to Google Play using a custom-made keystore instead of the Android Debug one.
Your scenario sounds like your are running your application from Eclipse/Android Studio and when you do that you are using the Debug keystore key hash which you have probably included in Facebook Developer Console already like in your previous app.
If you are exporting your APK with a different keystore you will have to get its Hash Key for this specific key store, like in the online examples you mentioned above.
I will be happy to know if you are signing your APK with debug keystore or your own. That will make things more simpler to answer.
I projecting REST API service for application where users are competing with each other. Off course, one of the main question is cheat-attack protection.
For example, app sends this request to add a new score for this scheme:
HTTP PUT /score
"value" => 72
"access_token" => XXXXXXX
And then malefactor make same request with modified value:
HTTP PUT /score
"value" => 9000
"access_token" => XXXXXXX
So, this is vulnerability. The solution is in the signature queries in a predetermined pattern:
sig = hashF(params + salt)
where salt is client_secret. But for Android I can decompile app and copy client_secret. I can move client_secret to native code, but it's still be vulnerability - anyone can use my native library or decompile it.
I thought use for this fingerprints of my app certificate. It's can be used inside the code. Like this:
PackageInfo info;
try {
info = getContext().getPackageManager().getPackageInfo("com.mypackage", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
return new String(Base64.encode(md.digest(), 0));
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Suprisely, but with this code I can get access to any app fingerprint. So what I can use as a salt?
You cannot completely protect any secret due to the nature of the platform, as you noted already. It's just all about adding layers of protection. If your competition is casual and there isn't much on the line, what you have already is is fine.
If you are doing something with real money involved or other highly valuable making your application a target, you need to at least do something with a user authentication to protect it. For example, user logs in to app (SSL request to server) -> gets token from server -> uses token to sign requests.
At least then you aren't storing anything in your APK required to post a new score. But this can get really complex to do it right, and it still isn't totally bulletproof. Somebody how really wants to can still engineer how it works and fake it.
I have a rest server and I want only my app to be able to communicate with my REST server. ie if someone puts the url in a browser they wouldn't be able to communicate with my server
At the moment i am thinking of adding my key hash as an extra parameter on my request calls and then storing the key
The hash is not stored on my app but is automatically retrieved using the following method.
public static void getHashes(Activity act) {
PackageInfo info;
try {
info = act.getPackageManager().getPackageInfo("com.my.package.myapp", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md;
md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String something = new String(Base64.encode(md.digest(), 0));
//String something = new String(Base64.encodeBytes(md.digest()));
Log.i("hash key", something);
}
} catch (NameNotFoundException e1) {
Log.e("name not found", e1.toString());
} catch (NoSuchAlgorithmException e) {
Log.e("no such an algorithm", e.toString());
} catch (Exception e) {
Log.e("exception", e.toString());
}
}
I will have this method only called once for example at a certain time so that i can see it on the logcat and save it to my server after i compile to the production apk. this means it won't be outputed again for anyone else to see.
I will store this key on my server and my app will send this key everytime the a request is made. if the key is different then my server will not respond.
Is this method secure? can anyone see a flaw in it?
No, it's not secure.
An attacker can observe a single instance of this hash being sent in a request, and then include it in their own requests and your server won't be able to differentiate. The hash could, for example, be published on the web by the first person to obtain it.
However, this isn't just a problem with your approach: there are no known secure solutions to the general problem, see this answer for a slightly more comprehensive discussion (in the context of WCF rather than Android, but the argument is identical).
No, you need to ask the server for the challenge first and then return the hashed value of the app key, that challenge, content of the message itself (other than signatures) and some random array of bytes that is known on both app and server side. If done this way, should be immune to the network sniffing, replay attacks and also attempts to use legitimate app as a message template generator (valid authentication, other content may be replaced). You may also authenticate the server side same way.
Unfortunately almost nothing can be done against reverse-engineering of your app after the .apk file is in the hands of the attacker. However Google and Amazon app stores offer some protection against getting the .apk for disassembling.