EncryptedSharedPreferenes 是 androidx 下安全组件中的加密类,实现SharedPreferences的键值对加密。

开发者文档中提供了SharedPreferences加密键值对的实例代码,其中使用MasterKeys来进行密钥管理,而在 MasterKeys 的文档中提示该类已废弃,应使用MasterKey.Builder来管理主密钥(版本说明:基于 Jetpack Security 1.1.0-alpha01),示例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 // this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
         MASTER_KEY_ALIAS,
         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
         .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
         .setKeySize(KEY_SIZE)
         .build();
 MasterKey masterKey = new MasterKey.Builder(MainActivity.this)
         .setKeyGenParameterSpec(spec)
         .build();
 EncryptedSharedPreferences.create(
         MainActivity.this,
         "your-app-preferences-name",
         masterKey, // masterKey created above
         EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
         EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);

密钥管理

KeyGenParameterSpecandroid.security.keystore中的类,用于指定密钥的参数,相当于先制定一个规范,规范中指明密钥别名、密钥用途、加密模式等密钥属性,然后在生成密钥的时候直接使用指定的规范。我们重点需要关注的是,主密钥的生成和存储,也就是下面调用的MasterKey.Builder方法。

MasterKey.Builder就是用于生成MasterKey的构建器,最终生成密钥的方法是构建器里的build(),那么在build()里面做了什么事情,需要深入到源码里面去看看。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Builds a {@link MasterKey} from this builder.
* @return The master key.
*/
@NonNull
public MasterKey build() throws GeneralSecurityException, IOException {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		return buildOnM();
	} else {
		return new MasterKey(mKeyAlias, null);
	}
}

private MasterKey buildOnM() throws GeneralSecurityException, IOException {
  ...
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mRequestStrongBoxBacked) {
    if (mContext.getPackageManager().hasSystemFeature(
      PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
      builder.setIsStrongBoxBacked(true);
    }
  }
  mKeyGenParameterSpec = builder.build();//完成KeyGenParameterSpec的构建
  ...
  @SuppressWarnings("deprecation")
    String keyAlias = MasterKeys.getOrCreate(mKeyGenParameterSpec);//按照Spec指定的参数创建密钥
  return new MasterKey(keyAlias, mKeyGenParameterSpec);
}

我们以 Android 9.0 为参考,build中调用的是 buildOnM(),而在buildOnM()中会检查系统是否支持基于硬件的 StrongBox Keystore,如果支持,则调用setIsStrongBoxBacked(true)以设置该密钥由 StrongBox 安全芯片保护,至此密钥参数设置的最后一步完成,并返回一个KeyGenParameterSpec的实例,build()内容很简单,直接返回一个KeyGenParameterSpec实例,如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public KeyGenParameterSpec build() {
	return new KeyGenParameterSpec(
    mKeystoreAlias,
    mNamespace,
    mKeySize,
    ......
    mIsStrongBoxBacked,
    mUserConfirmationRequired,
    mUnlockedDeviceRequired,
    mCriticalToDeviceEncryption);
  }
}

而后返回到buildOnM()中,调用MasterKeys.getOrCreate(mKeyGenParameterSpec)创建主密钥并返回密钥别名字符串,然后逐级返回上层调用方法。