I have a piece of code in my application that retrieves the application certificate at runtime and uses it as a key to encrypt some confidential information.
Is it possible for some attacker to get that certificate byte[] by decompiling my code or is that certificate only visible to my application?
Here is how I get the certificate:
PackageManager pm = this.getPackageManager();
String packageName = this.getPackageName();
int field = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo;
packageInfo = pm.getPackageInfo(packageName, field);
Signature[] signatures = packageInfo.signatures;
// and here we have the DER encoded X.509 certificate
byte[] certificate = signatures[0].toByteArray()
PackageInfo signature is public key,
It is not private key. And this key is accessible to any app installed on your phone.
The application certificate is public; the private key used to sign the app never leaves your possession (unless you give it away). The private key cannot be reconstructed from the public key. The only way they would be able to decrypt your confidential info is via some sort of brute force attempt, against which there is ultimately no defense. Fortunately for you, the expense of running such a brute force attempt is so large that nobody in their right mind would attempt it unless they thought there were nuclear secrets etc. inside.
Related
We used Anroid Keystore to store some confidential data and set up a password for Keystore. This passwords are used in conjunction with the KeyStore class in the load, getKey and setKeyEntry methods.
The Keystore itself is encrypted and app can only view and query its own data so we can say that data are somewhat secure inside Keystore but how we can secure the password that associated with keystore account? I found many example online and most of them having hardcoded password in code or use null parameter.
Please see in below example. I want to know what is the best approach to secure hardcoded password?
Want to find a safe way in android device itself to store this hardcoded password. Assume that moving it to external place like database, service call etc. options are NOT available.
Context context;
KeyStore ks;
KeyStore.PasswordProtection prot;
static readonly object fileLock = new object ();
const string FileName = "Xamarin.Social.Accounts";
static readonly char[] Password = "3295043EA18CA264B2C40E0B72051DEF2D07AD2B4593F43DDDE1515A7EC32617".ToCharArray ();
public AndroidAccountStore (Context context)
{
this.context = context;
ks = KeyStore.GetInstance (KeyStore.DefaultType);
**prot = new KeyStore.PasswordProtection (Password);**
try {
lock (fileLock) {
using (var s = context.OpenFileInput (FileName)) {
ks.Load (s, Password);
}
}
}
catch (FileNotFoundException) {
//ks.Load (null, Password);
LoadEmptyKeyStore (Password);
}
}
Assume that moving it to external place like database, service call etc. is NOT possible
You want to securely store sensitive information on the local user's machine.
The only way to do that is encrypting it. The most popular encryption algorithm is AES, and luckily Microsoft included an implementation of it in C#.
However, encryption uses a secret key to encrypt/decrypt the data, so we're basically moving the problem back - now we need to store that encryption key securely.
You could hard-code that key in the app, but a dedicated attacker could still get it and decrypt the password.
Instead, get that password from the user. Ask them to provide a password, hash it (using e.g. SHA256) and use the hash as the key for the encryption.
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..
I am trying to write an application that enables users to exchange their public (e.g. RSA) keys via Bluetooth/NFC. To store and later retrieve the keys, I would like to use Android's KeyChain API.
I have read the online API documentation and some tutorials/examples so far. They all involve importing a keychain from a PKCS12 file which contains a private key and the corresponding public key and certificate. Once this file is imported, an alias-String is returned for subsequent reference. So far, so good.
However, what I want to do is receive someone's public key, store it, get back an alias, store that alias somewhere (e.g. contacts data) and use it to retrieve back the public key when the user wants to encrypt a message to that person. Is that possible? I have very little experience with Public-key cryptography and have the feeling that I have misunderstood the whole purpose of the KeyChain API.
Any help would be much apprechiated! Thanks.
The Android KeyChain API is designed to store SSL certificates and keys: your own keys when installing a PKCS#12 or a trusted root certification authorities certificates. This keys and certificate are then available to all applications.
It depends on the purpose of the application you are developing but you maybe should consider using a KeyStore dedicated to your application to store the keys you received instead of the KeyChain API.
Another limitation of both API (KeyStore and KeyChain) is taht it is not possible to directly store public keys. You need to have a certificate. I suggest you to embedded a self-signed certificate in your application and use this certificate to sign "dummy" certificates containing the public keys the application will receive.
A simplified code snippet with the bouncycastle library to store a public RSA key:
public void storeRSAPublicKey(String alias, BigInteger modulus, BigInteger exponent)
{
/** Load the key to generate the certificate */
KeyStore ks = getApplicationKeyStore();
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)ks.getEntry(MY_PRIVATE_KEY, null);
X509Certificate issuerCert = (X509Certificate)entry.getCertificate();
PrivateKey skey = entry.getPrivateKey();
/** Prepare the certificate template */
RSAKeyParameters params = new RSAKeyParameters(false, modulus, exponent);
SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfoFactory.SubjectPublicKeyInfo(params);
X500Name issuer = new X500Name(issuerCert.getIssuerX500Principal().getName());
X500Name subject = new X500Name("CN=alias");
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuer, randomSeriaNumber(), new Date(), dateIn20years(), subject, pkInfo);
/** Generate the certificate */
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(skey);
X509CertificateHolder holder = builder.build(signer);
/** Store the certificate in the KeyStore */
JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
X509Certificate cert = conv.getCertificate(holder);
ks.setCertificateEntry(alias, cert);
pushKeyStoreToPersistentStorage(ks);
Now you can get the certificate with ks.getCertificateEntry(alias);
Note that I omitted some non fundamental code like serial number generation and not-after date computation.
You can create the initial KeyStore with keytool and add it to you app as a resource.
I am building an Android application that communicates with an online webservice. I plan on releasing the application's source code on GitHub. For my production version, which will utilize my personal webservice I want to allow only my digitally signed apk to connect.
Is is possible to request the APK's keystore and confirm the username/password from that keystore?
If this is not possible how else can I produce this functionality?
Edit:
I have read into the class Certificate It looks like I might be able to user public/private keys to confirm an identity. But I am still unsure of an implementation
I use this --
static public String getPackageFingerPrint( Context ctx ) {
PackageManager pm = ctx.getPackageManager();
String packageName = ctx.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (NameNotFoundException e) {
return "";
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch (CertificateException e) {
return "";
}
X509Certificate c = null;
try {
c = (X509Certificate) cf.generateCertificate(input);
} catch (CertificateException e) {
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getPublicKey().getEncoded());
StringBuffer hexString = new StringBuffer();
for (int i=0;i<publicKey.length;i++) {
String appendString = Integer.toHexString(0xFF & publicKey[i]);
if(appendString.length()==1)hexString.append("0");
hexString.append(appendString);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e1) {
return "";
}
}
The problem I see with your approach is that anyone could determine the package fingerprint or your package and send it to your web-service. A better possibility would be to use a challenge-response mechanism: Your web-service sends you a unique session-token, which your app encrypts or digests using a shared algorithm, and then sends this encrypted token back to your service for verification. Of course, you wouldn't want to publish that algorithm to github.
You cannot restrict a web service to apks signed by a specific key.
The signature of your apk gets validated by the Android OS, and is not directly shared with web services the device connects to. Even if you read your signature from the keystore and send it along with requests, an attacker could just send the same signature (e.g. the same byte stream) without having access to your private key. He would just need to grab your signed apk, read the bytes from the keystore (or listen to a legitimate request) and spoil the data.
You would need to sign individual requests to have a level of security. But if you keep a private key in release versions (the key not distributed on gitHub), and sign requests using that key you are not safe as the private key is distributed as part of your apk and thus can get extracted easily.
In any way your API could get accessed by other apks.
However, there might be another way to restrict your API, for example by using license tokens, etc. In that case you would probably not care if the user builds the apk by himself, as long as he has a valid license token. If a license token is exploited and distributed, you could react to a high amount of traffic on that license token and for example block it from further requests. As I don't use Google Play I'm not sure in how far they can get validated from your server, but maybe the application licensing portal is a good starting point to search for suitable tokens.
I have developed an android application which need secure communication with the server. I get exception about untrusted server because my server's certificate is not part of android's cert list.
I make use of following KeyChain APIs (available from ICS onwards) to prompt user for the certificate installation, after which the communication works seamlessly.
BufferedInputStream bis = new BufferedInputStream(getAssets().open(
PKCS12_FILENAME));
byte[] keychain = new byte[bis.available()];
bis.read(keychain);
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychain);
installIntent.putExtra(KeyChain.EXTRA_NAME, DEFAULT_ALIAS);
startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
I am using the above code on the application startup and it prompts even when the certificate is already present. With regards to this, I have following two questions,
Programmatically how do I identify whether a particular certificate is already present or not? So that I prompt only when it is not already present.
Is there any event which occur during application installation, that I should use to prompt user for the certificate installation?
You can use something like this to enumerate trusted certificates. If the alias starts with 'user' it is user-installed. This is not part of the public API though, so it might break on future versions. More details here: http://nelenkov.blogspot.com/2011/12/ics-trust-store-implementation.html
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
ks.load(null, null);
Enumeration aliases = ks.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
X09Certificate cert = (X509Certificate)
ks.getCertificate(alias);
Log.d(TAG, "Subject DN: " +
cert.getSubjectDN().getName());
Log.d(TAG, "Issuer DN: " +
cert.getIssuerDN().getName());
}
Since seems that it is not possible to retrieve a list of installed certificates programmatically, you can't say when your certificate is already installed. IMHO you should install it if you get the exception about untrusted server.
Also there is not an event occouring during application installation (also who should catch such event?)