Would appreciate any hint what I am missing in below code?
void addKeyToKeyStore() {
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias("MY_ALIAS")
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=MY_ALIAS"))
.build());
}
void ListTheKeys() {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
final Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
Log.d(TAG, "Key Alias = "+aliases.nextElement());
}
String tempstr= "Entry for MY_ALIAS is " + (ks.isKeyEntry(key)? "exist" : "not exist");
Log.d(TAG, tempstr)
}
If same app calls addKeyToKeyStore() and then ListTheKeys() - the entry is exist. It also works if app restarted or phone rebooted.
However if app1 calls addKeyToKeyStore() but app2 then calls ListTheKeys() - no entry that was added.
Have tried set same sharedUserID in AndroidManifest(s) - no effect.
I need to be able to access KeyStore from different apps.
Thanks in advance!
Never mind. I have figured out - a bit messed up with sharedUserID. If the sharedUserID is declared properly - everything works.
Issue can be closed.
Related
i am trying to run some spongycastle code in android:
try {
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
ECGenParameterSpec spec = new ECGenParameterSpec("P-256");
KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "SC");
generator.initialize(spec, new SecureRandom());
KeyPair keyPair = generator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
String publicKeyStr = publicKey.getW().getAffineX().toString() + ":" + publicKey.getW().getAffineY().toString();
Log.d(TAG, publicKeyStr);
Calendar c = Calendar.getInstance();
Date d0 = c.getTime();
c.add(Calendar.DATE, 1);
Date expiry = c.getTime();
String token = Jwts.builder()
.setIssuedAt(d0)
.setSubject("00000000-0000-0000-0000-000000000001")
.setExpiration(expiry)
.signWith(privateKey, SignatureAlgorithm.ES256).compact();
Log.d(TAG, token);
} catch (Exception e) {
Log.d(TAG, e.toString());
}
in API version 21+, it works as expected.
in API version 18, it works in debug. in release, it fails in function generator.generateKeyPair() with java.lang.IllegalArgumentException: Invalid point.
in API version 16, it fails in function Jwts.builder().signWith() with io.jsonwebtoken.security.SignatureException: Invalid Elliptic Curve PrivateKey. can't recognise key type in ECDSA based signer.
any idea what i am doing wrong?
edit: i have apparently fixed the problem by switching from spongycastle to bouncycastle.
in my build.gradle i changed this:
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
to this:
implementation 'org.bouncycastle:bcpkix-jdk15on:1.60'
and in my code i got rid of this line:
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
and added these lines:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
Security.insertProviderAt(new BouncyCastleProvider(), 1);
and i changed the provider from "SC" to "BC".
and now my code works as i would like, for all versions of the android API.
my concern now is the comment at this link...
Spongy Castle: is it obsolete?
...by the author of spongycastle:
Why might Spongy Castle not be obsolete?
...
even on post-Android 3.0 devices, device manufacturers are not above
carelessly bundling libraries, it's possible that Bouncy Castle may
still be bundled on some obscure devices.
what does that mean exactly? could it cause my code above to malfunction on certain devices?
use bouncy castle:
dependencies {
// https://mvnrepository.com/artifact/org.bouncycastle
implementation "org.bouncycastle:bcprov-jdk15on:1.60"
implementation "org.bouncycastle:bcpkix-jdk15on:1.60"
}
Which method stores Private and Public keys in AndroidKeyStore?
I have implemented below code to initialise keystore and generate private and public keys.
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyPair keyPair;
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 50);
KeyPairGenerator generator= KeyPairGenerator.getInstance("RSA","AndroidKeyStore");
KeyPairGeneratorSpec keyPairGeneratorSpec = new KeyPairGeneratorSpec.Builder(context).
setAlias("alias").
setSubject(new X500Principal("O=Authority")).
setSerialNumber(BigInteger.ONE).
setStartDate(start.getTime()).
setEndDate(end.getTime()).build();
if (generator != null) {
generator.initialize(keyPairGeneratorSpec);
}
keyPair = generator.generateKeyPair();
To your question, this line
generator.generateKeyPair(); implement the keypair generating and storing processs.
You might be confused when seeing codes in java.security.KeyPairGenerator like this
public KeyPair generateKeyPair() {
// ...
return null;
}
But actually since KeyPairGenerator is an abstract class, the 'true' class here using is
java.security.KeyPairGenerator$Delegate, which delegates the generateKeyPair like this
You can also check the difference of alias list in "AndroidKeyStore" before and after this process.
I would like to share the gist about how to view alias list of any keystore. Hope it might help you testing: https://gist.github.com/davidkhala/4aa1d6b44f287699aeac028786633c7a
When you generate a key pair with AndroidKeyStore, it is already automatically stored. You can retrieve it when you need it using the alias you specified ("alias" in your example).
Note, however, that when you get the PrivateKey, you do not actually get the private key secrets. Those stay in secure hardware and never leave it, so they can't leak. But you get a PrivateKey object which you can use just as though you had the secrets; your requests to encrypt or sign are sent to the secure hardware, which uses the secrets to perform the operation and hands the result back to you.
In my app we are using RSA key, that the app generate (using android key store) on the first launch. From unknown reason, the app failed to retrieved the key from the key store on some of the devices. I've checked the logs, and I could not find a correlation between this bug to a specific OS version or to a specific device model. Also, I know for sure the app tried to read it only after the key created. So - my question is that: As far as I know, android key store should be persistent. What can cause such a bug?
Bellow are relevant code samples.
Key generation:
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", keyStore.getProvider());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
KeyGenParameterSpec spec;
spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT| KeyProperties.PURPOSE_SIGN| KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setKeySize(2048)
.build();
generator.initialize(spec);
} else {
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 500);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal("CN="+ subject))
.setSerialNumber(BigInteger.valueOf(new Random().nextInt(Integer.MAX_VALUE)))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setKeySize(2048)
.build();
generator.initialize(spec);
}
return generator.generateKeyPair();
} catch (Exception e) {
logger.warn("Failed to create private key in store", e);
return null;
}
The keystore itself intialized using the following code:
KeyStore androidKeyStore = KeyStore.getInstance("AndroidKeyStore");
androidKeyStore.load(null);
return androidKeyStore;
And we use the following code to retrieve the key, the bug is that on some devices the keystore returns null:
try {
Key key = keyStore.getKey(alias, null);
if (key == null){
logger.warn("Key not found in key store");
return null;
}
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keyStore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
} else {
logger.warn("Key found, but not from current type. type found: " + key.getClass().getSimpleName());
}
return null;
}catch (Exception e){
logger.warn("Failed to get private key in store", e);
return null;
}
Thanks,
Omer
Just in case someone will run into the same problem:
I've found out that Azure Active Directory library for android suffer from similar issue, and from reading the code I've saw they linked to two issues that are similar to this problem and to another issue we have. Because of that I am planing to use keystore based on p12 file, stored in the app private storage.
Is it that you appear to lose your keys immediately after generation or some time later they are lost? Take a look at this question AndroidKeyStore getEntry is consistently failing after certain point which links to this great article: http://doridori.github.io/android-security-the-forgetful-keystore/
The moral of this story is if you use the AndroidKeyStore be prepared to lose your keys under certain circumstances!
In my android app, I receive an API token that I get from an http request
I did some research, and, If it seems the best way to store this key, is with the Android Keystore Provider.
I had a look at the documentation https://developer.android.com/training/articles/keystore.html#UsingAndroidKeyStore
But since i'm new with Android and programing in general, I need some help to put that in place.
I'm not sure in which variable I should save the key :
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias(alias)
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=test1"))
.build());
KeyPair kp = kpg.generateKeyPair();
Then how should i retrieve the key, using (I think) the following code :
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
Thank you for your help.
I would assume that your API token is only valid for a certain period of time. Just store it in a SharedPreferences for your application. If it isn't there, or it has expired, just get another one.
Following this blog, I'm using this code to create and store a KeyPair in Android KeyStore:
Context ctx = getApplicationContext();
Calendar notBefore = Calendar.getInstance();
Calendar notAfter = Calendar.getInstance();
notAfter.add(1, Calendar.YEAR);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(ctx).
setAlias(RSA_KEYS_ALIAS).setSubject(
new X500Principal(String.format("CN=%s, OU=%s",
getApplicationName(), ctx.getPackageName()))).
setSerialNumber(BigInteger.ONE).
setStartDate(notBefore.getTime()).setEndDate(notAfter.getTime()).build();
KeyPairGenerator kpGenerator;
try {
kpGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpGenerator.initialize(spec);
kpGenerator.generateKeyPair();
} catch (Exception e) {
showException(e);
}
When I try to retrieve public key from the KeyStore using this code, a NullPointerException with the message chain == null is thrown.
public RSAPublicKey getRSAPublicKey() {
RSAPublicKey result = null;
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.PrivateKeyEntry keyEntry =
(KeyStore.PrivateKeyEntry) keyStore.getEntry(RSA_KEYS_ALIAS, null); // --< exception is thrown here
result = (RSAPublicKey) keyEntry.getCertificate().getPublicKey();
}
} catch (Exception e) {
showException(e);
}
return result;
}
The same goes with the the code to retrieve private key.
Update:
I compared my code with Google BasicAndroidKeyStore sample. The mechanism to generate, store and retrieve the key pair in that sample is virtually the same to what I've implemented. I'm puzzled as to why this code has stopped functioning after a few months of perfectly working.
Any suggestions or hints would be appreciated.
Apparently names in Android KeyStore must be unique amongst all Apps. I had another app which used the same name for its keys. After changing the the common library used by both apps to create and and use keys to include package name in its key names, the problem went away...
In my case, I had multiple calls to obtain the KeyStore at nearly the same time. I had to create a single instance and refer to that if it existed, otherwise KeyStore.getInstance("AndroidKeyStore") returned null and raised an Exception.
To protect against multiple async requests causing a crash, use a stored instance from just one KeyStore.getInstance().
In my case, I was trying to get public key before generating it. (getRSAPublicKey() called before generateKeyPair())