Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
782618f
prototype
p3dr0rv Jun 11, 2025
df9555d
add interface ISecretKeyLoader
p3dr0rv Jun 12, 2025
0b256af
nits
p3dr0rv Jun 12, 2025
1637d5c
nits
p3dr0rv Jun 12, 2025
baf65c7
nits
p3dr0rv Jun 12, 2025
2a9c3de
be able to test
p3dr0rv Jun 13, 2025
e653ac3
more updates
p3dr0rv Jun 13, 2025
e8a1f8f
fix interface error
p3dr0rv Jun 13, 2025
cebb50b
Merge branch 'pedroro/interface-for-secretkeyloader' into pedroro/pro…
p3dr0rv Jun 13, 2025
f644d7a
fix error
p3dr0rv Jun 13, 2025
3e203d5
fix tests
p3dr0rv Jun 13, 2025
327c886
Merge branch 'pedroro/interface-for-secretkeyloader' into pedroro/pro…
p3dr0rv Jun 13, 2025
db8781a
clean and upadte
p3dr0rv Jun 16, 2025
3f730d1
Merge branch 'dev' into pedroro/prototype
p3dr0rv Jul 7, 2025
1961dd7
fixing merge errors
p3dr0rv Jul 7, 2025
6e9717d
Enhance AndroidWrappedKeyProvider and CipherSpec with additional cons…
p3dr0rv Jul 7, 2025
15382fa
Refactor AndroidKeyStoreRsaKekManager for improved key management; re…
p3dr0rv Jul 8, 2025
ca22c2a
Refactor key management in AndroidKeyStoreRsaKekManager and related c…
p3dr0rv Jul 10, 2025
404e23f
feat: Implement AndroidWrappedKeyProviderFactory and OAEPAndroidWrapp…
p3dr0rv Jul 10, 2025
8e62a7b
phasex
p3dr0rv Jul 11, 2025
f630ace
phase2
p3dr0rv Jul 11, 2025
1ab72c9
remove rsamanager
p3dr0rv Jul 11, 2025
fb0fcf9
feat: Add CipherSpec and KeyGenSpec classes for cryptographic operati…
p3dr0rv Jul 12, 2025
a80d4a6
feat: Replace NewAndroidWrappedKeyProvider with KeyStoreBackedSecretK…
p3dr0rv Jul 14, 2025
db44184
feat: Refactor cryptographic key handling and enhance logging
p3dr0rv Jul 14, 2025
cd419eb
Merge branch 'dev' into pedroro/prototype
p3dr0rv Jul 14, 2025
6cf8039
feat: Update CryptoParameterSpecFactory with lazy initialization and …
p3dr0rv Jul 15, 2025
41390b8
feat: Update AndroidKeyStoreUtilTest to handle null parameters in wra…
p3dr0rv Jul 15, 2025
54ad32b
feat: Refactor AndroidWrappedKeyProviderTest to use ISecretKeyProvide…
p3dr0rv Jul 16, 2025
9f5c380
feat: Add key management methods to AndroidWrappedKeyProviderTest for…
p3dr0rv Jul 16, 2025
9373bb8
feat: Replace AndroidWrappedKeyProvider with KeyStoreBackedSecretKeyP…
p3dr0rv Jul 16, 2025
82e7a6c
feat: Add instrumented tests for KeyStoreBackedSecretKeyProvider and …
p3dr0rv Jul 18, 2025
ec24441
Merge branch 'dev' into pedroro/prototype
p3dr0rv Jul 18, 2025
9971a28
Update common/src/main/java/com/microsoft/identity/common/crypto/KeyS…
p3dr0rv Jul 18, 2025
c1de3ae
update changelog
p3dr0rv Jul 18, 2025
077a29f
refactor: Update flightsProvider usage and improve logging verbosity …
p3dr0rv Jul 25, 2025
2d4b140
improve telemtry
p3dr0rv Jul 25, 2025
0372b4f
feat: Introduce WrappedSecretKey class and implement new key storage …
p3dr0rv Aug 12, 2025
14eb2e3
Merge branch 'dev' into pedroro/prototype
p3dr0rv Aug 12, 2025
f5e8b02
API level 19 is not available
p3dr0rv Aug 12, 2025
6482078
feat: Enhance key storage and retrieval with backward and forward com…
p3dr0rv Aug 20, 2025
629817b
Merge branch 'dev' into pedroro/prototype
p3dr0rv Aug 20, 2025
4818c5b
feat: Implement new wrapped secret key format with metadata and compa…
p3dr0rv Aug 20, 2025
5845348
feat: Introduce static flag to skip key invalidation checks for perfo…
p3dr0rv Aug 21, 2025
8f275c9
refactor: Update AndroidWrappedKeyProviderTest to use provider type k…
p3dr0rv Aug 25, 2025
a2485d5
Update common/src/main/java/com/microsoft/identity/common/crypto/Wrap…
p3dr0rv Sep 2, 2025
80edd0e
Update common/src/main/java/com/microsoft/identity/common/crypto/Wrap…
p3dr0rv Sep 2, 2025
6d0e060
refactor: Rename byteArray to wrappedKeyData in WrappedSecretKey and …
p3dr0rv Sep 3, 2025
43701a9
Merge branch 'pedroro/prototype' of https://github.com/AzureAD/micros…
p3dr0rv Sep 3, 2025
6c2d198
feat: Introduce WrappedSecretKey serialization with legacy and new fo…
p3dr0rv Sep 4, 2025
0816f74
Merge branch 'dev' into pedroro/prototype
p3dr0rv Sep 4, 2025
4ec9e27
refactor: Update exception handling in WrappedSecretKey tests and add…
p3dr0rv Sep 4, 2025
e659bd8
refactor: Rename variable rawData to wrappedSecretKeyData for clarity…
p3dr0rv Sep 4, 2025
2c19f96
no longer suuport API 22
p3dr0rv Sep 4, 2025
fb8e000
refactor: Update cipher transformation assignment in WrappedSecretKey…
p3dr0rv Sep 4, 2025
a9319fd
refactor: Update wrapped secret key serialization to use IDs and impr…
p3dr0rv Sep 17, 2025
5b30101
Merge branch 'dev' into pedroro/prototype
p3dr0rv Sep 17, 2025
2caeae1
repair merge error
p3dr0rv Sep 17, 2025
e0105ea
refactor: Update metadata handling in WrappedSecretKeySerializer and …
p3dr0rv Sep 17, 2025
9bdd48b
upadte tests and doc
p3dr0rv Sep 17, 2025
6ae70e5
Merge branch 'dev' into pedroro/prototype
p3dr0rv Sep 26, 2025
2fdaa12
Refactor key serialization: Introduce AbstractWrappedSecretKeySeriali…
p3dr0rv Sep 26, 2025
76c0fa7
Update tests to use KeyStoreBackedSecretKeyProvider instead of Androi…
p3dr0rv Sep 26, 2025
b945c47
Merge branch 'dev' into pedroro/prototype
p3dr0rv Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [MAJOR] Add KeyStoreBackedSecretKeyProvider (#2674)
- [MINOR] Add Open Id configuration issuer validation reporting in OpenIdProviderConfigurationClient (#2751)
- [MINOR] Add helper method to record elapsed time (#2768)
- [MINOR] Implement TenantUtil (#2761)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,90 @@

import com.microsoft.identity.common.adal.internal.AuthenticationSettings;
import com.microsoft.identity.common.internal.util.AndroidKeyStoreUtil;
import com.microsoft.identity.common.java.crypto.key.ISecretKeyProvider;
import com.microsoft.identity.common.java.exception.ClientException;
import com.microsoft.identity.common.java.util.FileUtil;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.File;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

import javax.crypto.SecretKey;
import javax.security.auth.x500.X500Principal;

@RunWith(Parameterized.class)
public class AndroidWrappedKeyProviderTest {

@Parameterized.Parameter(0)
public String providerType;

@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{"KEYSTORE_BACKED"},
{"ANDROID_WRAPPED"}
// Add other provider types here as keywords
});
}

private ISecretKeyProvider createProvider() {
if ("KEYSTORE_BACKED".equals(providerType)) {
return new KeyStoreBackedSecretKeyProvider(context, MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH);
} else if ("ANDROID_WRAPPED".equals(providerType)) {
return new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
} else {
throw new IllegalArgumentException("Unsupported provider type: " + providerType);
}
}

private SecretKey getKeyFromCache(ISecretKeyProvider keyProvider) {
if (keyProvider instanceof AndroidWrappedKeyProvider) {
return ((AndroidWrappedKeyProvider) keyProvider).getKeyFromCache();
} else if (keyProvider instanceof KeyStoreBackedSecretKeyProvider) {
return ((KeyStoreBackedSecretKeyProvider) keyProvider).getKeyFromCache();
}
throw new IllegalArgumentException("Unsupported key provider type: " + keyProvider.getClass().getName());
}

private void clearKeyFromCache(ISecretKeyProvider keyProvider) {
if (keyProvider instanceof AndroidWrappedKeyProvider) {
((AndroidWrappedKeyProvider) keyProvider).clearKeyFromCache();
} else if (keyProvider instanceof KeyStoreBackedSecretKeyProvider) {
((KeyStoreBackedSecretKeyProvider) keyProvider).clearKeyFromCache();
} else {
throw new IllegalArgumentException("Unsupported key provider type: " + keyProvider.getClass().getName());
}
}

private SecretKey readSecretKeyFromStorage(ISecretKeyProvider keyProvider) throws ClientException {
if (keyProvider instanceof AndroidWrappedKeyProvider) {
return ((AndroidWrappedKeyProvider) keyProvider).readSecretKeyFromStorage();
} else if (keyProvider instanceof KeyStoreBackedSecretKeyProvider) {
return ((KeyStoreBackedSecretKeyProvider) keyProvider).readSecretKeyFromStorage();
}
throw new IllegalArgumentException("Unsupported key provider type: " + keyProvider.getClass().getName());
}

private SecretKey generateNewSecretKey(ISecretKeyProvider keyProvider) throws ClientException {
if (keyProvider instanceof AndroidWrappedKeyProvider) {
return ((AndroidWrappedKeyProvider) keyProvider).generateRandomKey();
} else if (keyProvider instanceof KeyStoreBackedSecretKeyProvider) {
return ((KeyStoreBackedSecretKeyProvider) keyProvider).generateNewSecretKey();
}
throw new IllegalArgumentException("Unsupported key provider type: " + keyProvider.getClass().getName());
}

final Context context = ApplicationProvider.getApplicationContext();
final String MOCK_KEY_ALIAS = "MOCK_KEY_ALIAS";
final String MOCK_KEY_FILE_PATH = "MOCK_KEY_FILE_PATH";
Expand Down Expand Up @@ -111,17 +175,17 @@ private AlgorithmParameterSpec getMockKeyPairGeneratorSpec(final String alias) {

@Test
public void testGenerateKey() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
final SecretKey secretKey = keyProvider.generateRandomKey();
final ISecretKeyProvider keyProvider = createProvider();
final SecretKey secretKey = generateNewSecretKey(keyProvider);

Assert.assertEquals(AES_ALGORITHM, secretKey.getAlgorithm());
}

@Test
public void testReadKeyDirectly() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = initkeyProviderWithKeyEntry();
final ISecretKeyProvider keyProvider = initkeyProviderWithKeyEntry();
final SecretKey secretKey = keyProvider.getKey();
final SecretKey storedSecretKey = keyProvider.readSecretKeyFromStorage();
final SecretKey storedSecretKey = readSecretKeyFromStorage(keyProvider);

// They're not the same object!
Assert.assertNotSame(secretKey, storedSecretKey);
Expand All @@ -139,12 +203,12 @@ public void testReadKeyDirectly() throws ClientException {
public void testLoadKey() throws ClientException {
// Nothing exists. This load key function should generate a key if the key hasn't exist.
Assert.assertNull(AndroidKeyStoreUtil.readKey(MOCK_KEY_ALIAS));
Assert.assertNull(FileUtil.readFromFile(getKeyFile(), AndroidWrappedKeyProvider.KEY_FILE_SIZE));
Assert.assertNull(FileUtil.readFromFile(getKeyFile(), KeyStoreBackedSecretKeyProvider.KEY_FILE_SIZE));

final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
final ISecretKeyProvider keyProvider = createProvider();
final SecretKey secretKey = keyProvider.getKey();

final SecretKey key = keyProvider.getKeyFromCache();
final SecretKey key = getKeyFromCache(keyProvider);
Assert.assertNotNull(key);
Assert.assertEquals(AES_ALGORITHM, secretKey.getAlgorithm());
Assert.assertArrayEquals(secretKey.getEncoded(), key.getEncoded());
Expand All @@ -154,18 +218,18 @@ public void testLoadKey() throws ClientException {
@Test
public void testLoadKeyFromCorruptedFile_TruncatedExisingKey() throws ClientException {
// Create a new Keystore-wrapped key.
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
keyProvider.generateRandomKey();
final ISecretKeyProvider keyProvider = createProvider();
generateNewSecretKey(keyProvider);

final byte[] wrappedKey = FileUtil.readFromFile(getKeyFile(), AndroidWrappedKeyProvider.KEY_FILE_SIZE);
final byte[] wrappedKey = FileUtil.readFromFile(getKeyFile(), KeyStoreBackedSecretKeyProvider.KEY_FILE_SIZE);
Assert.assertNotNull(wrappedKey);

// Overwrite the key file with corrupted data.
FileUtil.writeDataToFile(Arrays.copyOfRange(wrappedKey, 0, wrappedKey.length/2), getKeyFile());

// It should fail to read, with an exception, and everything should be wiped.
try{
keyProvider.readSecretKeyFromStorage();
readSecretKeyFromStorage(keyProvider);
Assert.fail();
} catch (ClientException e){
Assert.assertEquals(INVALID_KEY, e.getErrorCode());
Expand All @@ -175,24 +239,24 @@ public void testLoadKeyFromCorruptedFile_TruncatedExisingKey() throws ClientExce
Assert.assertFalse(getKeyFile().exists());

// the next read should be unblocked.
Assert.assertNull(keyProvider.readSecretKeyFromStorage());
Assert.assertNull(readSecretKeyFromStorage(keyProvider));
}

@Test
public void testLoadKeyFromCorruptedFile_InjectGarbage() throws ClientException {
// Create a new Keystore-wrapped key.
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
keyProvider.generateRandomKey();
final ISecretKeyProvider keyProvider = createProvider();
generateNewSecretKey(keyProvider);

final byte[] wrappedKey = FileUtil.readFromFile(getKeyFile(), AndroidWrappedKeyProvider.KEY_FILE_SIZE);
final byte[] wrappedKey = FileUtil.readFromFile(getKeyFile(), KeyStoreBackedSecretKeyProvider.KEY_FILE_SIZE);
Assert.assertNotNull(wrappedKey);

// Overwrite the key file with corrupted data.
FileUtil.writeDataToFile(new byte[]{10, 20, 30, 40}, getKeyFile());

// It should fail to read, with an exception, and everything should be wiped.
try{
keyProvider.readSecretKeyFromStorage();
readSecretKeyFromStorage(keyProvider);
Assert.fail();
} catch (ClientException e){
Assert.assertEquals(INVALID_KEY, e.getErrorCode());
Expand All @@ -202,14 +266,14 @@ public void testLoadKeyFromCorruptedFile_InjectGarbage() throws ClientException
Assert.assertFalse(getKeyFile().exists());

// the next read should be unblocked.
Assert.assertNull(keyProvider.readSecretKeyFromStorage());
Assert.assertNull(readSecretKeyFromStorage(keyProvider));
}

// 1s With Google Pixel XL, OS Version 29 (100 loop)
@Test
@Ignore
public void testPerf_WithCachedKey() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
final ISecretKeyProvider keyProvider = createProvider();

long timeStartLoop = System.nanoTime();
for (int i = 0; i < TEST_LOOP; i++) {
Expand All @@ -224,11 +288,11 @@ public void testPerf_WithCachedKey() throws ClientException {
@Test
@Ignore("Performance test, ignore in normal test run")
public void testPerf_NoCachedKey() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
final ISecretKeyProvider keyProvider = createProvider();

long timeStartLoopNotCached = System.nanoTime();
for (int i = 0; i < 100; i++) {
keyProvider.clearKeyFromCache();
clearKeyFromCache(keyProvider);
keyProvider.getKey();
}
long timeFinishLoopNotCached = System.nanoTime();
Expand All @@ -241,31 +305,31 @@ public void testPerf_NoCachedKey() throws ClientException {
*/
@Test
public void testLoadDeletedKeyStoreKey() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = initkeyProviderWithKeyEntry();
final ISecretKeyProvider keyProvider = initkeyProviderWithKeyEntry();

AndroidKeyStoreUtil.deleteKey(MOCK_KEY_ALIAS);

// Cached key also be wiped.
final SecretKey key = keyProvider.getKeyFromCache();
final SecretKey key = getKeyFromCache(keyProvider);
Assert.assertNull(key);
}

@Test
public void testLoadDeletedKeyFile() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = initkeyProviderWithKeyEntry();
final ISecretKeyProvider keyProvider = initkeyProviderWithKeyEntry();

FileUtil.deleteFile(getKeyFile());

// Cached key also be wiped.
final SecretKey key = keyProvider.getKeyFromCache();
final SecretKey key = getKeyFromCache(keyProvider);
Assert.assertNull(key);
}

private AndroidWrappedKeyProvider initkeyProviderWithKeyEntry() throws ClientException {
final AndroidWrappedKeyProvider keyProvider = new AndroidWrappedKeyProvider(MOCK_KEY_ALIAS, MOCK_KEY_FILE_PATH, context);
private ISecretKeyProvider initkeyProviderWithKeyEntry() throws ClientException {
final ISecretKeyProvider keyProvider = createProvider();
final SecretKey key = keyProvider.getKey();
Assert.assertNotNull(key);
Assert.assertNotNull(keyProvider.getKeyFromCache());
Assert.assertNotNull(getKeyFromCache(keyProvider));
return keyProvider;
}
}
Loading