gpt4 book ai didi

kotlin 中的 Android keystore

转载 作者:行者123 更新时间:2023-12-03 21:03:30 25 4
gpt4 key购买 nike

我正在开发一个需要非常安全的应用程序,因此我们希望使用 Android Keystore 来帮助使某些事情变得更安全,例如后端的 accessTokens。

我已经尝试过实现其中的一些,它似乎可以工作,但我主要是根据示例工作,主要是 Securely Storing Keys in Android Keystore

这是我的一些代码:

object AndroidKeyStore {
const val ANDROID_KEY_STORE = "AndroidKeyStore"
const val AES_MODE = "AES/GCM/NoPadding"
private var iv: ByteArray = byteArrayOf(55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44)

//IS IT SAFE TO HAVE THIS HERE?
const val SECRET_ALIAS = "TEST"

private fun generateSecretKey(keyAlias: String): SecretKey {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
val spec = KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()

keyGenerator.init(spec)
return keyGenerator.generateKey()
}

private fun getSecretKey(keyAlias: String): SecretKey {
val keyStore = KeyStore.getInstance(ANDROID_KEY_STORE).apply { load(null) }
if (keyStore.getEntry(keyAlias, null) != null) {
val secretKeyEntry = keyStore.getEntry(keyAlias, null) as KeyStore.SecretKeyEntry
return secretKeyEntry.secretKey ?: generateSecretKey(keyAlias)
}
return generateSecretKey(keyAlias)
}

fun encrypt(data: String): String {
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(SECRET_ALIAS), GCMParameterSpec(128, iv))
iv = cipher.iv
val encodedBytes = cipher.doFinal(data.toByteArray())
return Base64.encodeToString(encodedBytes, Base64.NO_WRAP)
}

fun decrypt(encrypted: String): String {
val cipher = Cipher.getInstance(AES_MODE)
val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(SECRET_ALIAS), spec)
val encodedBytes = Base64.decode(encrypted, Base64.NO_WRAP)
val decoded = cipher.doFinal(encodedBytes)
return String(decoded, Charsets.UTF_8)
}

}

这是我的 keystore 代码,它似乎可以加密/解密东西。但是我很难准确地理解 Secret 别名是什么以及将它安全地放在哪里。

同时,我也在使用安全的偏好商店。所以我的想法是,将 accessToken 保存在安全首选项存储中,但使用此 keystore 代码对其进行加密。

这是安全首选项存储的代码。
object SecurePreferenceUtils {

enum class SecurePreferenceKeys {
AccessToken, Test
}

fun putString(key: SecurePreferenceKeys, value: String) {
SecurePrefs.securePreferences.edit().putString(key.name, value).commit()
}

fun getString(key: SecurePreferenceKeys, defaultValue: String): String {
return SecurePrefs.securePreferences.getString(key.name, defaultValue) ?: ""
}

object SecurePrefs {
lateinit var securePreferences: SecurePreferences
}

最后,当我从后端获取 accessToken 时,我希望能够这样做。
SecurePreferenceUtils.putString(SecurePreferenceUtils.SecurePreferenceKeys.AccessToken, AndroidKeyStore.encrypt(""))

val token = AndroidKeyStore.decrypt(SecurePreferenceUtils.getString(SecurePreferenceUtils.SecurePreferenceKeys.AccessToken, AndroidKeyStore.encrypt("")))

希望我对我想去的地方有某种意义,我只是不太确定我在正确的道路上。

任何帮助表示赞赏!

最佳答案

别名 对一个键是唯一的,如果要存储用户名和密码,则需要 2 个唯一别名
在这里,我从资源 xml 文件中获取它
它是任何唯一的字符串值

<resources>
<string name="app_name">Data_Persistent</string>
<string name="app_package">com.aprorit.keystoreexample</string>
</resources>

If SDK is less than 18, here I'm using shared preferences sinceKeystore available 18 upward. And decoding to base64 and store it

If SDK is 18 and above, use Keystore


这是您访问它的方式
private const val PASSWORD_KEY = "password"
private val passwordStorage: PasswordStorageHelper = PasswordStorageHelper(context)

fun savePassword(password: String) {
passwordStorage.setData(PASSWORD_KEY, password.toByteArray())
}

fun getPassword() : String {
return String((passwordStorage.getData(PASSWORD_KEY) ?: ByteArray(0)))
}

fun removePassword() {
passwordStorage.remove(PASSWORD_KEY)
}
这是处理几乎所有事情的 PasswordStorageHelper 类
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.security.KeyChain
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyInfo
import android.security.keystore.KeyProperties
import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
import com.example.data_persistent.R
import java.io.IOException
import java.math.BigInteger
import java.security.*
import java.security.cert.CertificateException
import java.security.spec.AlgorithmParameterSpec
import java.security.spec.InvalidKeySpecException
import java.util.*
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import javax.security.auth.x500.X500Principal


class PasswordStorageHelper(context: Context) {
private val tag = "PasswordStorageHelper"

private val PREFS_NAME = "SecureData"

private var passwordStorage: PasswordStorageInterface?

init {
passwordStorage = if (Build.VERSION.SDK_INT < 18) {
PasswordStorageHelperSDK16();
} else {
PasswordStorageHelperSDK18();
}

var isInitialized: Boolean? = false;

try {
isInitialized = passwordStorage?.init(context);
} catch (ex: Exception) {
Log.e(tag, "PasswordStorage initialisation error:" + ex.message, ex);
}

if (isInitialized != true && passwordStorage is PasswordStorageHelperSDK18) {
passwordStorage = PasswordStorageHelperSDK16();
passwordStorage?.init(context);
}
}


fun setData(key: String?, data: ByteArray?) {
passwordStorage?.setData(key!!, data ?: ByteArray(0))
}

fun getData(key: String?): ByteArray? {
return passwordStorage?.getData(key ?: "")
}

fun remove(key: String?) {
passwordStorage?.remove(key ?: "")
}

private interface PasswordStorageInterface {
fun init(context: Context?): Boolean
fun setData(key: String?, data: ByteArray?)
fun getData(key: String?): ByteArray?
fun remove(key: String?)
}

private inner class PasswordStorageHelperSDK16 : PasswordStorageInterface {
private var preferences: SharedPreferences? = null

override fun init(context: Context?): Boolean {
preferences = context?.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
return true
}

override fun setData(key: String?, data: ByteArray?) {
if (data == null) return
val editor = preferences?.edit()
editor?.putString(key, Base64.encodeToString(data, Base64.DEFAULT))
editor?.apply()
}

override fun getData(key: String?): ByteArray? {
val res = preferences?.getString(key, null) ?: return null
return Base64.decode(res, Base64.DEFAULT)
}

override fun remove(key: String?) {
val editor = preferences?.edit()
editor?.remove(key)
editor?.apply()
}
}


private inner class PasswordStorageHelperSDK18 : PasswordStorageInterface {

private val KEY_ALGORITHM_RSA: String = "RSA";

private val KEYSTORE_PROVIDER_ANDROID_KEYSTORE: String = "AndroidKeyStore";
private val RSA_ECB_PKCS1_PADDING: String = "RSA/ECB/PKCS1Padding";

private var preferences: SharedPreferences? = null;
private var alias: String? = null;

@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
override fun init(context: Context?): Boolean {
preferences = context?.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
alias = context?.getString(R.string.app_package);

val ks: KeyStore?

try {
ks = KeyStore.getInstance(KEYSTORE_PROVIDER_ANDROID_KEYSTORE);

//Use null to load Keystore with default parameters.
ks?.load(null);

// Check if Private and Public already keys exists. If so we don't need to generate them again
val privateKey: Key? = ks?.getKey(alias, null);
if (privateKey != null && ks.getCertificate(alias) != null) {
val publicKey: PublicKey? = ks.getCertificate(alias).publicKey;
if (publicKey != null) {
// All keys are available.
return true;
}
}
} catch (ex: Exception) {
return false;
}

// Create a start and end time, for the validity range of the key pair that's about to be
// generated.
val start = GregorianCalendar();
val end = GregorianCalendar();
end.add(Calendar.YEAR, 10);

// Specify the parameters object which will be passed to KeyPairGenerator
val spec: AlgorithmParameterSpec?
if (Build.VERSION.SDK_INT < 23) {
spec = context?.let {
android.security.KeyPairGeneratorSpec.Builder(it)
// Alias - is a key for your KeyPair, to obtain it from Keystore in future.
.setAlias(alias ?: "")
// The subject used for the self-signed certificate of the generated pair
.setSubject(X500Principal("CN=$alias"))
// The serial number used for the self-signed certificate of the generated pair.
.setSerialNumber(BigInteger.valueOf(1337))
// Date range of validity for the generated pair.
.setStartDate(start.time).setEndDate(end.time)
.build()
};
} else {
spec = KeyGenParameterSpec.Builder(alias ?: "", KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build();
}

// Initialize a KeyPair generator using the the intended algorithm (in this example, RSA
// and the KeyStore. This example uses the AndroidKeyStore.
val kpGenerator: KeyPairGenerator
try {
kpGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
kpGenerator.initialize(spec);
// Generate private/public keys
kpGenerator.generateKeyPair();
} catch (e: Exception) {
when (e) {
is NoSuchAlgorithmException, is InvalidAlgorithmParameterException, is NoSuchProviderException -> {
try {
ks?.deleteEntry(alias)
} catch (e1: Exception) {
// Just ignore any errors here
}
}
}

}

// Check if device support Hardware-backed keystore
try {
var isHardwareBackedKeystoreSupported: Boolean? = null

isHardwareBackedKeystoreSupported = if (Build.VERSION.SDK_INT < 23) {
KeyChain.isBoundKeyAlgorithm(KeyProperties.KEY_ALGORITHM_RSA)
} else {
val privateKey: Key = ks.getKey(alias, null)
//KeyChain.isBoundKeyAlgorithm(KeyProperties.KEY_ALGORITHM_RSA)
val keyFactory: KeyFactory = KeyFactory.getInstance(privateKey.algorithm, "AndroidKeyStore")
val keyInfo: KeyInfo = keyFactory.getKeySpec(privateKey, KeyInfo::class.java)
keyInfo.isInsideSecureHardware
}
Log.d(tag, "Hardware-Backed Keystore Supported: $isHardwareBackedKeystoreSupported");
} catch (e: Exception) {
//KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | InvalidKeySpecException | NoSuchProviderException e
}

return true;
}

override fun setData(key: String?, data: ByteArray?) {
var ks: KeyStore? = null;
try {
ks = KeyStore.getInstance(KEYSTORE_PROVIDER_ANDROID_KEYSTORE);

ks.load(null);
if (ks.getCertificate(alias) == null) return;

val publicKey: PublicKey? = ks.getCertificate(alias).publicKey;

if (publicKey == null) {
Log.d(tag, "Error: Public key was not found in Keystore");
return;
}

val value: String = encrypt(publicKey, data);

val editor: SharedPreferences.Editor? = preferences?.edit();
editor?.putString(key, value);
editor?.apply();
} catch (e: Exception) {
when (e) {
is NoSuchAlgorithmException, is InvalidKeyException, is NoSuchPaddingException,
is IllegalBlockSizeException, is BadPaddingException, is NoSuchProviderException,
is InvalidKeySpecException, is KeyStoreException, is CertificateException, is IOException -> {

try {
ks?.deleteEntry(alias)
} catch (e1: Exception) {
// Just ignore any errors here
}
}
}
}
}


override fun getData(key: String?): ByteArray? {
var ks: KeyStore? = null;
try {
ks = KeyStore.getInstance(KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
ks.load(null);
val privateKey: Key = ks.getKey(alias, null);
return decrypt(privateKey, preferences?.getString(key, null));
} catch (e: Exception) {
//KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException
// | UnrecoverableEntryException | InvalidKeyException | NoSuchPaddingException
// | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
try {
ks?.deleteEntry(alias);
} catch (e1: Exception) {
// Just ignore any errors here
}
}
return null;
}


override fun remove(key: String?) {
val editor: SharedPreferences.Editor? = preferences?.edit();
editor?.remove(key);
editor?.apply();
}

private fun encrypt(encryptionKey: PublicKey, data: ByteArray?): String {
val cipher: Cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
val encrypted: ByteArray = cipher.doFinal(data);
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}

private fun decrypt(decryptionKey: Key, encryptedData: String?): ByteArray? {
if (encryptedData == null) return null;
val encryptedBuffer: ByteArray = Base64.decode(encryptedData, Base64.DEFAULT);
val cipher: Cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
cipher.init(Cipher.DECRYPT_MODE, decryptionKey);
return cipher.doFinal(encryptedBuffer);
}

}
}

关于kotlin 中的 Android keystore ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56132679/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com