1.KeyStore简介:
Keytool:
一个Java数据证书的管理工具,Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中
keystore
可以理解为一个容器,它里面存储了:
1.可以存储对称密钥实体,或者密钥对的公钥和私钥
2.可信任的证书,只包含公钥
使用keytool命令或者AndroidStudio自带工具都可以生成我们自己的KeyStore,这里略
2.生成签名APK时需要一个KeyStore文件:
在通过AndroidStudio–>Build–>Generate Signed APK生成一个带签名的APK时,会要求输入一个key store path:
选择创建新的KeyStore,并填入新建KeyStore的名字,密码,以及开发者认证信息(国家,组织等)就可以创建一个自己的KeyStore,打包带签名的APK时,这个KeyStore中的信息会被一起封到APK中,所以每个带有签名的App中,都会有一个开发者为之创建的KeyStore,其他的App无法访问这个KeyStore,所以利用这个KeyStore可以存储一些密钥在里面。
3.Keystore做AES加密例:
通过ailas和password可以唯一地关联一个KeyStore文件,这个alias通常不区分大小写
public class EncryptTool {
private static final String alias = "AndroidKeyStore";
KeyStore keyStore;
public void initKeyStore() throws Exception {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
}
public static byte[] encrypt(String password) throws Exception {
//初始化一个使用AES加密的密钥生成器
final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
//配置参数
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M){
} else {
//这里生成密钥的参数中设置了alias,生成密钥的时候会自动将这个密钥保存在KeyStore中
final KeyGenParameterSpec keyGenParameterSpec =
new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
//用参数初始化密钥生成器
keyGenerator.init(keyGenParameterSpec);
}
//用密钥生成器产生密钥
final SecretKey secretKey = keyGenerator.generateKey();
//构造密码
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
//获取默认的initialization vector (IV),解密需要这个iv
//这个iv也可以手动生成一个随机数,并设置给
iv = cipher.getIV();
byte[] encryption = cipher.doFinal(password.getBytes("UTF-8"));
//加密完成,产生一个字节数组
return encryption;
}
public static String decrypt(byte[] data) throws Exception {
initKeyStore();
//从keystore中获取密钥Entry
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null);
//从entry中获取AES密钥
final SecretKey secretKey = secretKeyEntry.getSecretKey();
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
final byte[] decodedData = cipher.doFinal(data);
return new String(decodedData, "UTF-8");
}
}
4.Keystore做RSA加密例:
public class KeyStoreTool {
private static volatile KeyStoreTool sInstance = new KeyStoreTool();
private static final String PROVIDER_NAME = "AndroidKeyStore";
private static final String ALGORITHM = "RSA";
private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";
private KeyStore keyStore;
//获取AndroidKeyStore类型的KeyStore
public KeyStoreTool() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (Exception e) {
e.printStackTrace();
}
}
//用alias创建一个新的密钥对
private void createNewRSAKeyPair(Context context, @NonNull String keyAlias) throw Exception {
if (!keyStore.containsAlias(keyAlias)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 30);
AlgorithmParameterSpec spec;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
spec = new KeyPairGeneratorSpec.Builder(context.getApplicationContext())
//生成密钥的参数中设置了alias,生成密钥的时候会自动将这个密钥保存在KeyStore中
.setAlias(keyAlias)
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
} else {
spec = new KeyGenParameterSpec.Builder(keyAlias,KeyProperties.PURPOSE_ENCRYPT| KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateSerialNumber(BigInteger.ONE)
.setCertificateNotBefore(start.getTime())
.setCertificateNotAfter(end.getTime())
.build();
}
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER_NAME);
generator.initialize(spec);
generator.generateKeyPair();
}
}
//获取alias对应的密钥对中的私钥
private KeyStore.PrivateKeyEntry getPrivateKey(@NonNull String keyAlias) throws Exception {
keyStore.load(null);
KeyStore.Entry entry = keyStore.getEntry(keyAlias, null);
return (KeyStore.PrivateKeyEntry) entry;
}
//用alias对应的密钥加密字节数组
public byte[] encrypt(@NonNull byte[] data, @NonNull String keyAlias) throws Exception {
KeyStore.PrivateKeyEntry privateKeyEntry = getPrivateKey(keyAlias);
final Cipher inCipher = Cipher.getInstance(TRANSFORMATION);
inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inCipher);
cipherOutputStream.write(data);
cipherOutputStream.close();
return outputStream.toByteArray();
}
//用alias对应的密钥加密一个String
public String encryptString(@NonNull String plainText, @NonNull String keyAlias) throws Exception {
if (!TextUtils.isEmpty(plainText) && !TextUtils.isEmpty(keyAlias)) {
byte[] encrypted = encrypt(plainText.getBytes(), keyAlias);
if (encrypted != null) {
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}
}
return "";
}
public byte[] decrypt(@NonNull byte[] data, @NonNull String keyAlias) throws Exception {
KeyStore.PrivateKeyEntry privateKeyEntry = getPrivateKey(keyAlias);
Cipher output = Cipher.getInstance(TRANSFORMATION);
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream =new CipherInputStream(new ByteArrayInputStream(data), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte) nextByte);
}
byte[] bytes = new byte[values.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i);
}
return bytes;
}
//用alias对应的密钥解密字符串
public String decryptString(@NonNull String secureText, @NonNull String keyAlias) throws Exception{
if (!TextUtils.isEmpty(secureText) && !TextUtils.isEmpty(keyAlias)) {
byte[] encrypted = Base64.decode(secureText, Base64.DEFAULT);
byte[] raw = decrypt(encrypted, keyAlias);
if (raw != null) {
return new String(raw, 0, raw.length);
}
}
return "";
}
}
可以看到使用KeyStore管理密钥和用普通方式处理加密使用的基本类型都差不多,只是用keystore管理需要一个alias,添加了alias的密钥创建时会自动保存在KeyStore中,已创建AES密钥为例:
生成AES密钥的过程:
final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
KeyGenParameterSpec keyGenParameterSpec = ...
keyGenerator.init(keyGenParameterSpec);
final SecretKey secretKey = keyGenerator.generateKey();
密钥参数可以配置keystoreAlias
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
private final String mKeystoreAlias;
}
KeyGenerator通过KeyGeneratorSpi生成密钥:
public class KeyGenerator {
private volatile KeyGeneratorSpi spi;
public final SecretKey generateKey() {
...
return spi.engineGenerateKey();
}
}
对于AES加密,它的KeyGeneratorSpi是一个AndroidKeyStoreKeyGeneratorSpi对象:
public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
private final KeyStore mKeyStore = KeyStore.getInstance();
private KeyGenParameterSpec mSpec;
protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) {
mSpec = spec;
}
//KeyStore保存了keyAliasInKeystore
protected SecretKey engineGenerateKey() {
String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
int errorCode = mKeyStore.generateKey(keyAliasInKeystore,...);
...
return new AndroidKeyStoreSecretKey(keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA);
}
}