我正在学习Android中的密码学。尝试加密/解密用户的密码。加密方法工作得很好,但解密方法总是出错。
下面是CryptoManager类的代码:
package edu.xfoleks.komodoro.presentation.utils
import android.security.keystore.KeyProperties
import android.security.keystore.KeyProtection
import android.util.Base64
import java.nio.charset.StandardCharsets
import java.security.KeyStore
import java.util.Calendar
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.spec.IvParameterSpec
class CryptoManager {
init {
generateSecretKey()
}
private fun generateSecretKey() {
val keyStore = KeyStore.getInstance(KEY_STORE).apply {
load(null)
}
val keyGenerator = KeyGenerator.getInstance(ALGORITHM).apply {
init(KEY_SIZE)
}
val secretKey = keyGenerator.generateKey()
val startTime = Calendar.getInstance()
val expireTime = Calendar.getInstance().apply {
add(Calendar.YEAR, 2)
}
val entry = KeyStore.SecretKeyEntry(secretKey)
val protectionParameter =
KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setKeyValidityStart(startTime.time)
.setKeyValidityEnd(expireTime.time)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(PADDING)
.build()
keyStore.setEntry(ALIAS, entry, protectionParameter)
}
fun encrypt(plainText: String): String {
val keyStore = KeyStore.getInstance(KEY_STORE).apply { load(null) }
val secretKey = keyStore.getKey(ALIAS, null)
return try {
val cipher = Cipher.getInstance(TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val cipherText = Base64.encodeToString(cipher.doFinal(plainText.toByteArray()), Base64.DEFAULT)
val iv = Base64.encodeToString(cipher.iv, Base64.DEFAULT)
"${cipherText}.$iv"
} catch (ex: Exception) {
ex.printStackTrace()
""
}
}
fun decrypt(cipherText: String): String? {
val keyStore = KeyStore.getInstance(KEY_STORE).apply { load(null) }
val secretKey = keyStore.getKey(ALIAS, null)
val array = cipherText.split(".")
val cipherData = Base64.decode(array[0], Base64.DEFAULT)
val iv = Base64.decode(array[1], Base64.DEFAULT)
return try {
val cipher = Cipher.getInstance(TRANSFORMATION)
val spec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
val clearText = cipher.doFinal(cipherData)
val encoded = String(clearText, 0, clearText.size, StandardCharsets.UTF_8)
encoded
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
companion object {
private const val KEY_STORE = "AndroidKeyStore"
private const val KEY_SIZE = 256
private const val ALIAS = "secret"
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"
}
}
使用解密/加密的类:
class LogInViewModel @Inject constructor(
private val userInteractor: UserInteractor
) : ViewModel() {
private var _user = MutableLiveData<UserInfoItem>()
private fun decryptPassword(password: String): String? {
val cryptoManager = CryptoManager()
return cryptoManager.decrypt(password)
}
fun logIn(userName: String, password: String): Boolean {
val user = UserInfoItem(userName, password)
_user.value = user
val existingUser = userInteractor.getUser(user.toUser()).toUserInfoItem()
val decryptedPassword = decryptPassword(existingUser.password)
return user.userName.equals(existingUser.userName) && user.password.equals(decryptedPassword)
}
}
class RegistrationViewModel @Inject constructor(
private val userInteractor: UserInteractor
) : ViewModel() {
private var _user = MutableLiveData<UserInfoItem>()
private fun encryptPassword(password: String): String {
if (password.isEmpty()) {
throw EmptyPasswordException()
}
val cryptoManager = CryptoManager()
return cryptoManager.encrypt(password)
}
fun registerUser(userName: String, password: String) {
if (userName.isEmpty() || password.isEmpty()) { throw UserNotCreatedException() }
val encryptedPassword = encryptPassword(password)
val user = UserInfoItem(userName, encryptedPassword)
_user.value = user
userInteractor.registerUser(user.toUser())
}
}
错误消息:
javax.crypto.BadPaddingException
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:609)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
at edu.xfoleks.komodoro.presentation.utils.CryptoManager.decrypt(CryptoManager.kt:83)
at edu.xfoleks.komodoro.presentation.screens.viewmodels.LogInViewModel.decryptPassword(LogInViewModel.kt:22)
at edu.xfoleks.komodoro.presentation.screens.viewmodels.LogInViewModel.logIn(LogInViewModel.kt:29)
at edu.xfoleks.komodoro.presentation.screens.fragments.LogInFragment.onCreateView$lambda$5(LogInFragment.kt:47)
at edu.xfoleks.komodoro.presentation.screens.fragments.LogInFragment.$r8$lambda$NaDtgxEMrBbrOBDtRje0oMjYmGs(Unknown Source:0)
at edu.xfoleks.komodoro.presentation.screens.fragments.LogInFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7506)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1211)
at android.view.View.performClickInternal(View.java:7483)
at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
at android.view.View$PerformClick.run(View.java:29334)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Caused by: android.security.KeyStoreException: Invalid argument (internal Keystore code: -38 message: In KeystoreOperation::finish
Caused by:
0: In finish: KeyMint::finish failed.
1: Error::Km(ErrorCode(-38))) (public error code: 10 internal Keystore code: -38)
at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:369)
at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:603)
... 20 more
我曾试图保存加密的所有设置,然后用它来解密,但它什么也没给我。
1条答案
按热度按时间4c8rllxm1#
为了密码的安全性,我们需要使用哈希,而不是加密/解密。您可以查找并实现bcrypt,scrypt或Argon 2等算法。