android - Android 应用安全问题:Android API 级别 24 及以上的凭据存储用户名密码未加密
问题描述
为在此问题的早期描述中对细节的轻描淡写而道歉。这是更详细的问题:
我们的 Android 应用程序未能通过安全扫描,因为它使用 ECB 密码的高级加密标准 (AES) 加密算法加密用户名和密码并在本地存储。Sonarcube 参考声明我们需要使用没有填充的 Galois/Counter Mode (GCM)。但是,这不适用于 API 级别 24 及更高级别。
我们正在尝试使用...解决此问题
Cipher.getInstance(“AES/GCM/NoPadding”);
...但在 API 级别 24 及更高级别没有运气。加密没有发生。
更多详情:
我们在 App Login 期间使用 keystore 对用户 ID 和密码进行加密,这可以正常工作。但是,当我们在应用程序的另一部分对其进行解密时,我们会得到AEADBadTagException。
这是加密的代码(这很好用):
@RequiresApi(api = Build.VERSION_CODES.M)
private void encryptText() {
try {
final byte[] encryptedText = encryptor
.encryptText("MY_ALIAS",mPsd.toString());
} catch (UnrecoverableEntryException | NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException | IOException | NoSuchPaddingException | InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException | SignatureException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
这是加密器类(这似乎也可以正常工作):
public class EnCryptor {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private byte[] encryption;
private byte[] iv;
public EnCryptor() {
}
@RequiresApi(api = Build.VERSION_CODES.M)
public byte[] encryptText(final String alias, final String textToEncrypt)
throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException,
NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException,
InvalidAlgorithmParameterException, SignatureException, BadPaddingException,
IllegalBlockSizeException {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(alias));
iv = cipher.getIV();
encryption = cipher.doFinal(textToEncrypt.getBytes("UTF-8"));
SharedPreferencesManager.getInstance().saveEncrypt(Base64.encodeToString(encryption, Base64.DEFAULT));
SharedPreferencesManager.getInstance().saveEncrypted_iv(Base64.encodeToString(iv, Base64.DEFAULT));
return (encryption);
}
@RequiresApi(api = Build.VERSION_CODES.M)
@NonNull
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
NoSuchProviderException, InvalidAlgorithmParameterException {
final KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
keyGenerator.init(new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
return keyGenerator.generateKey();
}
public byte[] getEncryption() {
return encryption;
}
public byte[] getIv() {
return iv;
}
}
但是,稍后在应用程序的另一部分尝试解密密码时,我们会遇到错误。
来自 Fragment 的方法调用:
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private char[] decryptText() {
try {
String txt = decryptor.decryptData("MY_ALIAS",
Base64.decode(SharedPreferencesManager.getInstance().getEncrypt(), Base64.DEFAULT),
Base64.decode(SharedPreferencesManager.getInstance().getEncrypted_iv(), Base64.DEFAULT));
return txt.toCharArray();
} catch (UnrecoverableEntryException | NoSuchAlgorithmException |
KeyStoreException | NoSuchPaddingException | NoSuchProviderException |
IOException | InvalidKeyException e) {
} catch (IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return "".toCharArray();
}
这是引发异常的 Decryptor 类
public class DeCryptor {
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private KeyStore keyStore;
public DeCryptor() throws CertificateException, NoSuchAlgorithmException, KeyStoreException,
IOException {
initKeyStore();
}
private void initKeyStore() throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException {
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
try {
keyStore.load(null);
} catch (java.security.cert.CertificateException e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public String decryptData(final String alias, final byte[] encryptedData, final byte[] encryptionIv)
throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException,
NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException,
BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(alias), spec);
return new String(cipher.doFinal(encryptedData), "UTF-8");
}
private SecretKey getSecretKey(final String alias) throws NoSuchAlgorithmException,
UnrecoverableEntryException, KeyStoreException {
return ((KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null)).getSecretKey();
}
}
这是我们遇到AEADBadTagException 的地方。
解决方案
使用以下行进行解密:
IvParameterSpec ivSpec = new IvParameterSpec(encryptionIv)
而不是
final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv)
推荐阅读
- http - 在 NGINX 中重定向时添加请求标头
- python - 如何在 tkinter 窗口中查找特定像素
- node.js - 如何使用 NodeJS 找到在 Windows 10 中打开的所有窗口的名称?
- node.js - 我可以在局域网上部署带有后端节点和 mongodb 的反应应用程序吗?
- r - ggplot2:组内的条形图顺序
- pandas - Pandas DataFrame 追加系列
- firebase - 如何在firestore中构造数据以避免多个字段的范围查询
- rust - .bind_rustls() 443 超时/不在公共 IP 上服务(使用 certbot 证书)
- bash - 在while循环结束后执行命令
- python - 如何从客户那里收取多笔付款并在 Django 中计算剩余余额?