Android AES加密解密工具类
Posted 川峰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AES加密解密工具类相关的知识,希望对你有一定的参考价值。
一个用于android AES加密解密的工具类,记录一下。。。
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import androidx.annotation.RequiresApi
import java.io.*
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
@RequiresApi(Build.VERSION_CODES.M)
class CryptoManager
companion object
private const val IV_BLOCK_SIZE = 16
private const val ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
private const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
private const val PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
private const val TRANSFORMATION = "$ALGORITHM/$BLOCK_MODE/$PADDING"
private const val KeyStoreType = "AndroidKeyStore"
private const val KEY_ALIAS = "SecretKeyAlias"
private val cipher = Cipher.getInstance(TRANSFORMATION) //创建密码器
fun encrypt(encryptBytes: ByteArray): ByteArray?
try
cipher.init(Cipher.ENCRYPT_MODE, getKey()) //用密钥初始化Cipher对象
val final = cipher.doFinal(encryptBytes)
return cipher.iv + final // iv占前16位,加密后的数据占后面
catch (e: Exception)
e.printStackTrace()
return null
fun decrypt(decryptBytes: ByteArray): ByteArray?
try
val iv = decryptBytes.copyOfRange(0, IV_BLOCK_SIZE) // 先取出IV
val decryptData = decryptBytes.copyOfRange(IV_BLOCK_SIZE, decryptBytes.size) // 取出加密后的数据
cipher.init(Cipher.DECRYPT_MODE, getKey(), IvParameterSpec(iv))
return cipher.doFinal(decryptData)
catch (e: Exception)
e.printStackTrace()
return null
// type设为"AndroidKeyStore"使用Android专门提供的密钥存储系统,可以根据别名获取key, 类似于sp
private val keyStore = KeyStore.getInstance(KeyStoreType).apply load(null)
private fun getKey(): SecretKey
val existingKey = keyStore.getEntry(KEY_ALIAS, null) as? KeyStore.SecretKeyEntry
return existingKey?.secretKey ?: generateKey()
private fun generateKey(): SecretKey
return KeyGenerator.getInstance(ALGORITHM, KeyStoreType).apply
init(
KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(PADDING)
.setUserAuthenticationRequired(false)
.setRandomizedEncryptionRequired(true)
.build()
)
.generateKey()
/**
* 将 text 加密
*/
fun encrypt(text: String): String?
val encryptedBytes = encrypt(text.toByteArray())
return encryptedBytes?.let
// NO_WRAP is important as was getting \\n at the end
Base64.encodeToString(it, Base64.NO_WRAP)
/**
* 将 text 解密
*/
fun decrypt(base64EncodedText: String): String?
val decodedCipherBytes = Base64.decode(base64EncodedText, Base64.NO_WRAP)
val decryptedBytes = decrypt(decodedCipherBytes)
return decryptedBytes?.let String(it)
(注意:上面代码使用了一些API要求在 Android 6.0
以上)
以下 Cipher 类支持设置的加密算法:
上面代码中 Cipher
使用的就是 AES/CBC/PKCS7Padding
这一种加密算法。
这里关键的一点是加密解密用的 key 是使用 KeyStore 来存储,它是 Android 的密钥库系统,使用 KeyStore 存储的密钥只能使用但是不能从设备中导出,安全性更好。详情可以参考:Android 密钥库系统。
使用方式:
// 加密结果
val textEncrypted = CryptoManager.encrypt("hello world") ?: ""
// 解密结果
val textDecrypted = CryptoManager.decrypt(textEncrypted) ?: ""
这种使用方式是将文本加密后以Base64的字符串形式返回,解密时再传回这段Base64字符串。
还可以直接加密成 byte 数组来保存,但解密时同样需要传回 byte 数组:
// 加密结果
var encryptedBytes: ByteArray? = CryptoManager.encrypt(text.toByteArray())
// 如需将加密结果转换成文本,可以这样做
val textEncrypted = encryptedBytes?.let String(it) ?: ""
// 解密结果
var decryptedBytes: ByteArray? = encryptedBytes?.let CryptoManager.decrypt(it)
// 如需将解密结果转换成文本,可以这样做
val textDecrypted = decryptedBytes?.let String(it) ?: ""
示例 demo:
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.fly.mycompose.application.ui.theme.MyComposeApplicationTheme
@RequiresApi(Build.VERSION_CODES.M)
class EncryptActivity : ComponentActivity()
var encryptedBytes: ByteArray? = null
var decryptedBytes: ByteArray? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContent
MyComposeApplicationTheme
var text by remember mutableStateOf("")
var textEncrypted by remember mutableStateOf("")
var textDecrypted by remember mutableStateOf("")
Column(modifier = Modifier
.fillMaxSize()
.padding(32.dp)
.verticalScroll(rememberScrollState())
)
TextField(
value = text,
onValueChange = text = it ,
modifier = Modifier.fillMaxWidth(),
placeholder = Text(text = "请输入加密内容")
)
Spacer(modifier = Modifier.height(8.dp))
Row
Button(onClick =
textEncrypted = CryptoManager.encrypt(text) ?: ""
// encryptedBytes = CryptoManager.encrypt(text.toByteArray())
// textEncrypted = encryptedBytes?.let String(it) ?: ""
)
Text(text = "加密")
Spacer(modifier = Modifier.width(16.dp))
Button(onClick =
textDecrypted = CryptoManager.decrypt(textEncrypted) ?: ""
// decryptedBytes = encryptedBytes?.let CryptoManager.decrypt(it)
// textDecrypted = decryptedBytes?.let String(it) ?: ""
)
Text(text = "解密")
Text(text = "加密后的内容:$textEncrypted")
Text(text = "解密后的内容:$textDecrypted")
下面是另一个工具类,在其他地方看到的,实现方式也是类似,这个没有API在 Android 6.0 以上的限制,但是它没有使用 KeyStore 来存储 key,在加密/解密时还需要显示的指定一个密码。
import android.util.Base64
import android.util.Base64.*
import java.io.UnsupportedEncodingException
import java.security.GeneralSecurityException
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESCryptUtil
private const val AES_MODE = "AES/CBC/PKCS7Padding"
private const val CHARSET = "UTF-8"
private const val CIPHER = "AES"
private const val HASH_ALGORITHM = "SHA-256"
private val IV_BYTES = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
/**
* Generates SHA256 hash of the password which is used as key
*
* @param password used to generated key
* @return SHA256 of the password
*/
private fun generateKey(password: String): SecretKeySpec
val digest = MessageDigest.getInstance(HASH_ALGORITHM)
val bytes = password.toByteArray(charset(CHARSET))
digest.update(bytes, 0, bytes.size)
val key = digest.digest()
return SecretKeySpec(key, CIPHER)
/**
* Encrypt and encode message using 256-bit AES with key generated from password.
*
* @param password used to generated key
* @param message the thing you want to encrypt assumed String UTF-8
* @return Base64 encoded CipherText
* @throws GeneralSecurityException if problems occur during encryption
*/
fun encrypt(password: String, message: String): String
try
val key = generateKey(password)
val cipherText = encrypt(key, IV_BYTES, message.toByteArray(charset(CHARSET)))
//NO_WRAP is important as was getting \\n at the end
return Base64.encodeToString(cipherText, Base64.NO_WRAP)
catch (e: UnsupportedEncodingException)
throw GeneralSecurityException(e)
/**
* More flexible AES encrypt that doesn't encode
*
* @param key AES key typically 128, 192 or 256 bit
* @param iv Initiation Vector
* @param message in bytes (assumed it's already been decoded)
* @return Encrypted cipher text (not encoded)
* @throws GeneralSecurityException if something goes wrong during encryption
*/
@Throws(GeneralSecurityException::class)
fun encrypt(key: SecretKeySpec, iv: ByteArray, message: ByteArray): ByteArray
val cipher = Cipher.getInstance(AES_MODE)
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec)
return cipher.doFinal(message)
/**
* Decrypt and decode ciphertext using 256-bit AES with key generated from password
*
* @param password used to generated key
* @param base64EncodedCipherText the encrpyted message encoded with base64
* @return message in Plain text (String UTF-8)
* @throws GeneralSecurityException if there's an issue decrypting
*/
@Throws(GeneralSecurityException::class)
fun decrypt(password: String, base64EncodedCipherText: String): String
try
val key = generateKey(password)
val decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP)
val decryptedBytes = decrypt(key, IV_BYTES, decodedCipherText)
return String(decryptedBytes, charset(CHARSET))
catch (e: UnsupportedEncodingException)
throw GeneralSecurityException(e)
/**
* More flexible AES decrypt that doesn't encode
*
* @param key AES key typically 128, 192 or 256 bit
* @param iv Initiation Vector
* @param decodedCipherText in bytes (assumed it's already been decoded)
* @return Decrypted message cipher text (not encoded)
* @throws GeneralSecurityException if something goes wrong during encryption
*/
@Throws(GeneralSecurityException::class)
fun decrypt(key: SecretKeySpec, iv: ByteArray, decodedCipherText: ByteArray): ByteArray
val cipher = Cipher.getInstance(AES_MODE)
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec)
return cipher.doFinal(decodedCipherText)
使用方式:
// 加密结果
val textEncrypted = AESCryptUtil.encrypt("123456", "hello world")
// 解密结果
val textDecrypted = AESCryptUtil.decrypt("123456", textEncrypted)
以上是关于Android AES加密解密工具类的主要内容,如果未能解决你的问题,请参考以下文章