Android中的加密
目录,本文主要分为两部分:
1.使用AES算法进行加密
2.使用RSA算法进行加密
在使用Encryption之前最好学习一些东西:
- 算法类型(非对称和对称)
- 模式和填充
- 按键类型
- Java密码体系结构
- Android Key Store
1、安卓中的AES加密
AES是一种众所周知且最推荐的标准加密算法,该算法使用替换置换网络通过对称密钥对纯数据进行加密。
对称密钥是由 基于密码的密钥派生功能( PBKDF2 ) 生成的是获取密码并用 salt 对其进行哈希处理多次,以确保生成唯一的密钥,即使不同的用户使用相同的密码也是如此。该密钥还可以使用 Android Keystore 和 Galois / Counter Mode( GCM ) 用作阻止模式来生成 。 AES支持128、192和256位的密钥长度。
AES仅将128位数据作为一个块进行加密,要对整个消息进行加密,我们必须选择一种块模式,例如 电子密码簿(ECB) 或 密码块链接(CBC),计数器模式(CTR) ,其中可以对多个块进行加密到单个密文。
ECB在每个块上使用相同的未更改密钥进行加密
CBC使用先前生成的密码对新块进行加密,并对第一个块使用Initialization Vector
CTR将块密码转换为流密码,这意味着无需填充
AES加密将在API 23+以上提供
带有GCM块模式的AES加密
在这种方法中,将使用密码生成一个初始向量,随后将用于解密数据,该方法中不需要加salt。
创建密钥库:
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
keyStore.load(null)
创建对称密钥:
VAL的KeyGenerator = 的KeyGenerator .getInstance(KeyProperties。KEY_ALGORITHM_AES,ANDROID_KEYSTORE)
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
KEY_ALIAS,
关键属性。PURPOSE_ENCRYPT 或 KeyProperties。PURPOSE_DECRYPT)
.setBlockModes(KeyProperties。BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties。ENCRYPTION_PADDING_NONE)
// .setUserAuthenticationRequired(true)//需要锁定屏幕,如果禁用了锁定屏幕则无效
// .setUserAuthenticationValidityDurationSeconds(120)//仅在密码验证后的x秒内可用。
.setRandomizedEncryptionRequired(true)//每次调用时相同明文的4种不同密文
。建立()
keyGenerator。初始化(keyGenParameterSpec)
val secretkey = keyGenerator.generateKey()
setUserAuthenticationRequired()对于加密和解密所需的身份验证为true,这可能取决于移动提供商,有时会导致崩溃应用
然后我们需要检查秘钥是否已经存在了,当然这是可选的:
fun isKeyExists(keyStore : KeyStore): Boolean {
val aliases = keyStore.aliases()
while (aliases.hasMoreElements()) {
return (KEY_ALIAS == aliases.nextElement())
return false
}
使用AES加密数据:
fun encryptData(data: ByteArray): HashMap<String, ByteArray> {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getSymmetricKey())
val eiv = (Base64.encodeToString(cipher.iv,Base64.NO_WRAP)).toByteArray()
val edata = (Base64.encodeToString(cipher.doFinal(data),Base64.NO_WRAP)).toByteArray()
return hashMapOf(Pair(IV_VALUE,eiv),Pair(ENC_VALUE,edata))
}
密码实例转换格式应为算法/模式/填充,加密数据和IV将以字节数组格式生成。将其转换为Base64只是增加了一层安全性,并且是完全可选的
解密数据:
fun decryptNoBase(ivBytes : ByteArray,encryptedBytes : ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, getSymmetricKey(), GCMParameterSpec(128, ivBytes))
val decryptedData = cipher.doFinal(encryptedBytes).fromBytetoString()
return decryptedData
}
具有CBC块模式的AES加密
在这种方法中,将需要使用 密码 charArray来加密和解密数据:
加密:
fun encrypt(datatoEncrypt: ByteArray): HashMap<String, ByteArray> {
val salt = ByteArray(256)
SecureRandom().nextBytes(salt)
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
val iterationCount = 1324
val keyLength = 256
val pbKeySpec = PBEKeySpec(ENCRYPT_PASSWORD.toCharArray(), salt, iterationCount, keyLength)
val keyBytes = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(pbKeySpec).encoded
val secKey = SecretKeySpec(keyBytes, KeyProperties.KEY_ALGORITHM_AES)
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(Cipher.ENCRYPT_MODE, secKey , IvParameterSpec(iv))
return hashMapOf(Pair(SALT_VALUE,salt),Pair(IV_VALUE,iv),Pair(ENC_VALUE,cipher.doFinal(datatoEncrypt)))
}
解密数据,使用异常处理和断点需要与加密数据时生成的盐和IV相同的盐和IV,以更好地了解执行流程和异常(如果引发)
2、使用RSA加密
一种非对称密码算法,基本上使用两个不同的密钥来加密和解密数据。公钥用于加密,而私钥用于解密
RSA从API级别10+开始,但是不建议使用,大多数填充都在API级别18+上运行,与对称加密相比,加密过程有点慢。
先创建秘钥对:
fun createAsymmetricKeyPair(): KeyPair {
val generator: KeyPairGenerator
if (hasMarshmallow()) {
generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE)
val builder = KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
//.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
} else {
generator = KeyPairGenerator.getInstance("RSA")
generator.initialize(2048)
return generator.generateKeyPair()
}
加密解密数据:
fun encrypt(data: String, publicKey: Key?): String {
val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val bytes = cipher.doFinal(data.toByteArray())
return Base64.encodeToString(bytes, Base64.DEFAULT)
fun decrypt(data: String, privateKey: Key?): String {