简体   繁体   中英

ECDH key agreement generated Key Pair is always invalid in Android 12

I've written the below code following the official guide for ECDH Key Agreement on Android.

private static final String EC_SPEC = "p-256";
private static final String KEY_PAIR_NAME = "abcdefgh";
private static final String MAC_ALG = "HMACSHA256";
private static final String INFO_TAG = "ECDH p-256 AES-256-GCM-SIV\0";
private static final String KEY_AGREEMENT_ALG = "ECDH";
private static final String KEY_STORE_PROVIDER = "AndroidKeyStore";

public static KeyPair generateKeys() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, ParseException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC, KEY_STORE_PROVIDER);
    keyPairGenerator.initialize(
            new KeyGenParameterSpec.Builder(
                    KEY_PAIR_NAME,
                    KeyProperties.PURPOSE_AGREE_KEY)
                    .setAlgorithmParameterSpec(new ECGenParameterSpec(EC_SPEC))
                    .setUserAuthenticationRequired(false)
                    .setKeyValidityEnd(DateFormat.getDateInstance().parse("Aug 1, 2199"))
                    .build());
    return keyPairGenerator.generateKeyPair();
}

public static byte[] sharedSecret(PrivateKey mine, PublicKey remote) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
    KeyAgreement keyAgreement = KeyAgreement.getInstance(KEY_AGREEMENT_ALG, KEY_STORE_PROVIDER);
    //Line 55 here ↓ where error occurs
    keyAgreement.init(mine);
    keyAgreement.doPhase(remote, true);
    return keyAgreement.generateSecret();
}

But at the time of execution the code throws below error:

W/System.err: android.security.keystore.KeyPermanentlyInvalidatedException: Key permanently invalidated
W/System.err:     at android.security.keystore2.KeyStoreCryptoOperationUtils.getInvalidKeyException(KeyStoreCryptoOperationUtils.java:123)
W/System.err:     at android.security.keystore2.AndroidKeyStoreKeyAgreementSpi.ensureKeystoreOperationInitialized(AndroidKeyStoreKeyAgreementSpi.java:228)
W/System.err:     at android.security.keystore2.AndroidKeyStoreKeyAgreementSpi.engineInit(AndroidKeyStoreKeyAgreementSpi.java:96)
W/System.err:     at javax.crypto.KeyAgreement.init(KeyAgreement.java:498)
W/System.err:     at javax.crypto.KeyAgreement.init(KeyAgreement.java:470)
W/System.err:     at app.exploitr.sec.rts.security.ECCUtil.sharedSecret(ECCUtil.java:55)

I've tried to test the code in the following manner,

try {
    KeyPair one = ECCUtil.generateKeys();
    KeyPair two = ECCUtil.generateKeys();

    byte[] sec_one = ECCUtil.sharedSecret(one.getPrivate(), two.getPublic());
    byte[] sec_two = ECCUtil.sharedSecret(two.getPrivate(), one.getPublic());

    Log.d("TAG 1st SHARED", new String(sec_one));
    Log.d("TAG 2nd SHARED", new String(sec_two));

} catch (IOException | GeneralSecurityException | ParseException e) {
    e.printStackTrace();
}

I can't understand why the keys are getting permanently invalidated instantly despite of setting user authentication disabled or large timeout. As key generation has issues with emulators and I've only 1 Android 12 device ( KeyProperties.PURPOSE_AGREE_KEY was introduced in API 31) I tried to generate and test both the local and remote keys on the device. What should be done here?

The generateKeys() method creates a new key pair with always the same alias ( KEY_PAIR_NAME , ie abcdefgh ). This makes the first key pair invalid as soon as the second key pair is generated.
Using the invalid private key in init() then throws an exception whose error message actually describes the problem quite accurately: KeyPermanentlyInvalidatedException: key permanently invalidated .
The fix is to create the second key pair with a different alias (for this, the alias can eg be passed to generateKeys() and forwarded to KeyGenParameterSpec.Builder() ):

...
KeyPair one = generateKeys("alias_one");
KeyPair two = generateKeys("alias_two");
...

public static KeyPair generateKeys(String alias) throws ... {
    ...
    keyPairGenerator.initialize(
        new KeyGenParameterSpec.Builder(
            alias,
    ...
}

Also it is wrong to convert the shared secret to a string using new String() . This applies the default charset encoding, which generally corrupts the binary data. Instead a binary-to-text encoding like Base64 should be used for the conversion to a string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM