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)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是 Java 中使用 AES 加密解密工具类,其中包括了 AES/CBC/PKCS5Padding 算法的加密解密方法: ```java import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; public class AESUtil { private static final String ALGORITHM_AES = "AES"; private static final String CIPHER_AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding"; private static final String IV = "0123456789abcdef"; // 16 位 /** * AES 加密 * * @param data 需要加密的数据 * @param key 密钥字符串(16、24 或 32 位) * @return 加密后的字符串 */ public static String encryptByAES(String data, String key) { try { byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_AES); Cipher cipher = Cipher.getInstance(CIPHER_AES_CBC_PKCS5PADDING); IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encryptedBytes); } catch (Exception e) { e.printStackTrace(); } return null; } /** * AES 解密 * * @param data 需要解密的数据 * @param key 密钥字符串(16、24 或 32 位) * @return 解密后的字符串 */ public static String decryptByAES(String data, String key) { try { byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_AES); Cipher cipher = Cipher.getInstance(CIPHER_AES_CBC_PKCS5PADDING); IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] encryptedBytes = Base64.getDecoder().decode(data); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { e.printStackTrace(); } return null; } } ``` 使用示例: ```java public static void main(String[] args) { String data = "Hello, world!"; String key = "1234567890123456"; String encryptedData = AESUtil.encryptByAES(data, key); System.out.println("加密后的数据:" + encryptedData); String decryptedData = AESUtil.decryptByAES(encryptedData, key); System.out.println("解密后的数据:" + decryptedData); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值