Kotlin 中的 AES 加密

Posted

技术标签:

【中文标题】Kotlin 中的 AES 加密【英文标题】:AES Encryption in Kotlin 【发布时间】:2021-09-28 04:31:27 【问题描述】:

android docs 给出以下 sn-p 以了解如何在 AES 中加密消息:

val plaintext: ByteArray = ...
val keygen = KeyGenerator.getInstance("AES")
keygen.init(256)
val key: SecretKey = keygen.generateKey()
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, key)
val ciphertext: ByteArray = cipher.doFinal(plaintext)
val iv: ByteArray = cipher.iv

执行此方法时出现此错误:

未解析的引用:密码

所以看起来“密码”对象不是本地的,但是我无法知道如何按照 Android 文档导入它。 如何设置我的项目以使用“密码”?

【问题讨论】:

javax.crypto.Cipher 是 JCE 的一部分,应该可用。 import javax.crypto.Cipher 不起作用吗?那么你的环境可能有问题。 有趣。看起来它会起作用,但是我不确定根据文档我应该如何了解它。另外,我什至不知道上面的代码是如何工作的。为什么将“明文”声明为 ByteArray?密码在哪里?为什么要在最后一步声明iv?最终加密发生在哪里? 我将编辑这个问题,因为我不知道如何使用上面的代码“sn-p”加密字符串 @metamonkey 请检查我的更新答案一次 :) 评论中的问题都可以从文档中得到解答(例如:为什么“明文”声明为ByteArray 因为doFinal() 期望@987654328 @) 或者可以用基本的密码学知识来回答(例如:为什么要在最后一步声明iv?因为解密需要它)。关于您问题中的 Edit:如果您想使用密码而不是密钥,则应使用基于密码的密钥派生函数,如 PBKDF2 或 Argon2。 【参考方案1】:

javax.crypto.Cipher 是 JCE 的一部分,应该可用。导入javax.crypto.Cipher 不起作用吗?那么你的环境可能有问题。

【讨论】:

【参考方案2】:

我不确定是否有必要使用 Cipher,以及我提供的解决方案是否是最好的方法,但我可以使用 AESencryptiondecryption 使用以下text 输入的代码,表示String

加密

// text

val aesEncrypt: AESEncrypt = AESEncrypt()
val encryptedByteArray = aesEncrypt.encrypt(text)
val baos_text = ByteArrayOutputStream()
val oosText = ObjectOutputStream(baos_text)
oosText.writeObject(encryptedByteArray)
val encryptedText = String(android.util.Base64.encode(baos_text.toByteArray(), android.util.Base64.DEFAULT))

// key

val key = aesEncrypt.mySecretKey
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(key)
val keyAsString = String(android.util.Base64.encode(baos.toByteArray(), android.util.Base64.DEFAULT))

// initialisation vector

val iv = aesEncrypt.myInitializationVector
val baosIV = ByteArrayOutputStream()
val oosIV = ObjectOutputStream(baosIV)
oosIV.writeObject(iv)
val initialisationVector = String(android.util.Base64.encode(baosIV.toByteArray(), android.util.Base64.DEFAULT))

解密

您必须保存 keyinitialisation vectorencrypted text 才能将其解密。

val initialisationVector = ... // get from wherever you saved it, local db, firebase...

val bytesIV = android.util.Base64.decode(iv, android.util.Base64.DEFAULT)
val oisIV = ObjectInputStream(ByteArrayInputStream(bytesIV))
val initializationVectorIV = oisIV.readObject() as ByteArray


val encryptedText = ... // get 

val bytesText = android.util.Base64.decode(encryptedText, android.util.Base64.DEFAULT)
val oisText = ObjectInputStream(ByteArrayInputStream(bytesText))
val textByteArray = oisText.readObject() as ByteArray


val key = ... // get your key
val bytesKey = android.util.Base64.decode(key, android.util.Base64.DEFAULT)
val oisKey = ObjectInputStream(ByteArrayInputStream(bytesKey))
val secretKeyObj = oisKey.readObject() as SecretKey


val aesDecrypt = AESDecrypt(secretKeyObj,initializationVectorIV)
val decryptedByteArray = aesDecrypt.decrypt(textByteArray)

val textAfterDecryption = decryptedByteArray.toString(charset("UTF-8"))

编辑

AES 帮助类:

import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey

class AESEncrypt 

    var mySecretKey: SecretKey? = null
    var myInitializationVector: ByteArray? = null

    fun encrypt(strToEncrypt: String): ByteArray 

        val plainText = strToEncrypt.toByteArray(Charsets.UTF_8)
        val keygen = KeyGenerator.getInstance("AES")
        keygen.init(256)

        val key = keygen.generateKey()
        mySecretKey = key

        val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
        cipher.init(Cipher.ENCRYPT_MODE, key)
        val cipherText = cipher.doFinal(plainText)
        myInitializationVector = cipher.iv

        return cipherText
    


AES 解密助手

import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec

class AESDecrypt(private val mySecretKey: SecretKey?, private val initializationVector: ByteArray?) 

    fun decrypt(dataToDecrypt: ByteArray): ByteArray 
        val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
        val ivSpec = IvParameterSpec(initializationVector)
        cipher.init(Cipher.DECRYPT_MODE, mySecretKey, ivSpec)
        val cipherText = cipher.doFinal(dataToDecrypt)

        return cipherText
    


如果您还需要任何帮助,请告诉我们:)

【讨论】:

这是什么类型的 AES?即分组密码?什么填充?等等。另外,保存初始化向量? imo这没有意义。 好吧,我的错,这是一个旧代码,我忘记了这个助手类确实是由我创建的,是的,它确实使用了Cipher。让我更新我的答案 您可以编辑答案以显示密码输入,而不仅仅是生成新密钥吗? 对于 CBC 模式的攻击者来说,IV “just”需要完全不可预测,通常只是直接在密文前面加上 16 个随机字节。 在用于加密的那一刻,它需要让攻击者无法预测。将 iv 和消息存储在同一数据库的不同位置是可以的。但是对于密钥:密钥管理是整本书的主题;将密钥存储在密文旁边显然不是一个好主意;几乎按照定义,密钥应该比密文更难访问(否则:你要保护什么?)。

以上是关于Kotlin 中的 AES 加密的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin ECC 加密

Swift Sha512 加密(翻译 Kotlin 代码)

密码学RSA加密 kotlin实现方法(支持任意字节长度)

Kotlin 中的多平台 InputStream 替代方案?

Kotlin中级- - - 初识Kotlin中的对象.md

Kotlin——数组