Java – ECDH uses the Android KeyStore to generate private keys

ECDH uses the Android KeyStore to generate private keys… here is a solution to the problem.

ECDH uses the Android KeyStore to generate private keys

I’m trying to implement ECDH in Android using private generated by the Android KeyStore Provider.

public byte[] ecdh(PublicKey otherPubKey) throws Exception {

try {
        ECPublicKey ecPubKey = (ECPublicKey) otherPubKey;
        KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
        PrivateKey pk = (PrivateKey) LoadPrivateKey("Backend");
        keyAgreement.init(pk);
        keyAgreement.doPhase(ecPubKey, true);

return (keyAgreement.generateSecret());
    }
    catch (Exception e)
    {
        Log.e("failure", e.toString());
        return null;
    }
}

However, this exception is caught in keyAgreement.init(pk):

E/failure: java.security.InvalidKeyException: cannot identify EC private key: java.security.InvalidKeyException: no encoding for EC private key

I used before successfully generating a “backend” public/private key pair:

public void GenerateNewKeyPair(String alias)
        throws Exception {

if (!keyStore.containsAlias(alias)) {
         use the Android keystore
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
        keyGen.initialize(
                new KeyGenParameterSpec.Builder(
                        alias,
                        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
                        .setDigests(KeyProperties.DIGEST_SHA256,
                                KeyProperties.DIGEST_SHA384,
                                KeyProperties.DIGEST_SHA512)
                        .setRandomizedEncryptionRequired(true)
                        .build());
         generates the keypair
        KeyPair keyPair = keyGen.generateKeyPair();
    }

Then I load the private key using the following method:

public PrivateKey LoadPrivateKey(String alias) throws Exception {
    PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
    return key;
}

Anyone know what’s going on and can help me understand how to fix it? Thanks!

Solution

As far as I know through research and trial and error, this feature is not currently supported.

I believe the best thing you can do is sign the public key of the EC key pair you generated outside of AndroidKeyStore with an EC key pair stored in AndroidKeyStore. You can then use the signing key certificate to send this signing public key to another party, generate a shared key (outside of AndroidKeyStore), and then store the KDF-derived SecretKey on the generated key. I recommend using this non-AndroidKeyStore generated key pair once (and therefore only for deriving secrets) and repeating this process to regenerate keys as deemed necessary.

EDIT: When I say “store SecretKey,” I mean in the AndroidKeyStore. In this case, the key will initially be in the so-called “normal world”, but this is the best you can do at the moment.

Related Problems and Solutions