确认凭证
主要目的:设置不用验证时间 设置为30秒,当超过30秒后则需要重新验证身份才能操作。
您的应用可以根据用户在多久之前最后一次解锁设备来验证其身份。此功能让用户不必费心记忆应用特定密码,您也无需实现自己的身份验证用户界面。您的应用应当利用此功能并结合实现公钥或私钥,以进行用户身份验证。
要设置成功验证用户身份后可再次使用同一密钥的超时持续时间,请在设置 KeyGenerator 或 KeyPairGenerator 时调用新增的 setUserAuthenticationValidityDurationSeconds() 方法。
要设置成功验证用户身份后可再次使用同一密钥的超时持续时间,请在设置 KeyGenerator 或 KeyPairGenerator 时调用新增的 setUserAuthenticationValidityDurationSeconds() 方法。
避免过多显示重新验证对话框 -- 您的应用应尝试先使用加密对象,如果超时到期,请使用 createConfirmDeviceCredentialIntent() 方法在您的应用内重新验证用户身份。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="button"
android:text="输入密码确认凭据"/>
<TextView
android:id="@+id/already_has_valid_device_credential_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
/>
</LinearLayout>
package th.zxq.com.android60;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
/**
* Created by Administrator on 2017/8/14.
*/
public class SurePJActivity extends AppCompatActivity {
private static final String KEY_NAME = "my_key";//我们的钥匙在Android钥匙商店的别名。
private static final int AUTHENTICATION_DURATION_SECONDS = 30;//设置多少秒后重新验证身份
private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
private KeyguardManager mKeyguardManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_surepj);
Button button= (Button) findViewById(R.id.button);
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
//检测是否设置安全密码或者指纹,有无安全保护。
if (!mKeyguardManager.isKeyguardSecure()) {
Toast.makeText(this, "去设置->安全录入密码或者指纹", Toast.LENGTH_LONG).show();
button.setEnabled(false);
return;
}
createKey();
}
public void button(View view)
{
tryEncrypt();
}
/**
* Tries to encrypt some data with the generated key in {@link #createKey} which is
* only works if the user has just authenticated via device credentials.
* 尝试使用{@link #createKey}中生成的密钥加密某些数据,只有在用户刚刚通过设备凭据进行身份验证时,该数据才有效。
*/
private boolean tryEncrypt() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
// Try encrypting something, it will only work if the user authenticated within
// the last AUTHENTICATION_DURATION_SECONDS seconds.
// 尝试加密某些东西,只有用户在最后一次认证30秒内进行身份验证,才能工作。超过30秒后在操作就会报异常
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
cipher.doFinal(SECRET_BYTE_ARRAY);
// If the user has recently authenticated, you will reach here.
//如果用户最近已通过身份验证,您将到达这里。
showAlreadyAuthenticated();
return true;
} catch (UserNotAuthenticatedException e) {
// User is not authenticated, let's authenticate with device credentials.
showAuthenticationScreen();
return false;
} catch (KeyPermanentlyInvalidatedException e) {
// This happens if the lock screen has been disabled or reset after the key was
// generated after the key was generated.
//如果在生成密钥后锁定屏幕已被禁用或复位,则会发生这种情况。
Toast.makeText(this, "Keys are invalidated after created. Retry the purchase\n"
+ e.getMessage(),
Toast.LENGTH_LONG).show();
return false;
} catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
CertificateException | UnrecoverableKeyException | IOException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
/**
* 在Android Key Store中创建一个对称密钥,只能在用户在最近X秒内通过身份验证身份验证后使用。
*/
public void createKey()
{
// 生成一个密钥来解密支付凭证,令牌等。
try {
KeyStore androidKeyStore = KeyStore.getInstance("AndroidKeyStore");
androidKeyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
//设置Android KeyStore中出现密钥的条目的别名,以及Builder的构造函数中的约束(目的)
keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
// Require that the user has unlocked in the last 30 seconds
//要求用户在过去30秒内解锁,6.0新api
.setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | NoSuchProviderException
| InvalidAlgorithmParameterException | KeyStoreException
| CertificateException | IOException e) {
throw new RuntimeException("Failed to create a symmetric key", e);
}
}
private void showAuthenticationScreen() {
//创建“确认凭据”屏幕。 您可以自定义标题和说明。
// Create the Confirm Credentials screen. You can customize the title and description. Or
//如果您将其留空,我们将为您提供一个通用的
// we will provide a generic one for you if you leave it null
Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
if (intent != null) {
startActivityForResult(intent, 1);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
// Challenge completed, proceed with using cipher
if (resultCode == RESULT_OK) {
if (tryEncrypt()) {
MyUitls.showToast(SurePJActivity.this,"验证通过了");
}
} else {
// The user canceled or didn’t complete the lock screen
// operation. Go to error/cancellation flow.
}
}
}
private void showAlreadyAuthenticated() {
TextView textView = (TextView) findViewById(
R.id.already_has_valid_device_credential_message);
textView.setVisibility(View.VISIBLE);
textView.setText(getString(R.string.already_confirmed_device_credentials_within_last_x_seconds, AUTHENTICATION_DURATION_SECONDS));
findViewById(R.id.button).setEnabled(false);
}
}
<resources>
<string name="app_name">Android6.0</string>
<string name="already_confirmed_device_credentials_within_last_x_seconds">验证通过后,%1$s 秒之内不用验证,锁屏秒数重置</string>
</resources>