1. encryptedsharedpreferences
encryptedsharedpreferences 是一个开源库,用于对 sharedpreferences 进行加密存储,提供了更高的安全性。
示例代码
// 创建 encryptedsharedpreferences
masterkeys.keypair keypair = masterkeys.generatekeypair(context, masterkeys.aes256_gcm_spec);
string keyalias = keypair.getalias();
encryptedsharedpreferences encryptedsharedpreferences = encryptedsharedpreferences.create(
context,
"encrypted_prefs",
keyalias,
encryptedsharedpreferences.prefkeyencryptionscheme.aes256_siv,
encryptedsharedpreferences.prefvalueencryptionscheme.aes256_gcm
);
// 存储 token
sharedpreferences.editor editor = encryptedsharedpreferences.edit();
editor.putstring("token", token);
editor.apply();
// 获取 token
string token = encryptedsharedpreferences.getstring("token", null);
2. sqlcipher
sqlcipher 是一个开源库,用于对 sqlite 数据库进行加密存储,适用于需要更高安全性的场景。
示例代码
// 初始化 sqlcipher 数据库
sqlitedatabase db = sqlitedatabase.openorcreatedatabase(
new file(context.getfilesdir(), "encrypted.db"),
"password", // 数据库密码
null
);
// 创建表并存储 token
db.execsql("create table if not exists tokens (token text)");
db.execsql("insert into tokens (token) values (?)", new object[]{token});
db.close();
3.使用 android keystore加密后存储
keystore 提供了硬件级别的加密保护,即使设备被 root,也很难获取存储在 keystore 中的密钥。
非常适合存储 token、密码等敏感信息。
不过使用 keystore 比较复杂,需要生成密钥对、加密和解密数据等操作。而加密和解密操作会带来一定的性能开销。
示例代码
1. 生成密钥对
在应用首次启动时,生成一个密钥对并存储在 keystore 中。
import android.security.keystore.keygenparameterspec;
import android.security.keystore.keyproperties;
import android.util.base64;
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.unrecoverableentryexception;
import java.security.cert.certificateexception;
import javax.crypto.cipher;
import javax.crypto.keygenerator;
import javax.crypto.secretkey;
import javax.crypto.spec.gcmparameterspec;
public class keystoremanager {
private static final string keystore_provider = "androidkeystore";
private static final string key_alias = "myappkeyalias";
private static final string encryption_algorithm = keyproperties.key_algorithm_aes;
private static final string encryption_block_mode = keyproperties.block_mode_gcm;
private static final string encryption_padding = keyproperties.encryption_padding_none;
private static final string encryption_transformation = encryption_algorithm + "/"
+ encryption_block_mode + "/" + encryption_padding;
private keystore keystore;
public keystoremanager() throws keystoreexception, certificateexception, nosuchalgorithmexception, ioexception {
keystore = keystore.getinstance(keystore_provider);
keystore.load(null);
}
public void generatekey() throws nosuchalgorithmexception, invalidalgorithmparameterexception {
keygenerator keygenerator = keygenerator.getinstance(keyproperties.key_algorithm_aes, keystore_provider);
keygenerator.init(new keygenparameterspec.builder(key_alias,
keyproperties.purpose_encrypt | keyproperties.purpose_decrypt)
.setblockmodes(encryption_block_mode)
.setencryptionpaddings(keyproperties.encryption_padding_none)
.build());
keygenerator.generatekey();
}
public byte[] encryptdata(string data) throws exception {
cipher cipher = cipher.getinstance(encryption_transformation);
cipher.init(cipher.encrypt_mode, getsecretkey());
return cipher.dofinal(data.getbytes());
}
public string decryptdata(byte[] encrypteddata) throws exception {
cipher cipher = cipher.getinstance(encryption_transformation);
cipher.init(cipher.decrypt_mode, getsecretkey());
return new string(cipher.dofinal(encrypteddata));
}
private secretkey getsecretkey() throws unrecoverableentryexception, keystoreexception {
return (secretkey) keystore.getkey(key_alias, null);
}
}
2. 使用 keystoremanager
在你的应用中,使用 keystoremanager 来存储和读取 token。
import android.os.bundle;
import android.util.log;
import androidx.appcompat.app.appcompatactivity;
import java.io.ioexception;
import java.security.keystoreexception;
import java.security.nosuchalgorithmexception;
import java.security.unrecoverableentryexception;
import java.security.cert.certificateexception;
public class mainactivity extends appcompatactivity {
private static final string tag = "mainactivity";
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
try {
keystoremanager keystoremanager = new keystoremanager();
// 生成密钥对(只需在首次启动时调用一次)
keystoremanager.generatekey();
// 加密 token
string accesstoken = "your_access_token_here";
byte[] encryptedaccesstoken = keystoremanager.encryptdata(accesstoken);
//存储请参考下述的几种方式
// 解密 token
string decryptedaccesstoken = keystoremanager.decryptdata(encryptedaccesstoken);
log.d(tag, "encrypted token: " + base64.encodetostring(encryptedaccesstoken, base64.default));
log.d(tag, "decrypted token: " + decryptedaccesstoken);
} catch (keystoreexception | certificateexception | nosuchalgorithmexception | ioexception | unrecoverableentryexception | invalidalgorithmparameterexception e) {
e.printstacktrace();
}
}
}
代码说明
生成密钥对:
- 使用
keygenparameterspec定义密钥的属性。 - 使用
keygenerator生成密钥对并存储在 keystore 中。
- 使用
加密数据:
- 使用
cipher对数据进行加密。 - 返回加密后的字节数组。
- 使用
解密数据:
- 使用
cipher对加密数据进行解密。 - 返回解密后的字符串。
- 使用
存储和读取 token:
- 将加密后的 token 存储在应用的私有目录中(例如
sharedpreferences或文件系统)。 - 需要时,读取加密数据并解密。
- 将加密后的 token 存储在应用的私有目录中(例如
安全性建议
- 密钥管理:确保密钥的生成和使用过程安全,避免密钥泄露。
- 存储加密数据:将加密后的 token 存储在应用的私有目录中,避免被其他应用访问。
- 错误处理:在实际应用中,需要对各种异常情况进行处理,确保应用的稳定性和安全性。
加密后的几种存储方式
1. 加密后采用 sharedpreferences存储
sharedpreferences 是 android 中一种轻量级的存储方式,适合存储少量的键值对数据。你可以将加密后的 token 存储到 sharedpreferences 中。
// 存储加密后的 token 到 sharedpreferences
sharedpreferences sharedpreferences = getsharedpreferences("myapppreferences", mode_private);
sharedpreferences.editor editor = sharedpreferences.edit();
editor.putstring("encryptedtoken", base64.encodetostring(encryptedaccesstoken, base64.default));
editor.apply();
从 sharedpreferences 中读取时:
sharedpreferences sharedpreferences = getsharedpreferences("myapppreferences", mode_private);
string encryptedtoken = sharedpreferences.getstring("encryptedtoken", null);
if (encryptedtoken != null) {
byte[] encryptedaccesstoken = base64.decode(encryptedtoken, base64.default);
// 然后可以对 encryptedaccesstoken 进行解密等操作
}
2. 加密后采用sqlite数据库存储
如果应用中有数据库(如 sqlite),也可以将加密后的 token 存储到数据库中。这种方式适合需要结构化存储的场景。
1. tokendatabasehelper 类
以下是 tokendatabasehelper 类的完整代码,用于创建和管理 sqlite 数据库:
import android.content.contentvalues;
import android.content.context;
import android.database.cursor;
import android.database.sqlite.sqlitedatabase;
import android.database.sqlite.sqliteopenhelper;
public class tokendatabasehelper extends sqliteopenhelper {
private static final string database_name = "token.db";
private static final int database_version = 1;
private static final string table_tokens = "tokens";
private static final string column_id = "id";
private static final string column_token = "token";
public tokendatabasehelper(context context) {
super(context, database_name, null, database_version);
}
@override
public void oncreate(sqlitedatabase db) {
string createtable = "create table " + table_tokens + "("
+ column_id + " integer primary key autoincrement,"
+ column_token + " text" + ")";
db.execsql(createtable);
}
@override
public void onupgrade(sqlitedatabase db, int oldversion, int newversion) {
db.execsql("drop table if exists " + table_tokens);
oncreate(db);
}
public void savetoken(string token) {
sqlitedatabase db = this.getwritabledatabase();
contentvalues values = new contentvalues();
values.put(column_token, token);
db.insert(table_tokens, null, values);
db.close();
}
public string gettoken() {
string token = null;
sqlitedatabase db = this.getreadabledatabase();
cursor cursor = db.query(table_tokens, new string[]{column_token}, null, null, null, null, null);
if (cursor != null && cursor.movetofirst()) {
token = cursor.getstring(cursor.getcolumnindex(column_token));
}
cursor.close();
db.close();
return token;
}
}
2. mainactivity 中的实现
在 mainactivity 中,我们将使用 tokendatabasehelper 来存储和读取加密后的 token。
以下是完整的代码:
import android.os.bundle;
import android.util.base64;
import android.util.log;
import androidx.appcompat.app.appcompatactivity;
import java.io.ioexception;
import java.security.keystoreexception;
import java.security.nosuchalgorithmexception;
import java.security.unrecoverableentryexception;
import java.security.cert.certificateexception;
public class mainactivity extends appcompatactivity {
private static final string tag = "mainactivity";
private tokendatabasehelper dbhelper;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
// 初始化数据库帮助类
dbhelper = new tokendatabasehelper(this);
try {
keystoremanager keystoremanager = new keystoremanager();
// 生成密钥对(只需在首次启动时调用一次)
keystoremanager.generatekey();
// 加密 token
string accesstoken = "your_access_token_here";
byte[] encryptedaccesstoken = keystoremanager.encryptdata(accesstoken);
// 将加密后的 token 存储到数据库
string encodedtoken = base64.encodetostring(encryptedaccesstoken, base64.default);
dbhelper.savetoken(encodedtoken);
// 从数据库中读取 token
string retrievedtoken = dbhelper.gettoken();
if (retrievedtoken != null) {
byte[] retrievedencryptedtoken = base64.decode(retrievedtoken, base64.default);
// 解密 token
string decryptedaccesstoken = keystoremanager.decryptdata(retrievedencryptedtoken);
log.d(tag, "decrypted token: " + decryptedaccesstoken);
}
} catch (keystoreexception | certificateexception | nosuchalgorithmexception | ioexception | unrecoverableentryexception | invalidalgorithmparameterexception e) {
e.printstacktrace();
}
}
}
3. 代码说明
加密和存储 token
- 使用
keystoremanager加密 token。 - 将加密后的 token(base64 编码)存储到 sqlite 数据库中。
- 使用
读取和解密 token
- 从数据库中读取加密后的 token。
- 解密 token 并打印出来。
tokendatabasehelper
- 提供了
savetoken和gettoken方法,分别用于存储和读取 token 数据。
- 提供了
4. 注意事项
- 确保
keystoremanager类的generatekey、encryptdata和decryptdata方法实现正确。 - 数据库的
column_token字段存储的是 base64 编码后的加密数据,确保在存储和读取时正确处理编码和解码。 - 如果需要支持多条 token 数据,可以在
gettoken方法中添加逻辑,例如按时间戳排序或指定特定的 token。
3. 加密后采用内部文件存储
如果 token 数据较大,或者需要更安全的存储方式,可以将其存储到内部存储中。内部存储是私有的,其他应用无法访问。
// 存储到内部存储 file file = new file(getfilesdir(), "encryptedtoken.txt"); fileoutputstream fos = new fileoutputstream(file); fos.write(encryptedaccesstoken); fos.close();
从内部存储中读取时:
file file = new file(getfilesdir(), "encryptedtoken.txt"); fileinputstream fis = new fileinputstream(file); byte[] encryptedaccesstoken = new byte[(int) file.length()]; fis.read(encryptedaccesstoken); fis.close();
4. 云存储服务
如果需要跨设备同步 token,可以考虑使用云存储服务,如 firebase、dropbox 等。
示例代码(使用 firebase)
// 初始化 firebase 数据库
firebasedatabase database = firebasedatabase.getinstance();
databasereference tokensref = database.getreference("tokens");
// 存储 token
tokensref.child("usertoken").setvalue(token);
// 获取 token
tokensref.child("usertoken").addlistenerforsinglevalueevent(new valueeventlistener() {
@override
public void ondatachange(datasnapshot datasnapshot) {
string token = datasnapshot.getvalue(string.class);
// 使用 token
}
@override
public void oncancelled(databaseerror databaseerror) {
// 处理错误
}
});
总结
- encryptedsharedpreferences:提供加密的
sharedpreferences,适合存储少量敏感数据。 - sqlcipher:提供加密的 sqlite 数据库,适合需要更高安全性的场景。
- sqlite 数据库:适合存储结构化数据,支持复杂查询。建议先加密在存储。
- 文件存储:适合存储简单的文本数据,确保文件权限为
mode_private。建议先加密在存储。 - sharedpreferences:适合存储少量数据。建议先加密在存储。
- 云存储服务:适合跨设备同步数据,但需要依赖第三方服务。
以上就是token安全存储的几种方式小结的详细内容,更多关于token安全存储方式的资料请关注代码网其它相关文章!
发表评论