下面列出了java.security.KeyStore.SecretKeyEntry#android.security.keystore.KeyProperties 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@RequiresApi(Build.VERSION_CODES.M)
private static SecretKey createKeyStoreEntry() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
}
}
/**
* Retrieves a key store helper for the given key store alias.
*
* @param alias The alias to retrieve a helper for.
*/
public KeyStoreHelper(KeyStoreAlias alias) {
try {
// Load the key store if not already loaded.
if (store == null) loadKeyStore();
// Check if the alias exists. If not, create it.
if (!store.containsAlias(alias.getAlias())) {
Log.i("Generating new key for alias %s", alias); //NON-NLS
KeyGenerator keygen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias.getAlias(), KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keygen.init(spec);
this.key = keygen.generateKey();
} else {
Log.i("Loading existing key for alias %s", alias); //NON-NLS
KeyStore.SecretKeyEntry keyEntry = (KeyStore.SecretKeyEntry) store.getEntry(alias.getAlias(), null);
this.key = keyEntry.getSecretKey();
}
} catch (Exception e) {
Log.e("Unable to load key store or generate keys", e); //NON-NLS
}
}
private static byte[] encrypt(SecretKey key, byte[] blob)
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if (blob == null) {
return null;
}
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+ KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal(blob);
byte[] iv = cipher.getIV();
if (iv.length != PROFILE_KEY_IV_SIZE) {
throw new RuntimeException("Invalid iv length: " + iv.length);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(iv);
outputStream.write(ciphertext);
return outputStream.toByteArray();
}
public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
throws KeyStoreException {
Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material",
userId, uid, alias, secretKey.length));
try {
mKeyStore.setEntry(
getInternalAlias(userId, uid, alias),
new SecretKeyEntry(
new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
new KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
} catch (KeyStoreException e) {
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
}
/**
* Generate a new ES256 keypair (COSE algorithm -7, ECDSA + SHA-256 over the NIST P-256 curve).
*
* @param alias The alias used to identify this keypair in the keystore. Needed to use key
* in the future.
* @return The KeyPair object representing the newly generated keypair.
* @throws VirgilException
*/
private KeyPair generateNewES256KeyPair(String alias) throws VirgilException {
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec(CURVE_NAME))
.setDigests(KeyProperties.DIGEST_SHA256)
.setUserAuthenticationRequired(this.authenticationRequired) // fingerprint or similar
.setUserConfirmationRequired(false) // TODO: Decide if we support Android Trusted Confirmations
.setInvalidatedByBiometricEnrollment(false)
.setIsStrongBoxBacked(this.strongboxRequired)
.build();
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, KEYSTORE_TYPE);
keyPairGenerator.initialize(spec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new VirgilException("couldn't generate key pair: " + e.toString());
}
}
/**
* Generates a rsa key pair if it not exists.
*
* @param context the application context
*/
public static void generateKey(Context context) throws Exception {
KeyStore keyStore;
keyStore = KeyStore.getInstance(AndroidKeyStore);
keyStore.load(null, null);
// Generate the RSA key pairs for encryption
if (!keyStore.containsAlias(KEY_ALIAS)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 30);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=" + KEY_ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
kpg.initialize(spec);
kpg.generateKeyPair();
}
}
@TargetApi(Build.VERSION_CODES.M)
private boolean generateKey(String keystoreAlias, boolean isAuthenticationRequired) {
try {
final KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyGenerator.initialize(
new KeyGenParameterSpec.Builder(keystoreAlias,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationRequired(isAuthenticationRequired)
.build());
keyGenerator.generateKeyPair();
return true;
} catch ( NoSuchAlgorithmException
| NoSuchProviderException
| InvalidAlgorithmParameterException exc) {
exc.printStackTrace();
return false;
}
}
/**
* Gets a secret key from Android key store.
* If no key has been generated with a given alias then generate a new one
* @return
* @throws KeyStoreException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
* @throws UnrecoverableKeyException
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private static Key getSecretKey() throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException, NoSuchProviderException,
InvalidAlgorithmParameterException,
UnrecoverableKeyException {
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID);
keyStore.load(null);
if (!keyStore.containsAlias(KEY_ALIAS_AMAZE)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_ANDROID);
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEY_ALIAS_AMAZE,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM);
builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
builder.setRandomizedEncryptionRequired(false);
keyGenerator.init(builder.build());
return keyGenerator.generateKey();
} else {
return keyStore.getKey(KEY_ALIAS_AMAZE, null);
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
protected void generateKeysForAPIMOrGreater(String keyAlias) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyGenerator keyGenerator;
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_NAME);
keyGenerator.init(
new KeyGenParameterSpec.Builder(keyAlias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
// NOTE no Random IV. According to above this is less secure but acceptably so.
.setRandomizedEncryptionRequired(false)
.build());
// Note according to [docs](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html)
// this generation will also add it to the keystore.
keyGenerator.generateKey();
}
public boolean createKeyPair() {
try {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setKeySize(2048);
builder.setIsStrongBoxBacked(true);
builder.setInvalidatedByBiometricEnrollment(true);
builder.setUserAuthenticationRequired(true);
keyPairGenerator.initialize(builder.build());
keyPairGenerator.generateKeyPair();
return true;
} catch (InvalidAlgorithmParameterException e) {
return false;
}
}
/**
* Creates a symmetric key in the Android Key Store which can only be used after the user has
* authenticated with fingerprint.
*/
private void createKey() {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
mKeyStore.load(null);
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
mKeyGenerator.generateKey();
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| CertificateException | IOException e) {
Log.e(""," Failed to createKey, e:");
throw new RuntimeException(e);
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private static SecretKey createKey() throws FingerprintException {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
return keyGenerator.generateKey();
} catch (Exception e) {
throw new FingerprintException(App.get().getString(R.string.fingeprint_secretKeyGenerationFailed), e);
}
}
private KeyPair generateKeyPair(String keyName, boolean invalidatedByBiometricEnrollment) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
// Require the user to authenticate with a biometric to authorize every use of the key
.setUserAuthenticationRequired(true);
// Generated keys will be invalidated if the biometric templates are added more to user device
if (Build.VERSION.SDK_INT >= 24) {
builder.setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment);
}
keyPairGenerator.initialize(builder.build());
return keyPairGenerator.generateKeyPair();
}
@Override
public void generatePrivateKeyPair(String keyAlias) throws GeneralSecurityException, IOException {
//pre android-M, the keystore only support RSA key generation. So here we will generate a RSA keypair first, then generate the AES key.
//we then encrypt the AES key using the generated RSA public key, and save it using the SharedPreferences
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 99);
KeyPairGeneratorSpec generatorSpec = new KeyPairGeneratorSpec
.Builder(context)
.setAlias(keyAlias)
.setSubject(new X500Principal("CN=" + keyAlias))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
generator.initialize(generatorSpec);
generator.generateKeyPair();
}
public boolean createKeyPair() {
try {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setKeySize(2048);
builder.setIsStrongBoxBacked(true);
builder.setInvalidatedByBiometricEnrollment(true);
builder.setUserAuthenticationRequired(true);
keyPairGenerator.initialize(builder.build());
keyPairGenerator.generateKeyPair();
return true;
} catch (InvalidAlgorithmParameterException e) {
return false;
}
}
@RequiresApi(api = M)
private static void generateKeyPairForMarshmallow(@NonNull Context context) throws SecureStorageException {
try {
if (isRTL(context)) {
Locale.setDefault(Locale.US);
}
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ENCRYPTION_ALGORITHM, KEY_KEYSTORE_NAME);
KeyGenParameterSpec keyGenParameterSpec =
new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build();
generator.initialize(keyGenParameterSpec);
generator.generateKeyPair();
} catch (Exception e) {
throw new SecureStorageException(e.getMessage(), e, KEYSTORE_EXCEPTION);
}
}
private void createCipherKeyGenerator(){
if(cipherKeyGenCreated){
return;
}
try {
cipherKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, provider);
cipherKeyGenerator.init(new KeyGenParameterSpec.Builder(keyName,KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
cipherKeyGenCreated = true;
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new RuntimeException("Failed to create key generator", e);
}
}
private void createCipher(){
if(cipherCreated){
return;
}
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipherCreated = true;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("Failed to get an instance of Cipher", e);
}
}
/**
* Generate a new key pair entry in the Android Keystore by using the KeyPairGenerator API.
* This creates both a KeyPair and a self-signed certificate, both with the same alias,
* using the {@link #keyAlgorithm} provided.
*/
private void generateAuthenticationKey() throws GeneralSecurityException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm, keystoreName);
KeyGenParameterSpec.Builder specBuilder =
new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_SIGN)
.setCertificateSubject(new X500Principal("CN=unused"))
.setDigests(KeyProperties.DIGEST_SHA256);
if (keyAlgorithm.equals(KeyProperties.KEY_ALGORITHM_RSA)) {
specBuilder.setKeySize(KEY_SIZE_RSA)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
} else if (keyAlgorithm.equals(KeyProperties.KEY_ALGORITHM_EC)) {
specBuilder.setKeySize(KEY_SIZE_EC);
}
kpg.initialize(specBuilder.build());
kpg.generateKeyPair();
}
/**
* 创建密钥
*/
@TargetApi(Build.VERSION_CODES.M)
private void initKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
keyGenerator.init(builder.build());
keyGenerator.generateKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Generate NIST P-256 EC Key pair for signing and verification
*
* @param keyName
* @param invalidatedByBiometricEnrollment
* @return
* @throws Exception
*/
@TargetApi(Build.VERSION_CODES.P)
private KeyPair generateKeyPair(String keyName, boolean invalidatedByBiometricEnrollment) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
// Require the user to authenticate with a biometric to authorize every use of the key
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment);
keyPairGenerator.initialize(builder.build());
return keyPairGenerator.generateKeyPair();
}
public void createKey(String keyName) {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
mKeyStore.load(null);
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
mKeyGenerator.init(builder.build());
mKeyGenerator.generateKey();
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
@TargetApi(23)
private void initKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
keyGenerator.init(builder.build());
keyGenerator.generateKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
@TargetApi(Build.VERSION_CODES.M)
Cipher cipherForEncryption() throws GeneralSecurityException, IOException {
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
keyGenerator.initialize(getKeyGenParameterSpecBuilder(keyName, KeyProperties.BLOCK_MODE_ECB, KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, invalidatedByBiometricEnrollment)
.build());
keyGenerator.generateKeyPair();
KeyFactory keyFactory = KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_RSA);
Cipher cipher = createCipher();
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(keyFactory, keyStore));
return cipher;
}
/** Get encryption algorithm specification builder instance. */
@NonNull
@Override
protected KeyGenParameterSpec.Builder getKeyGenSpecBuilder(@NonNull final String alias)
throws GeneralSecurityException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
throw new KeyStoreAccessException("Unsupported API" + Build.VERSION.SDK_INT + " version detected.");
}
final int purposes = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT;
return new KeyGenParameterSpec.Builder(alias, purposes)
.setBlockModes(BLOCK_MODE_CBC)
.setEncryptionPaddings(PADDING_PKCS7)
.setRandomizedEncryptionRequired(true)
.setKeySize(ENCRYPTION_KEY_SIZE);
}
/** Get builder for encryption and decryption operations with required user Authentication. */
@NonNull
@Override
@SuppressLint("NewApi")
protected KeyGenParameterSpec.Builder getKeyGenSpecBuilder(@NonNull final String alias)
throws GeneralSecurityException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
throw new KeyStoreAccessException("Unsupported API" + Build.VERSION.SDK_INT + " version detected.");
}
final int purposes = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT;
return new KeyGenParameterSpec.Builder(alias, purposes)
.setBlockModes(BLOCK_MODE_ECB)
.setEncryptionPaddings(PADDING_PKCS1)
.setRandomizedEncryptionRequired(true)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(1)
.setKeySize(ENCRYPTION_KEY_SIZE);
}
/**
* Creates a symmetric key in the Android Key Store which can only be used after the user has
* authenticated with fingerprint.
*/
public void createKey() {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
mKeyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
mKeyGenerator.generateKey();
} catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
@RequiresApi(Build.VERSION_CODES.M)
public static SecretKey createKeyStoreEntryHmac() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_HMAC_SHA256, ANDROID_KEY_STORE);
KeyGenParameterSpec.Builder keySpecBuilder = new KeyGenParameterSpec.Builder(KEY_ALIAS_HMAC, KeyProperties.PURPOSE_SIGN);
KeyGenParameterSpec keyGenParameterSpec = keySpecBuilder.build();
keyGenerator.init(keyGenParameterSpec);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
}
}
@VisibleForTesting
protected String getDecryptedPasswordForTiedProfile(int userId)
throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
CertificateException, IOException {
if (DEBUG) Slog.v(TAG, "Get child profile decrytped key");
byte[] storedData = mStorage.readChildProfileLock(userId);
if (storedData == null) {
throw new FileNotFoundException("Child profile lock file not found");
}
byte[] iv = Arrays.copyOfRange(storedData, 0, PROFILE_KEY_IV_SIZE);
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
storedData.length);
byte[] decryptionResult;
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey decryptionKey = (SecretKey) keyStore.getKey(
LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
decryptionResult = cipher.doFinal(encryptedPassword);
return new String(decryptionResult, StandardCharsets.UTF_8);
}
private static byte[] decrypt(SecretKey key, byte[] blob)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
if (blob == null) {
return null;
}
byte[] iv = Arrays.copyOfRange(blob, 0, PROFILE_KEY_IV_SIZE);
byte[] ciphertext = Arrays.copyOfRange(blob, PROFILE_KEY_IV_SIZE, blob.length);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
return cipher.doFinal(ciphertext);
}