Issues with SpongyCastle dependency


#1

Hi there,

we provide an SDK that uses the Android Hardware backed KeyStore and which doesn’t use Expo itself, but a customer of us uses our SDK in combination with Expo.

Expo uses SpongyCastle as a dependency and registers it as the first Security provider. SC itself claims to support all types of RSA-/EC-keys but then fails with the ones of the HBKS. Now for this case, Google introduced the (undocumented) AndroidKeystoreBCWorkaround provider which you can specify explicitly on a crypto operation, but this only helps for Android >= 5.0.

Is there any reason why you use insertProviderAt instead of addProvider to register SC? IMHO this would be the only way to solve all problems.

I’m currently trying to work around the issue but don’t know at which point you register SC. If you gave me further information, this could also help much.

Thanks,

Max


#2

Hi @maxwahler – we use insertProviderAt in order to be certain that the SpongyCastle provider is used in preference for any device-specific security provider for our SecureStore module and other security-related logic.

We used to insert this provider lazily but had issues with nondeterministic behavior, so we moved to inserting the provider when the Exponent module is constructed, which happens when a React Native app activity is created.

I hope this helps! Let me know if you have any other questions.


#3

Hi @esamelson, thank you very much for clarification!

Is there an advice you can give me, so the AndroidKeyStore works but does not break anything (or as little as possible) from Exponent?

By the way, would it possible for you to explicitly use the SpongyCastle provider on cryptographic operations or would you run into compatibility issues too? In my opinion, SC breaks basic Android functionality when used on first position.


#4

Hey @maxwahler - I’m unclear on what exactly the problem is that you’re running into. Could you clarify what the problem is and what you’re concerned about breaking?

AFAIK we haven’t had any issues with SpongyCastle being registered as the first provider, and it’s also what they recommend on their own docs. However, I’d be interested to hear if you have some examples of how it breaks basic Android functionality when used as the first provider.


#5

Hi @esamelson, the issue appears when you want to use the hardware backed keystore, AndroidKeyStore. The following example illustrates the problem:

// register BouncyCastle/SpongyCastle
Security.insertProviderAt(new BouncyCastleProvider(), 1);

// generate a EC secp256r1 key pair using the AndroidKeyStore
KeyPairGenerator generator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");

AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(
        "test-" + System.currentTimeMillis(),
        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
        .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_NONE)
        .build();

generator.initialize(spec);

KeyPair keyPair = generator.generateKeyPair();

// prepare test data
byte[] data = new byte[20];
new Random().nextBytes(data);

// try to sign the data
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(keyPair.getPrivate());   // <-- it fails here
sig.update(data);
byte[] signature = sig.sign();
System.out.println("signature is "+Arrays.toString(signature));

This leads to: java.security.InvalidKeyException: cannot identify EC private key: java.security.InvalidKeyException: no encoding for EC private key

One way to work around that issue is to specify the crypto provider like:

Signature sig = Signature.getInstance("SHA256withECDSA", "AndroidKeyStoreBCWorkaround");

Unfortunately, this provider is - as fas as I researched - not documented. It’s in the compatibility document for Android 9 but may be subject to change in future versions. Also, the problem remains for Android < 6.0 because this provider does not exist there.

The current workaround we have in place moves the SC provider to the last position in provider list before initializing Signature and put it back in place afterwards, hoping there’s no parallel execution which requires SC during that.


#6

Hi @maxwahler – ugh, that’s a frustrating Android platform issue :disappointed: We only use the SC provider at a couple of specific times: (1) when the app is loading and any OTA updates are being fetched, (2) when fetching OTA updates using the Updates API, and (3) when any of the methods in the SecureStore module are being called. As long as your users aren’t trying to use the Updates/SecureStore modules when your library has moved the SC provider to the last position, your workaround should be just fine.

Hope that helps!


#7

Hi @esamelson, yep, those old Java APIs were really designed with a lot of good will in mind, to say the least.

We implemented the workaround now and tested it a bit with the sample application our customer created and so far, it looks quite good. We also gave them the advices you mentioned above hoping that this helps them to avoid any trouble.

I really hope that there’ll be a sensible solution to this issue in future Android versions. Both your and my point of view are reasonable but Android really lacks a proper mechanism to deal with crypto providers.