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 类支持设置的加密算法:

Android AES加密解密工具类_第1张图片

上面代码中 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")
                }
            }
        }
    }
}

Android AES加密解密工具类_第2张图片

下面是另一个工具类,在其他地方看到的,实现方式也是类似,这个没有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,android,AES加密/解密,加密,解密,AES)