【Android Keystore】Android 密钥库系统使用指南
Android 密钥库系统
https://developer.android.google.cn/privacy-and-security/keystore?hl=zh-cn
Android Keystore 系统的核心设计目标之一,就是确保密钥材料(Key Material)难以从设备中提取,并且通常无法在其他设备中使用。
Android Keystore 是一个系统服务,它提供了一个安全的容器来生成、存储和管理加密密钥,并确保密钥材料本身难以从设备中提取。
Android Keystore 是一个安全的密钥管理系统,而不仅仅是一个简单的密钥存储库。它负责密钥的全生命周期管理(生成、存储、使用),并确保密钥材料的安全
Android Keystore 系统提供了一种安全的方式来生成、存储和使用加密密钥,确保密钥材料难以从设备中提取,以下示例展示如何使用 Java 代码生成密钥、检查其是否受硬件支持、进行数据加密和解密,以及验证签名。
1. 生成密钥 🔐
首先,使用 KeyPairGenerator
生成一个 RSA 密钥对,并将其存储在 Android Keystore 中。密钥生成时会指定用途(如加密、解密、签名、验证)、摘要算法、是否需要用户认证等参数。
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;// 生成非对称密钥对(例如 RSA 或 EC)
public KeyPair generateKeyPair(String alias, boolean requireAuth) throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(alias,KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY).setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1).setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS).setKeySize(2048);if (requireAuth) {builder.setUserAuthenticationRequired(true);// 设置认证有效期(秒),或每次操作都需要认证// builder.setUserAuthenticationValidityDurationSeconds(300);}keyPairGenerator.initialize(builder.build());return keyPairGenerator.generateKeyPair();
}// 生成对称密钥(例如 AES)
public SecretKey generateSymmetricKey(String alias) throws Exception {KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias,KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE).setKeySize(256).build();keyGenerator.initialize(spec);return keyGenerator.generateKey();
}
2. 检查密钥的硬件支持 🔍
生成密钥后,可以检查密钥是否存储在硬件安全模块(如 TEE 或 StrongBox)中。
import android.security.keystore.KeyInfo;
import java.security.KeyFactory;public void checkKeySecurityLevel(String alias) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);KeyStore.Entry entry = keyStore.getEntry(alias, null);if (entry instanceof KeyStore.PrivateKeyEntry) {PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();KeyFactory factory = KeyFactory.getInstance(privateKey.getAlgorithm(), "AndroidKeyStore");KeyInfo keyInfo = factory.getKeySpec(privateKey, KeyInfo.class);boolean isInsideSecureHardware = keyInfo.isInsideSecureHardware();int securityLevel = KeyProperties.SECURITY_LEVEL_SOFTWARE;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {securityLevel = keyInfo.getSecurityLevel();if (securityLevel == KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {System.out.println("Key is in Trusted Execution Environment (TEE)");} else if (securityLevel == KeyProperties.SECURITY_LEVEL_STRONGBOX) {System.out.println("Key is in StrongBox");} else {System.out.println("Key is in software");}} else {System.out.println("isInsideSecureHardware: " + isInsideSecureHardware);}}
}
3. 使用密钥进行加密和解密 🔒
使用生成的密钥进行加密和解密操作。以下示例使用 RSA 非对称加密。
import javax.crypto.Cipher;public byte[] encryptData(String alias, byte[] data) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);KeyStore.Entry entry = keyStore.getEntry(alias, null);if (entry instanceof KeyStore.PrivateKeyEntry) {PublicKey publicKey = ((KeyStore.PrivateKeyEntry) entry).getCertificate().getPublicKey();Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(data);}throw new Exception("No such key or not a private key entry");
}public byte[] decryptData(String alias, byte[] encryptedData) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);KeyStore.Entry entry = keyStore.getEntry(alias, null);if (entry instanceof KeyStore.PrivateKeyEntry) {PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(encryptedData);}throw new Exception("No such key or not a private key entry");
}
4. 使用密钥进行签名和验证 ✍️
使用密钥对数据进行签名和验证,确保数据完整性和来源可信。
import java.security.Signature;public byte[] signData(String alias, byte[] data) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);KeyStore.Entry entry = keyStore.getEntry(alias, null);if (entry instanceof KeyStore.PrivateKeyEntry) {PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();Signature signature = Signature.getInstance("SHA256withRSA");signature.initSign(privateKey);signature.update(data);return signature.sign();}throw new Exception("No such key or not a private key entry");
}public boolean verifyData(String alias, byte[] data, byte[] signatureBytes) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);KeyStore.Entry entry = keyStore.getEntry(alias, null);if (entry instanceof KeyStore.PrivateKeyEntry) {PublicKey publicKey = ((KeyStore.PrivateKeyEntry) entry).getCertificate().getPublicKey();Signature signature = Signature.getInstance("SHA256withRSA");signature.initVerify(publicKey);signature.update(data);return signature.verify(signatureBytes);}throw new Exception("No such key or not a private key entry");
}
5. 注意事项 ⚠️
- 密钥别名: 每个密钥需要一个唯一的别名,用于在 Keystore 中标识和检索。
- 用户认证: 如果设置了
setUserAuthenticationRequired(true)
,使用密钥时需要用户认证(如指纹、PIN、图案等)。如果认证方式被移除,密钥将永久失效。 - 算法和模式支持: 不同设备和 Android 版本支持的算法和模式可能不同,建议检查设备支持情况。
- 错误处理: 所有操作都应妥善处理异常,如
KeyStoreException
、NoSuchAlgorithmException
、InvalidKeyException
等。
6. 完整流程示例 💡
以下是一个完整的示例,从生成密钥到使用它进行加密和解密:
public void fullKeyUsageExample() {String alias = "my_key_alias";try {// 1. 生成密钥KeyPair keyPair = generateKeyPair(alias, false);// 2. 检查硬件支持checkKeySecurityLevel(alias);// 3. 加密数据String originalData = "Sensitive data";byte[] encryptedData = encryptData(alias, originalData.getBytes("UTF-8"));// 4. 解密数据byte[] decryptedData = decryptData(alias, encryptedData);String decryptedString = new String(decryptedData, "UTF-8");// 5. 签名数据byte[] signature = signData(alias, originalData.getBytes("UTF-8"));// 6. 验证签名boolean isValid = verifyData(alias, originalData.getBytes("UTF-8"), signature);} catch (Exception e) {e.printStackTrace();}
}