正在初始化 FingerpringManager.Crypto 对象,获取不受 AndroidKeyStore 提供程序支持的 Crypto 原语?
Initializing FingerpringManager.Crypto Object, getting Crypto primitive not backed by AndroidKeyStore provider?
我正在使用 Android FingerPrintManager
API 并使用 KeyPairGenerator 创建密钥对,我想使用 public 密钥加密密码,然后在用户通过身份验证时解密通过输入 fingerPrint 但是我 运行 我的项目很快就崩溃并给出
Caused by: java.lang.IllegalArgumentException: Crypto primitive not
backed by AndroidKeyStore provider
我使用了这里的代码:
这个 post 说他能够进行加密和解密,并且 di 遵循相同的代码和步骤。
这是我的代码
public KeyStore getKeyStore() {
try {
return KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException exception) {
throw new RuntimeException("Failed to get an instance of KeyStore", exception);
}
}
public KeyPairGenerator getKeyPairGenerator() {
try {
return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException exception) {
throw new RuntimeException("Failed to get an instance of KeyPairGenerator", exception);
}
}
public Cipher getCipher() {
try {
return Cipher.getInstance("RSA");
} catch (NoSuchAlgorithmException | NoSuchPaddingException exception) {
throw new RuntimeException("Failed to get an instance of Cipher", exception);
}
}
private void createKeyPair() {
try {
mKeyPairGenerator = getKeyPairGenerator();
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationRequired(true)
.build());
mKeyPairGenerator.generateKeyPair();
} catch (InvalidAlgorithmParameterException exception) {
throw new RuntimeException(exception);
}
}
private boolean initCipher(int opmode) {
try {
mKeyStore = getKeyStore();
mKeyStore.load(null);
mCipher = getCipher();
if (opmode == Cipher.ENCRYPT_MODE) {
PublicKey key = mKeyStore.getCertificate(KEY_NAME).getPublicKey();
PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm())
.generatePublic(new X509EncodedKeySpec(key.getEncoded()));
OAEPParameterSpec spec = new OAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
mCipher.init(opmode, unrestricted, spec);
} else {
PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
mCipher.init(opmode, key);
}
return true;
} catch (KeyPermanentlyInvalidatedException exception) {
return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException
| IOException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | InvalidAlgorithmParameterException exception) {
throw new RuntimeException("Failed to initialize Cipher", exception);
}
}
private void encrypt(String password) {
try {
initCipher(Cipher.ENCRYPT_MODE);
byte[] bytes = mCipher.doFinal(password.getBytes());
enrcyptedPassword = Base64.encodeToString(bytes, Base64.NO_WRAP);
Log.d("EncryptedText", enrcyptedPassword);
} catch (IllegalBlockSizeException | BadPaddingException exception) {
throw new RuntimeException("Failed to encrypt password", exception);
}
}
private String decryptPassword(Cipher cipher) {
try {
initCipher(Cipher.DECRYPT_MODE);
byte[] bytes = Base64.decode(enrcyptedPassword, Base64.NO_WRAP);
return new String(cipher.doFinal(bytes));
} catch (IllegalBlockSizeException | BadPaddingException | RuntimeException exception) {
throw new RuntimeException("Failed to decrypt password", exception);
}
}
我从这里开始初始化我的 CryptoObject:
createKeyPair();
if (initCipher(Cipher.ENCRYPT_MODE)) {
mCryptoObject = new FingerprintManager.CryptoObject
(mCipher);
encrypt("1111");
if (!isFingerprintAuthAvailable()) {
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, this, null);
我在这一行遇到异常:
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, this, null);
在加密模式下,您正在使用 public 密钥 (unrestricted
) 初始化 Cipher
实例,该密钥不是 Android 密钥库密钥。要解决此问题,您可以使用 Android Keystore public 密钥(即 mKeyStore.getCertificate(KEY_NAME).getPublicKey()
而不是 unrestricted
)对其进行初始化。
但是,不清楚通过对用户授权进行 public 密钥加密可以获得什么。任何人都可以在没有用户授权的情况下执行加密操作,因为加密使用 public 密钥,根据定义它不是秘密的。在非对称加密中,唯一涉及私钥(根据定义 public 不为人知)的操作是解密和签名。因此,通常只有对用户授权进行解密或签名才有意义。
@AlexKlyubin 是对的,您不需要使用指纹管理器进行加密,只需解密即可。为了加密文本,您需要做的就是调用上面的encrypt(String password)
方法。
对于解密,您应该使用 FingerprintManagerCompat
而不是 FingerprintManager
。为了监听指纹事件和解密密码,你需要扩展FingerprintManagerCompat.AuthenticationCallback
。我扩展了这个class,并实现了一个回调接口:
public class FingerprintAuthentication extends FingerprintManagerCompat.AuthenticationCallback {
private final Callback mCallback;
public FingerprintCallback(Callback callback) {
mCallback = callback;
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
mCallback.onAuthenticationSucceeded(result);
}
@Override
public void onAuthenticationHelp(int messageId, CharSequence message) {
mCallback.onAuthenticationHelp(messageId, message);
}
@Override
public void onAuthenticationError(int messageId, CharSequence message) {
mCallback.onAuthenticationError(messageId, message);
}
@Override
public void onAuthenticationFailed() {
mCallback.onAuthenticationFailed();
}
public interface Callback {
void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result);
void onAuthenticationHelp(int messageId, CharSequence message);
void onAuthenticationError(int messageId, CharSequence message);
void onAuthenticationFailed();
}
}
有了这个你可以在你的Fragment
或Activity
中实现Callback
接口,然后开始监听事件:
private void startListening(boolean cipher) {
Timber.v("Start listening for fingerprint input");
mCancellationSignal = new CancellationSignal();
if(cipher) {
mFingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(mCipher),
0, mCancellationSignal, new FingerprintAuthentication(this), null);
} else {
setStage(Stage.CREDENTIALS);
}
}
最后,指纹认证成功后才能解密密码:
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
try {
mPassword = decryptPassword(result.getCryptoObject().getCipher());
} catch (IllegalBlockSizeException | BadPaddingException exception) {
exception.printStackTrace();
}
}
基本上,当用户首次登录时,您希望向他们显示一个选项 "use fingerprint in the future":
如果用户选择这个选项并点击登录,这就是你调用encrypt()
的时候。然后,下次要求用户登录时,您将显示指纹对话框:
这是你打电话给 startListening(initializeCipher(Cipher.DECRYPT_MODE))
的时候。
我正在使用 Android FingerPrintManager
API 并使用 KeyPairGenerator 创建密钥对,我想使用 public 密钥加密密码,然后在用户通过身份验证时解密通过输入 fingerPrint 但是我 运行 我的项目很快就崩溃并给出
Caused by: java.lang.IllegalArgumentException: Crypto primitive not backed by AndroidKeyStore provider
我使用了这里的代码:
public KeyStore getKeyStore() {
try {
return KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException exception) {
throw new RuntimeException("Failed to get an instance of KeyStore", exception);
}
}
public KeyPairGenerator getKeyPairGenerator() {
try {
return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException exception) {
throw new RuntimeException("Failed to get an instance of KeyPairGenerator", exception);
}
}
public Cipher getCipher() {
try {
return Cipher.getInstance("RSA");
} catch (NoSuchAlgorithmException | NoSuchPaddingException exception) {
throw new RuntimeException("Failed to get an instance of Cipher", exception);
}
}
private void createKeyPair() {
try {
mKeyPairGenerator = getKeyPairGenerator();
mKeyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationRequired(true)
.build());
mKeyPairGenerator.generateKeyPair();
} catch (InvalidAlgorithmParameterException exception) {
throw new RuntimeException(exception);
}
}
private boolean initCipher(int opmode) {
try {
mKeyStore = getKeyStore();
mKeyStore.load(null);
mCipher = getCipher();
if (opmode == Cipher.ENCRYPT_MODE) {
PublicKey key = mKeyStore.getCertificate(KEY_NAME).getPublicKey();
PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm())
.generatePublic(new X509EncodedKeySpec(key.getEncoded()));
OAEPParameterSpec spec = new OAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
mCipher.init(opmode, unrestricted, spec);
} else {
PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
mCipher.init(opmode, key);
}
return true;
} catch (KeyPermanentlyInvalidatedException exception) {
return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException
| IOException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | InvalidAlgorithmParameterException exception) {
throw new RuntimeException("Failed to initialize Cipher", exception);
}
}
private void encrypt(String password) {
try {
initCipher(Cipher.ENCRYPT_MODE);
byte[] bytes = mCipher.doFinal(password.getBytes());
enrcyptedPassword = Base64.encodeToString(bytes, Base64.NO_WRAP);
Log.d("EncryptedText", enrcyptedPassword);
} catch (IllegalBlockSizeException | BadPaddingException exception) {
throw new RuntimeException("Failed to encrypt password", exception);
}
}
private String decryptPassword(Cipher cipher) {
try {
initCipher(Cipher.DECRYPT_MODE);
byte[] bytes = Base64.decode(enrcyptedPassword, Base64.NO_WRAP);
return new String(cipher.doFinal(bytes));
} catch (IllegalBlockSizeException | BadPaddingException | RuntimeException exception) {
throw new RuntimeException("Failed to decrypt password", exception);
}
}
我从这里开始初始化我的 CryptoObject:
createKeyPair();
if (initCipher(Cipher.ENCRYPT_MODE)) {
mCryptoObject = new FingerprintManager.CryptoObject
(mCipher);
encrypt("1111");
if (!isFingerprintAuthAvailable()) {
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, this, null);
我在这一行遇到异常:
mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, this, null);
在加密模式下,您正在使用 public 密钥 (unrestricted
) 初始化 Cipher
实例,该密钥不是 Android 密钥库密钥。要解决此问题,您可以使用 Android Keystore public 密钥(即 mKeyStore.getCertificate(KEY_NAME).getPublicKey()
而不是 unrestricted
)对其进行初始化。
但是,不清楚通过对用户授权进行 public 密钥加密可以获得什么。任何人都可以在没有用户授权的情况下执行加密操作,因为加密使用 public 密钥,根据定义它不是秘密的。在非对称加密中,唯一涉及私钥(根据定义 public 不为人知)的操作是解密和签名。因此,通常只有对用户授权进行解密或签名才有意义。
@AlexKlyubin 是对的,您不需要使用指纹管理器进行加密,只需解密即可。为了加密文本,您需要做的就是调用上面的encrypt(String password)
方法。
对于解密,您应该使用 FingerprintManagerCompat
而不是 FingerprintManager
。为了监听指纹事件和解密密码,你需要扩展FingerprintManagerCompat.AuthenticationCallback
。我扩展了这个class,并实现了一个回调接口:
public class FingerprintAuthentication extends FingerprintManagerCompat.AuthenticationCallback {
private final Callback mCallback;
public FingerprintCallback(Callback callback) {
mCallback = callback;
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
mCallback.onAuthenticationSucceeded(result);
}
@Override
public void onAuthenticationHelp(int messageId, CharSequence message) {
mCallback.onAuthenticationHelp(messageId, message);
}
@Override
public void onAuthenticationError(int messageId, CharSequence message) {
mCallback.onAuthenticationError(messageId, message);
}
@Override
public void onAuthenticationFailed() {
mCallback.onAuthenticationFailed();
}
public interface Callback {
void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result);
void onAuthenticationHelp(int messageId, CharSequence message);
void onAuthenticationError(int messageId, CharSequence message);
void onAuthenticationFailed();
}
}
有了这个你可以在你的Fragment
或Activity
中实现Callback
接口,然后开始监听事件:
private void startListening(boolean cipher) {
Timber.v("Start listening for fingerprint input");
mCancellationSignal = new CancellationSignal();
if(cipher) {
mFingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(mCipher),
0, mCancellationSignal, new FingerprintAuthentication(this), null);
} else {
setStage(Stage.CREDENTIALS);
}
}
最后,指纹认证成功后才能解密密码:
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
try {
mPassword = decryptPassword(result.getCryptoObject().getCipher());
} catch (IllegalBlockSizeException | BadPaddingException exception) {
exception.printStackTrace();
}
}
基本上,当用户首次登录时,您希望向他们显示一个选项 "use fingerprint in the future":
如果用户选择这个选项并点击登录,这就是你调用encrypt()
的时候。然后,下次要求用户登录时,您将显示指纹对话框:
这是你打电话给 startListening(initializeCipher(Cipher.DECRYPT_MODE))
的时候。