- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我们如何安全地存储凭据数据以访问 Android 应用程序中的 smtp 服务器?这些数据是常量,只有开发人员应该知道它们。目前它们存储在代码中,但这并不安全,因为可以通过反编译应用程序看到它们。
是否可以为此目的使用 Android Keystore 系统?如何使用?最重要的是,Android Keystore 是否对已获得 root 权限的设备有效?
最佳答案
在 Android 应用程序中,您可以将数据存储在 SharedPreferences 中,但由于此数据实际上存储在文件中,因此任何拥有手机根访问权限的人都可以访问它。如果您想存储凭据或任何其他敏感数据,这意味着安全漏洞。
为了避免其他人看到这些明文数据,一个解决方案是在存储数据之前对其进行加密。从 API 18 开始,Android 引入了 KeyStore,它能够存储用于加密和解密数据的 key 。
API 23 之前的问题是您无法将 AES key 存储在 KeyStore 中,因此最可靠的加密 key 是具有私钥和公钥的 RSA。
所以我想到的解决方案是:
对于 23 以下的 API
API 23 及以上版本
还为加密添加了生成的 IV。
代码:
public class KeyHelper{
private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
private static final String AES_MODE_M = "AES/GCM/NoPadding";
private static final String KEY_ALIAS = "KEY";
private static final String AndroidKeyStore = "AndroidKeyStore";
public static final String SHARED_PREFENCE_NAME = "SAVED_TO_SHARED";
public static final String ENCRYPTED_KEY = "ENCRYPTED_KEY";
public static final String PUBLIC_IV = "PUBLIC_IV";
private KeyStore keyStore;
private static KeyHelper keyHelper;
public static KeyHelper getInstance(Context ctx){
if(keyHelper == null){
try{
keyHelper = new KeyHelper(ctx);
} catch (NoSuchPaddingException | NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | KeyStoreException | CertificateException | IOException e){
e.printStackTrace();
}
}
return keyHelper;
}
public KeyHelper(Context ctx) throws NoSuchPaddingException,NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException, CertificateException, IOException {
this.generateEncryptKey(ctx);
this.generateRandomIV(ctx);
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M){
try{
this.generateAESKey(ctx);
} catch(Exception e){
e.printStackTrace();
}
}
}
private void generateEncryptKey(Context ctx) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException, CertificateException, IOException {
keyStore = KeyStore.getInstance(AndroidKeyStore);
keyStore.load(null);
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
if (!keyStore.containsAlias(KEY_ALIAS)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStore);
keyGenerator.init(
new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
} else{
if (!keyStore.containsAlias(KEY_ALIAS)) {
// Generate a key pair for encryption
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 30);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(ctx)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=" + KEY_ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
kpg.initialize(spec);
kpg.generateKeyPair();
}
}
}
private byte[] rsaEncrypt(byte[] secret) throws Exception{
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
// Encrypt the text
Cipher inputCipher = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL");
inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inputCipher);
cipherOutputStream.write(secret);
cipherOutputStream.close();
return outputStream.toByteArray();
}
private byte[] rsaDecrypt(byte[] encrypted) throws Exception {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(KEY_ALIAS, null);
Cipher output = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(encrypted), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
return bytes;
}
private void generateAESKey(Context context) throws Exception{
SharedPreferences pref = context.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
String enryptedKeyB64 = pref.getString(ENCRYPTED_KEY, null);
if (enryptedKeyB64 == null) {
byte[] key = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
byte[] encryptedKey = rsaEncrypt(key);
enryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT);
SharedPreferences.Editor edit = pref.edit();
edit.putString(ENCRYPTED_KEY, enryptedKeyB64);
edit.apply();
}
}
private Key getAESKeyFromKS() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException{
keyStore = KeyStore.getInstance(AndroidKeyStore);
keyStore.load(null);
SecretKey key = (SecretKey)keyStore.getKey(KEY_ALIAS,null);
return key;
}
private Key getSecretKey(Context context) throws Exception{
SharedPreferences pref = context.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
String enryptedKeyB64 = pref.getString(ENCRYPTED_KEY, null);
byte[] encryptedKey = Base64.decode(enryptedKeyB64, Base64.DEFAULT);
byte[] key = rsaDecrypt(encryptedKey);
return new SecretKeySpec(key, "AES");
}
public String encrypt(Context context, String input) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher c;
SharedPreferences pref = context.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
String publicIV = pref.getString(PUBLIC_IV, null);
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
c = Cipher.getInstance(AES_MODE_M);
try{
c.init(Cipher.ENCRYPT_MODE, getAESKeyFromKS(), new GCMParameterSpec(128,Base64.decode(publicIV, Base64.DEFAULT)));
} catch(Exception e){
e.printStackTrace();
}
} else{
c = Cipher.getInstance(AES_MODE_M);
try{
c.init(Cipher.ENCRYPT_MODE, getSecretKey(context),new GCMParameterSpec(128,Base64.decode(publicIV, Base64.DEFAULT)));
} catch (Exception e){
e.printStackTrace();
}
}
byte[] encodedBytes = c.doFinal(input.getBytes("UTF-8"));
return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}
public String decrypt(Context context, String encrypted) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher c;
SharedPreferences pref = context.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
String publicIV = pref.getString(PUBLIC_IV, null);
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
c = Cipher.getInstance(AES_MODE_M);
try{
c.init(Cipher.DECRYPT_MODE, getAESKeyFromKS(), new GCMParameterSpec(128,Base64.decode(publicIV, Base64.DEFAULT)));
} catch(Exception e){
e.printStackTrace();
}
} else{
c = Cipher.getInstance(AES_MODE_M);
try{
c.init(Cipher.DECRYPT_MODE, getSecretKey(context), new GCMParameterSpec(128,Base64.decode(publicIV, Base64.DEFAULT)));
} catch (Exception e){
e.printStackTrace();
}
}
byte[] decodedValue = Base64.decode(encrypted.getBytes("UTF-8"), Base64.DEFAULT);
byte[] decryptedVal = c.doFinal(decodedValue);
return new String(decryptedVal);
}
public void generateRandomIV(Context ctx){
SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREFENCE_NAME, Context.MODE_PRIVATE);
String publicIV = pref.getString(PUBLIC_IV, null);
if(publicIV == null){
SecureRandom random = new SecureRandom();
byte[] generated = random.generateSeed(12);
String generatedIVstr = Base64.encodeToString(generated, Base64.DEFAULT);
SharedPreferences.Editor edit = pref.edit();
edit.putString(PUBLIC_IV_PERSONAL, generatedIVstr);
edit.apply();
}
}
private String getStringFromSharedPrefs(String key, Context ctx){
SharedPreferences prefs = ctx.getSharedPreferences(MyConstants.APP_SHAREDPREFS, 0);
return prefs.getString(key, null);
}
}
注意:这仅适用于 API 18 及更高版本
关于android - 在 Android 应用程序中存储凭据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42560868/
我有一个包含数字和整数的文件,我只想读取整数, 如果它们令人讨厌,请忽略宏,但是我只需要有整数,但是我必须确保还要读取字符串,然后忽略它们 我必须在这里修改什么: #include #include
我有一个这样格式化的txt文件: MyDepartureTown MyDestinationTown 123.45 Vehicle 12 我正在尝试将数据导入到我的 C 程序中。这是我用来实现这一目标
我创建了一个简单的文件,使用 flex,它生成了一个文件 lex.yy.c,现在,我想把它放到 C++ 程序中。 %{ #include %} %% stop printf("Stop co
我的一个程序用 c++ 代码生成一个大文件。有没有办法从另一个C++类调用将生成的代码插入其中? 这是一个小例子,可以清楚地说明我想要实现的目标。 生成的文件示例: FirstClass first
我需要了解我的程序“检查输入十六进制消息的第三个位置” 程序将采用十六进制值输入消息。例如0x0123456789abcdef 程序将检查输入消息的第三个位置,即 0 现在程序将采用另一条十六进制值的
当我将输入从输入文件重定向到 yacc 程序时,在它完成解析文件后,我希望 yacc 解析器打印其所做操作的摘要。如果我通过键盘输入内容然后按 Ctrl+D,我希望它执行相同的操作。有办法做到这一点吗
我正在扫描该文件,但它有两种不同的结构。 文件: ParisRoubaix "Marco MARCATO" 33 UAD ITA 26 5:43:31 ParisRoubaix "Sam BEWLEY
我想将winsock2.lib 添加到我的程序中,但不希望将其包含到最终的可执行文件中。有什么方法可以让我动态加载与winsock2关联的dll吗?如果没有,是否有任何 dll(Windows 附带)
我尝试了一个基本程序来将数据从数据库表检索到java程序中。编译结束后,运行代码时出现异常。控制台中没有显示错误。显示异常消息 import java.sql.*; public class clas
我想用 C++ 创建一个跨平台安装程序。它可以是任何压缩类型,例如 zip 或 gzip,像普通安装程序一样嵌入程序本身。我不想在不同的平台、linux 和 windows 上创建很多更改。如何跨平台
每次尝试用鼠标输入两个顶点时,我都会崩溃。我最近改变了组织每个形状的方式,以确保新形状与旧形状重叠。 这个项目的想法是制作各种交互式 Canvas 。用户可以在直线、三角形和矩形之间进行选择,然后选择
我想在我的程序中显示以下文本。当我在 python 中粘贴以下文本时,它会将反斜杠解释为转义序列并弄乱我的 ascii 艺术..任何解决这个问题的想法极客。这是我的文本想出现在我的节目中 _ _
我正在尝试加载名为 Tut16_ReadText.txt 的文件,并使其运行程序以输出其重或轻。 我收到了粘贴在下面的错误。我无法抽出时间让这个程序运行。谁能解释一下我必须做什么才能使这个程序正常工作
我想使用命令行将列表作为参数传递,例如: $python example.py [1,2,3] [4,5,6] 我希望第一个列表 [1,2,3] 成为 first_list,[4,5,6] 成为 se
在分析 C# 应用程序时,我发现名为“ThePreStub”的系统 (?) 方法中有相当多的 CPU 使用率。这是什么? 最佳答案 参见:CLR Inside out - The Performanc
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
我正在用 Python 开发一个游戏,想知道如何给它自己的图标。我使用的是 Windows 计算机,没有安装 Python 的额外东西。哦,我也在使用 3.3 版,这甚至可能吗? P.S 我在 Sta
我正在使用 tkinter 使用 Python 开发一个项目,该项目将允许对 IP 地址进行地理定位。我有原始转换,我可以获取 IP 地址并知道城市、州、国家、经度、纬度等。我想知道是否有任何方法可以
我编写了一个程序,您可以在其中选择任意数字并将其与任意数字的幂相关联。代码运行正常,直到它到达某个部分,然后我必须输入一个字符以使其移动到代码的下一部分。这就是我的意思: #include int
我正在编写“HACKING Art Of Exploitation”一书练习 Convert2.c 第 61 页。 这是我的代码。下面是我的问题。 #include void usage(char
我是一名优秀的程序员,十分优秀!