我在KeyStore
仅在华为设备(特别是华为Mate 9)中实施其他设备时遇到了一些问题。
错误日志:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method 'int android.security.IKeystoreService.del(java.lang.String, int)' on a null object reference
at android.security.KeyStore.delete(KeyStore.java:186)
at android.security.Credentials.deletePrivateKeyTypeForAlias(Credentials.java:292)
at android.security.Credentials.deleteAllTypesForAlias(Credentials.java:251)
at android.security.keystore.AndroidKeyStoreKeyPairGeneratorSpi.generateKeyPair(AndroidKeyStoreKeyPairGeneratorSpi.java:464)
at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:699)
at com.example.utility.crypto.KeyStoreHelper.createKeys(Unknown Source)
at com.example.utility.crypto.KeyStoreHelper.createKeys(Unknown Source)
at com.example.activities.register.SplashActivity$1.run(Unknown Source)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:255)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:776)
KeyStoreHelper文件
public class KeyStoreHelper {
public static final String TAG = "KeyStoreHelper";
/**
* Creates a public and private key and stores it using the Android Key
* Store, so that only this application will be able to access the keys.
*/
public static void createKeys(Context context, String alias) throws NoSuchProviderException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (!isSigningKey(alias)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
createKeysM(alias, false);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
createKeysJBMR2(context, alias);
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
static void createKeysJBMR2(Context context, String alias) throws NoSuchProviderException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException {
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 30);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
// You'll use the alias later to retrieve the key. It's a key
// for the key!
.setAlias(alias)
.setSubject(new X500Principal("CN=" + alias))
.setSerialNumber(BigInteger.valueOf(Math.abs(alias.hashCode())))
// Date range of validity for the generated pair.
.setStartDate(start.getTime()).setEndDate(end.getTime())
.build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(
SecurityConstants.TYPE_RSA,
SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
kpGenerator.initialize(spec);
KeyPair kp = kpGenerator.generateKeyPair();
Log.d(TAG, "Public Key is: " + kp.getPublic().toString());
}
@TargetApi(Build.VERSION_CODES.M)
static void createKeysM(String alias, boolean requireAuth) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
alias,KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(1024, F4))
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
// Only permit the private key to be used if the user authenticated
// within the last five minutes.
.setUserAuthenticationRequired(requireAuth)
.build());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Log.d(TAG, "Public Key is: " + keyPair.getPublic().toString());
} catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
/**
* JBMR2+ If Key with the default alias exists, returns true, else false.
* on pre-JBMR2 returns true always.
*/
public static boolean isSigningKey(String alias) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
try {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
keyStore.load(null);
return keyStore.containsAlias(alias);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
return false;
}
} else {
return false;
}
}
/**
* Returns the private key signature on JBMR2+ or else null.
*/
public static String getSigningKey(String alias) throws CertificateEncodingException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Certificate cert = getPrivateKeyEntry(alias).getCertificate();
if (cert == null) {
return null;
}
return Base64.encodeToString(cert.getEncoded(), Base64.NO_WRAP);
} else {
return null;
}
}
private static KeyStore.PrivateKeyEntry getPrivateKeyEntry(String alias) {
try {
KeyStore ks = KeyStore
.getInstance(KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
ks.load(null);
KeyStore.Entry entry = ks.getEntry(alias, null);
if (entry == null) {
LogUtils.LOGD(TAG, "No key found under alias: " + alias);
LogUtils.LOGD(TAG, "Exiting signData()...");
return null;
}
if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
LogUtils.LOGD(TAG, "Not an instance of a PrivateKeyEntry");
LogUtils.LOGD(TAG, "Exiting signData()...");
return null;
}
return (KeyStore.PrivateKeyEntry) entry;
} catch (Exception e) {
LogUtils.LOGE(TAG, e.getMessage(), e);
return null;
}
}
public static String encrypt(String alias, String plaintext) {
try {
PublicKey publicKey = getPrivateKeyEntry(alias).getCertificate().getPublicKey();
Cipher cipher = getCipher();
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeToString(cipher.doFinal(plaintext.getBytes()), Base64.NO_WRAP);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String alias, String ciphertext) {
try {
PrivateKey privateKey = getPrivateKeyEntry(alias).getPrivateKey();
Cipher cipher = getCipher();
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decode(ciphertext, Base64.NO_WRAP)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
return Cipher.getInstance(
String.format("%s/%s/%s",
SecurityConstants.TYPE_RSA,
SecurityConstants.BLOCKING_MODE,
SecurityConstants.PADDING_TYPE));
}
public interface SecurityConstants {
String KEYSTORE_PROVIDER_ANDROID_KEYSTORE = "AndroidKeyStore";
String TYPE_RSA = "RSA";
String PADDING_TYPE = "PKCS1Padding";
String BLOCKING_MODE = "NONE";
String SIGNATURE_SHA256withRSA = "SHA256withRSA";
String SIGNATURE_SHA512withRSA = "SHA512withRSA";
}
}