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安全存储方式的资料请关注代码网其它相关文章!
发表评论