我正在努力使用KeyStore将我的应用程序机密安全地存储在Android中。但是,我发现对于装有Android 7.1.1及更低版本的设备,使用KeyStore存储的密钥不是"硬件支持的?即每当我调用 isInsideSecureHardware(( 方法到 KeyInfo,它总是会返回我"false"。
据我测试,即使设备在设置->安全->凭据存储>存储类型中具有"存储类型 = 硬件支持",也会发生这种情况。但对于 Android 7.1.2 及更高版本来说,情况并非如此,只要凭据存储类型是硬件支持的,它总是为 isInsideSecureHardware(( 返回"true";从未尝试过其他类型的。
-
有没有办法强制将密钥存储在 Android 7.1.1 的安全硬件中,降至 4.3(根据我的理解,这就是在 KeyStore 中引入 HSM 的地方;如果我错了,请纠正我(?
-
谁能澄清一下如果存储的密钥不在安全硬件内会发生什么;密钥存储在哪里?它有多安全?为了简单起见,isInsideSecureHardware == true 与 isInsideSecureHardware == false 之间有什么区别?
这是我的代码片段,用于注入密钥并检查KeyInfo(是的,我要求Android 4.3......请忽略代码中的版本检查(:
private void injectKey(Context context, String keyName){
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
keyGenerator.init(new KeyGenParameterSpec.Builder(keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.setKeyValidityStart(start.getTime())
.setKeyValidityEnd(end.getTime())
//.setUserAuthenticationRequired(true) //need PIN to get key
//.setUserAuthenticationValidityDurationSeconds(86400) //1-day time can use key from entering PIN
//.setUnlockedDeviceRequired(true) API level 28
//.setIsStrongBoxBacked(true) API level 28
.build()
);
}
SecretKey secretKey = keyGenerator.generateKey();
//check key info
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo;
try {
keyInfo = (KeyInfo) factory.getKeySpec(secretKey, KeyInfo.class);
boolean insideHW = keyInfo.isInsideSecureHardware();
boolean authReq = keyInfo.isUserAuthenticationRequired();
boolean authHW = keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware();
String[] blockModes = keyInfo.getBlockModes();
String[] digests = keyInfo.getDigests();
String[] encryptionPaddings = keyInfo.getEncryptionPaddings();
int keySize = keyInfo.getKeySize();
Date keyValidityStart = keyInfo.getKeyValidityStart();
Date keyEndValid = keyInfo.getKeyValidityForConsumptionEnd();
String keyAlias = keyInfo.getKeystoreAlias();
int keyPurpose = keyInfo.getPurposes();
String [] signaturePaddings = keyInfo.getSignaturePaddings();
int authType = keyInfo.getUserAuthenticationValidityDurationSeconds();
MainActivity.showMessage(context, "Key Info",
"inside HW = " + insideHW + "n" +
"auth Req = " + authReq + "n" +
"auth HW = " + authHW + "n" +
"keySize = " + keySize + "n" +
"keyValidityStart = " + keyValidityStart + "n" +
"keyEndValid = " + keyEndValid + "n" +
"keyAlias = " + keyAlias + "n" +
"keyPurpose = " + keyPurpose + "n" +
"authType = " + authType + "n");
String checkKeyInfo = "";
} catch (InvalidKeySpecException e) {
String checkKeyInfo = "";
}
}
}
这些版本与您的发现并不完全一致,尽管Android硬件密钥库仅支持从API 23开始存储AES密钥 - 使用RSA密钥可能会取得更大的成功,因为这些密钥从API 18开始就受支持。
链接到 developer.android.com 上的相关文档
这就是为什么互联网上存在"如何使用硬件密钥库加密"的例子,建议在密钥库中创建一个RSA密钥对,使用SecureRandom或类似方法创建256位AES密钥,然后使用私有RSA密钥加密AES密钥,并将结果存储在SharedPreferences中。这适用于 API 18+,无需更改。
因此,请尝试使用 RSA 密钥,您会有更多的运气,但仍然不是 API 18 之前。