Created
December 20, 2018 03:43
-
-
Save XuNeal/bbcdd44f9f4825525ee9de89f02ed4ed to your computer and use it in GitHub Desktop.
Show How to use keystore in android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val HEX = "37158399CB98DCD114D873E06EBF4BCC" | |
val KEYSTORE_ALIAS = "imTokenDerivedKey" | |
val ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore" | |
val TAG = "Keychain" | |
var isEncrypt: Boolean = true | |
var encrypted: String = "" | |
var iv: ByteArray = ByteArray(0) | |
// 指纹验证相关回调方法 | |
class AuthenticationCallback(val textView: TextView) : | |
BiometricPrompt.AuthenticationCallback() { | |
fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?) { | |
// 只有指纹验证成功之后才可以获取相应的cipher,里面已经包含私钥 | |
val cipher = result!!.cryptoObject.cipher!! | |
try { | |
val stringBuilder = StringBuilder() | |
if (isEncrypt) { | |
stringBuilder.append("Encrypt: \n") | |
val b = hexStringToByteArray(HEX) | |
encrypted = byteArrayToHex(cipher.doFinal(b)) | |
iv = cipher.iv | |
stringBuilder.append("Data: ${HEX} \n") | |
stringBuilder.append("IV: ${byteArrayToHex(cipher.iv)} \n") | |
stringBuilder.append("CipherText: ${encrypted} \n") | |
} else { | |
stringBuilder.append("Decrypt: \n") | |
val b = hexStringToByteArray(encrypted) | |
val original = byteArrayToHex(cipher.doFinal(b)) | |
stringBuilder.append("IV: ${byteArrayToHex(cipher.iv)}\n") | |
stringBuilder.append("Decrypt Result: ${original} \n") | |
} | |
textView.text = stringBuilder.toString() | |
} catch (error: java.lang.Exception) { | |
Log.e("FP", error.message) | |
} | |
} | |
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { | |
super.onAuthenticationError(errorCode, errString) | |
Log.e(TAG, errString.toString()) | |
} | |
override fun onAuthenticationFailed() { | |
super.onAuthenticationFailed() | |
Log.e(TAG, "Authentication Failed") | |
} | |
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) { | |
super.onAuthenticationSucceeded(result) | |
} | |
} | |
class MainActivity : AppCompatActivity() { | |
@RequiresApi(Build.VERSION_CODES.M) | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
// 加载keystore实例 | |
val keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER) | |
keystore.load(null) | |
// 根据aes/cbc/pkcs7算法初始化cipher,此时尚未填充私钥 | |
val cipher = Cipher.getInstance( | |
KeyProperties.KEY_ALGORITHM_AES + "/" | |
+ KeyProperties.BLOCK_MODE_CBC + "/" | |
+ KeyProperties.ENCRYPTION_PADDING_PKCS7 | |
) | |
fun doCrypt() { | |
try { | |
// 获取指定alias的私钥,SecretKey类型的私钥无法看到原始内容 | |
val key = keystore.getKey(KEYSTORE_ALIAS, null) as SecretKey | |
val factory = SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore"); | |
val keyInfo = factory.getKeySpec(key, KeyInfo::class.java) as KeyInfo | |
// 确认该私钥是存储在SE设备中,否则抛出异常 | |
if (keyInfo.isInsideSecureHardware || isInSimulator()) { | |
throw Exception("Your device not support SE or TEE") | |
} | |
if (isEncrypt) cipher.init(Cipher.ENCRYPT_MODE, key) else cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv)) | |
val cancellationSignal = CancellationSignal() | |
val onClickListener = DialogInterface.OnClickListener {dialog, witch -> Log.i(TAG, "User cancel") } | |
// 构造指纹签名提示框 | |
val bioPrompt = BiometricPrompt.Builder(this) | |
.setTitle("Verify your Fingerprint") | |
.setDescription("Pls touch your Fingerprint sensor") | |
.setNegativeButton("Use Password", mainExecutor, onClickListener) | |
.build() | |
// val dialog = AlertDialog.Builder(this) | |
// .setTitle("Input FP") | |
// .show() | |
val callback = AuthenticationCallback(tvLog) | |
bioPrompt.authenticate(BiometricPrompt.CryptoObject(cipher), cancellationSignal, mainExecutor,callback) | |
// FingerprintManagerCompat.from(this).authenticate( | |
// FingerprintManagerCompat.CryptoObject(cipher), | |
// 0, // flags | |
// cancellationSignal, // authentication callback | |
// callback, | |
// null | |
// ) | |
} catch (e: Exception) { | |
Log.e(TAG, e.localizedMessage) | |
} | |
} | |
btnEncrypt.setOnClickListener { | |
isEncrypt = true | |
val aliases = keystore.aliases().toList() | |
// 判断之前是否生成或相应的alias的私钥,没有则申请系统生成 | |
if (!aliases.contains(KEYSTORE_ALIAS)) { | |
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE_PROVIDER) | |
keyGenerator.init( | |
KeyGenParameterSpec.Builder( | |
KEYSTORE_ALIAS, | |
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT | |
) | |
.setBlockModes(KeyProperties.BLOCK_MODE_CBC) | |
.setUserAuthenticationRequired(true) // 指定鉴权之后才能使用 | |
.setEncryptionPaddings( | |
KeyProperties.ENCRYPTION_PADDING_PKCS7 | |
) | |
.build() | |
) | |
keyGenerator.generateKey() | |
} | |
doCrypt() | |
} | |
btnDecrypt.setOnClickListener { | |
isEncrypt = false | |
doCrypt() | |
} | |
} | |
fun isInSimulator(): Boolean { | |
return android.os.Build.MODEL.contains("google_sdk") || | |
android.os.Build.MODEL.contains("Emulator") | |
} | |
} | |
// utils method | |
fun byteArrayToHex(a: ByteArray): String { | |
val sb = StringBuilder(a.size * 2) | |
for (b in a) | |
sb.append(String.format("%02x", b)) | |
return sb.toString() | |
} | |
fun hexStringToByteArray(s: String): ByteArray { | |
val b = ByteArray(s.length / 2) | |
for (i in b.indices) { | |
val index = i * 2 | |
val v = Integer.parseInt(s.substring(index, index + 2), 16) | |
b[i] = v.toByte() | |
} | |
return b | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment