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.
Related
I am having a slight problem with understanding the concept of RSA signing and verifying.
The problem is that I can create key pair (public and private key) which is perfectly fine.
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(2048, random);
KeyPair pair = keyGen.generateKeyPair();
myPrivateKey = pair.getPrivate();
myPublicKey = pair.getPublic();
Signing and verifying are as follows:
//Singing with private key
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(javaPrivateKey);
//Verifying with public key
Signature s = Signature.getInstance("SHA1withRSA");
s.initVerify(javaPublicKey);
When I print myPrivateKey and myPublicKey, I see modulus(n) and public exponent(e) are the same for public and private key.
I have converted public and private keys to base64 and hex and I get the different values which is perfectly fine. However, I cannot sign the message with base64 or hex. I can sign it only with what I get from this:
myPrivateKey = pair.getPrivate();
I am aware of that verifying needs to be done with the public key which is visible to everyone. When the message is verified by the receiver, does the receiver use modulus and exponent only? What part of the public key needs to be shared by the sender? Modulus and exponent or Base64 or hex value of the key?
Yes, the receiver uses modulus and exponent only; there are no other components that are required mathematically speaking to verify a signature for RSA.
The mathematical operations are performed using large numbers (BigInteger values commonly when RSA is implemented in software). To perform any kind of calculation the implementation must regenerate these numbers. How these numbers are transmitted doesn't matter to the algorithm.
Commonly RSA public keys are encoded using something based on PKCS#1, which specifies the public key format using ASN.1 (which defines the structure) and BER/DER (which defines the encoding of that structure). Of course, different protocols may use different encodings of public keys. PGP for instance uses an entirely different "package format" to encode keys.
Java however returns a SubjectPublicKeyInfo structure which is defines in the X.509 (certificate and CRL) specifications; besides the modulus and exponent is also contains an algorithm identifier to show that it is an RSA public key. So this structure can also be used to distribute other kind of key values. It can be retrieved by calling getEncoded() on the RSAPublicKey instance - assuming that this instance is compatible with the Oracle provided one - they generally are. Android's implementations certainly should be compatible with this structure. Note that the SubjectPublicKeyInfo structure contains the PKCS#1 public key structure inside of it.
To reverse you need an KeyFactory.getInstance("RSA") and derive the key using X509EncodedKeySpec, initialized with the given byte array.
If you need textual strings instead of binary then you can convert the bytes returned by getEncoded() into base 64 and hexadecimals. Of course, in that case, you need to reverse the encoding (i.e. decode) the result before being able to decode the bytes themselves.
It is also possible to encode the modulus and public exponent yourself. You can retrieve them using RSAPublicKey.getModulus() and RSAPublicKey.getPublicExponent(). To reverse them back into an RSAPublicKey you can use KeyFactory.getInstance("RSA") and RSAPublicKeySpec. That way you can for instance just create a string "(<modulus>, <exp>)" and use that to distribute the key. Generally you would want to keep to predefined standards though.
Not covered in this answer is the fact that to verify with a public key you first need to establish trust in the public key. If you cannot trust the public key then you don't know who created the public key. In that case you can also not trust the result of your verification operation; the signature may have been created with a key pair of an adversary. Delving into Public Key Infrastructures (PKI / PKIX) is a bit too much for this answer though.
Likewise: SHA-1 is not considered secure anymore, especially for signature generation / verification. You would want to use at least SHA256withRSA or - slightly more advanced and somewhat more secure - an RSA scheme that uses PSS. 2048 bits is ever so slightly too small for comfort; using a 4096 bit key is advisable if your scheme allows it.
I have access token from the server after authentication lets say "uyhjjfjfgg567f8fhjkkf" now I want to save it in the device securely. I looked in Keystore and Keychain in android developer sites. I dont clearly understand how it works and how we should retrieve the token from the keystore.
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.build());
KeyPair kp = kpg.generateKeyPair();
/*
* Load the Android KeyStore instance using the the
* "AndroidKeyStore" provider to list out what entries are
* currently stored.
*/
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
You don't need to save the access token, since it has short life anyway. Keeping it in memory is good enough.
You do need to keep the refresh token, and you have a few options for that:
In a file
Either directly in a file in the internal storage
or using SharedPreferences
or in a Database
Using the AccountManager
Consider using the StoredCredential. For the flow itself, I recommend you to use Google AppAuth library.
Of course, you can also encrypt the key using a cipher:
private static byte[] encrypt(byte[] key, byte[] text) throws GeneralSecurityException {
final SecretKeySpec skeySpec = new SecretKeySpec(key, KEY_ALGORITHM);
final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, sInitVectorSpec);
return cipher.doFinal(text);
}
And the key can be stored in the KeyStore.
We use a custom SharedPreference instance that encrypts the keys and values when adding, and decrypts when requesting.
SecurePreferences preferences = ...
preferences.edit().putString( "key", "value" ).apply(); // key and value are encrypted automatically
String value = preferences.getString( "key", null ); // key and value are decrypted automatically
I would only recommend using SharedPreferences if the values are encrypted, because even though the xml file is only available to the app, it can be accessed on rooted devices.
If you already using a SqlLiteDB, I would probably use that. If not, it's bit heavy for just saving a token.
EDIT:
An oauth token is completely unrelated to the key and keystore used to sign the app.
The oauth token is a token provided by the server after validating the user's credentials, within the app.
The keystore contains 1 or more certificates that is used to digitally sign the app. This is to prevent someone else from uploading an app that has the same package name as yours and replacing it.
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 am currently working on android application which is based on Client-server architecture. For data security, I am using Public-Private key pair for data encryption and signing. I am using AndroidKeyStore for storing key pair. Below is the code to generate key pair:
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(
mContext)
.setAlias(mPrivateKeyAlias)
.setSubject(new X500Principal("CN=" + mPrivateKeyAlias))
.setSerialNumber(
BigInteger.valueOf(System.currentTimeMillis()))
.setStartDate(start.getTime())
.setEndDate(end.getTime()).setKeySize(2048).build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(
"RSA",
"AndroidKeyStore");
kpGenerator.initialize(spec);
// Key Pair will be saved in AndroidKeyStore
KeyPair pair = kpGenerator.generateKeyPair();
After executing this code, Keystore releated files (CERT and PKEY files) will be generated at '/data/misc/keystore/user_0/' directory.
I am encrypting application sensitive data like auth-token and saving it to Shared Pref for security reasons.
But now when user changes device password or pin, keystore files are getting deleted as Masterkey used for keystore encryption is generated using device credentials.
Now to fix this issue, I tried to keep Public-Private key pair in RAM and when password gets changed. From onPasswordChanged(Context context, Intent intent) method of DeviceAdminReceiver, I am executing below code :
KeyStore keyStore = KeyStore
.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setKeyEntry(mPrivateKeyAlias, mPrivateKey.getPrivateKey(),
null, new Certificate[] { mPrivateKey.getCertificate() });
But, after this code only CERT file gets created at '/data/misc/keystore/user_0/' directory and while decryption using private key, giving some invalid signature error.
Also, I have shared my public key with server, encrypted data with private key, so creating new key pair would not be better solution.
So, how I can retain my public private key pair after device password change ? If there is no work around, what is the exact use of AndroidKeyStore? Where can I use it ?
This issue has been fixed by Google in Android 5.0 (Lollipop) release. But, for previous versions of Android, you will have to live with this issue. :(
no key distribution, public and private keys will be known by users (random key generator will not be used). I have to encrypt hashed message with private key in order to provide signature
message will only 10-20 characters, so system can be as simple as it is possible
For generating a digital signature, you don't need to encrypt the hash. Signing is a separate crypto primitive; the hash is not encrypted raw anyway, there's some padding. That said, the code is:
Signature Signer = Signature.getInstance("SHA1withRSA");
Signer.initSign(MyKey, new SecureRandom()); //Where do you get the key?
byte []Message = MyMessage(); //Initialize somehow
Signer.update(Message, 0, Message.length);
byte [] Signature = Sign.sign();
Okay, back up and tell us what you want. Are you trying to get privacy by protecting the contents of the message, or guarantee authenticity by showing that the message really came from the originator?
If you're looking for privacy, RSA isn't the way to go: use RSA to generate a private/public pair, and then use them to excahnge keys -- or exchange keys out of band. Use a streaming algorithm like AES to encrypt the message.
If you just want signature to show the message was originated by who you think it was, then have a look at the Wiki article on digital signature -- it's reasonably straightforward.