Token安全存儲(chǔ)的幾種方式小結(jié)
1. EncryptedSharedPreferences
EncryptedSharedPreferences 是一個(gè)開(kāi)源庫(kù),用于對(duì) SharedPreferences 進(jìn)行加密存儲(chǔ),提供了更高的安全性。
示例代碼
// 創(chuàng)建 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
);
// 存儲(chǔ) Token
SharedPreferences.Editor editor = encryptedSharedPreferences.edit();
editor.putString("token", token);
editor.apply();
// 獲取 Token
String token = encryptedSharedPreferences.getString("token", null);
2. SQLCipher
SQLCipher 是一個(gè)開(kāi)源庫(kù),用于對(duì) SQLite 數(shù)據(jù)庫(kù)進(jìn)行加密存儲(chǔ),適用于需要更高安全性的場(chǎng)景。
示例代碼
// 初始化 SQLCipher 數(shù)據(jù)庫(kù)
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
new File(context.getFilesDir(), "encrypted.db"),
"password", // 數(shù)據(jù)庫(kù)密碼
null
);
// 創(chuàng)建表并存儲(chǔ) 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加密后存儲(chǔ)
Keystore 提供了硬件級(jí)別的加密保護(hù),即使設(shè)備被 Root,也很難獲取存儲(chǔ)在 Keystore 中的密鑰。
非常適合存儲(chǔ) Token、密碼等敏感信息。
不過(guò)使用 Keystore 比較復(fù)雜,需要生成密鑰對(duì)、加密和解密數(shù)據(jù)等操作。而加密和解密操作會(huì)帶來(lái)一定的性能開(kāi)銷(xiāo)。
示例代碼
1. 生成密鑰對(duì)
在應(yīng)用首次啟動(dòng)時(shí),生成一個(gè)密鑰對(duì)并存儲(chǔ)在 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
在你的應(yīng)用中,使用 KeystoreManager 來(lái)存儲(chǔ)和讀取 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();
// 生成密鑰對(duì)(只需在首次啟動(dòng)時(shí)調(diào)用一次)
keystoreManager.generateKey();
// 加密 Token
String accessToken = "your_access_token_here";
byte[] encryptedAccessToken = keystoreManager.encryptData(accessToken);
//存儲(chǔ)請(qǐng)參考下述的幾種方式
// 解密 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();
}
}
}
代碼說(shuō)明
生成密鑰對(duì):
- 使用
KeyGenParameterSpec定義密鑰的屬性。 - 使用
KeyGenerator生成密鑰對(duì)并存儲(chǔ)在 Keystore 中。
- 使用
加密數(shù)據(jù):
- 使用
Cipher對(duì)數(shù)據(jù)進(jìn)行加密。 - 返回加密后的字節(jié)數(shù)組。
- 使用
解密數(shù)據(jù):
- 使用
Cipher對(duì)加密數(shù)據(jù)進(jìn)行解密。 - 返回解密后的字符串。
- 使用
存儲(chǔ)和讀取 Token:
- 將加密后的 Token 存儲(chǔ)在應(yīng)用的私有目錄中(例如
SharedPreferences或文件系統(tǒng))。 - 需要時(shí),讀取加密數(shù)據(jù)并解密。
- 將加密后的 Token 存儲(chǔ)在應(yīng)用的私有目錄中(例如
安全性建議
- 密鑰管理:確保密鑰的生成和使用過(guò)程安全,避免密鑰泄露。
- 存儲(chǔ)加密數(shù)據(jù):將加密后的 Token 存儲(chǔ)在應(yīng)用的私有目錄中,避免被其他應(yīng)用訪問(wèn)。
- 錯(cuò)誤處理:在實(shí)際應(yīng)用中,需要對(duì)各種異常情況進(jìn)行處理,確保應(yīng)用的穩(wěn)定性和安全性。
加密后的幾種存儲(chǔ)方式
1. 加密后采用 SharedPreferences存儲(chǔ)
SharedPreferences 是 Android 中一種輕量級(jí)的存儲(chǔ)方式,適合存儲(chǔ)少量的鍵值對(duì)數(shù)據(jù)。你可以將加密后的 Token 存儲(chǔ)到 SharedPreferences 中。
// 存儲(chǔ)加密后的 Token 到 SharedPreferences
SharedPreferences sharedPreferences = getSharedPreferences("MyAppPreferences", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("encryptedToken", Base64.encodeToString(encryptedAccessToken, Base64.DEFAULT));
editor.apply();
從 SharedPreferences 中讀取時(shí):
SharedPreferences sharedPreferences = getSharedPreferences("MyAppPreferences", MODE_PRIVATE);
String encryptedToken = sharedPreferences.getString("encryptedToken", null);
if (encryptedToken != null) {
byte[] encryptedAccessToken = Base64.decode(encryptedToken, Base64.DEFAULT);
// 然后可以對(duì) encryptedAccessToken 進(jìn)行解密等操作
}
2. 加密后采用SQLite數(shù)據(jù)庫(kù)存儲(chǔ)
如果應(yīng)用中有數(shù)據(jù)庫(kù)(如 SQLite),也可以將加密后的 Token 存儲(chǔ)到數(shù)據(jù)庫(kù)中。這種方式適合需要結(jié)構(gòu)化存儲(chǔ)的場(chǎng)景。
1. TokenDatabaseHelper 類
以下是 TokenDatabaseHelper 類的完整代碼,用于創(chuàng)建和管理 SQLite 數(shù)據(jù)庫(kù):
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 中的實(shí)現(xiàn)
在 MainActivity 中,我們將使用 TokenDatabaseHelper 來(lái)存儲(chǔ)和讀取加密后的 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);
// 初始化數(shù)據(jù)庫(kù)幫助類
dbHelper = new TokenDatabaseHelper(this);
try {
KeystoreManager keystoreManager = new KeystoreManager();
// 生成密鑰對(duì)(只需在首次啟動(dòng)時(shí)調(diào)用一次)
keystoreManager.generateKey();
// 加密 Token
String accessToken = "your_access_token_here";
byte[] encryptedAccessToken = keystoreManager.encryptData(accessToken);
// 將加密后的 Token 存儲(chǔ)到數(shù)據(jù)庫(kù)
String encodedToken = Base64.encodeToString(encryptedAccessToken, Base64.DEFAULT);
dbHelper.saveToken(encodedToken);
// 從數(shù)據(jù)庫(kù)中讀取 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. 代碼說(shuō)明
加密和存儲(chǔ) Token
- 使用
KeystoreManager加密 Token。 - 將加密后的 Token(Base64 編碼)存儲(chǔ)到 SQLite 數(shù)據(jù)庫(kù)中。
- 使用
讀取和解密 Token
- 從數(shù)據(jù)庫(kù)中讀取加密后的 Token。
- 解密 Token 并打印出來(lái)。
TokenDatabaseHelper
- 提供了
saveToken和getToken方法,分別用于存儲(chǔ)和讀取 Token 數(shù)據(jù)。
- 提供了
4. 注意事項(xiàng)
- 確保
KeystoreManager類的generateKey、encryptData和decryptData方法實(shí)現(xiàn)正確。 - 數(shù)據(jù)庫(kù)的
COLUMN_TOKEN字段存儲(chǔ)的是 Base64 編碼后的加密數(shù)據(jù),確保在存儲(chǔ)和讀取時(shí)正確處理編碼和解碼。 - 如果需要支持多條 Token 數(shù)據(jù),可以在
getToken方法中添加邏輯,例如按時(shí)間戳排序或指定特定的 Token。
3. 加密后采用內(nèi)部文件存儲(chǔ)
如果 Token 數(shù)據(jù)較大,或者需要更安全的存儲(chǔ)方式,可以將其存儲(chǔ)到內(nèi)部存儲(chǔ)中。內(nèi)部存儲(chǔ)是私有的,其他應(yīng)用無(wú)法訪問(wèn)。
// 存儲(chǔ)到內(nèi)部存儲(chǔ) File file = new File(getFilesDir(), "encryptedToken.txt"); FileOutputStream fos = new FileOutputStream(file); fos.write(encryptedAccessToken); fos.close();
從內(nèi)部存儲(chǔ)中讀取時(shí):
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. 云存儲(chǔ)服務(wù)
如果需要跨設(shè)備同步 Token,可以考慮使用云存儲(chǔ)服務(wù),如 Firebase、Dropbox 等。
示例代碼(使用 Firebase)
// 初始化 Firebase 數(shù)據(jù)庫(kù)
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference tokensRef = database.getReference("tokens");
// 存儲(chǔ) 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) {
// 處理錯(cuò)誤
}
});
總結(jié)
- EncryptedSharedPreferences:提供加密的
SharedPreferences,適合存儲(chǔ)少量敏感數(shù)據(jù)。 - SQLCipher:提供加密的 SQLite 數(shù)據(jù)庫(kù),適合需要更高安全性的場(chǎng)景。
- SQLite 數(shù)據(jù)庫(kù):適合存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù),支持復(fù)雜查詢。建議先加密在存儲(chǔ)。
- 文件存儲(chǔ):適合存儲(chǔ)簡(jiǎn)單的文本數(shù)據(jù),確保文件權(quán)限為
MODE_PRIVATE。建議先加密在存儲(chǔ)。 - SharedPreferences:適合存儲(chǔ)少量數(shù)據(jù)。建議先加密在存儲(chǔ)。
- 云存儲(chǔ)服務(wù):適合跨設(shè)備同步數(shù)據(jù),但需要依賴第三方服務(wù)。
以上就是Token安全存儲(chǔ)的幾種方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Token安全存儲(chǔ)方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java設(shè)計(jì)模式中的門(mén)面模式詳解
門(mén)面模式又叫外觀模式(Facade Pattern),主要用于隱藏系統(tǒng)的復(fù)雜性,并向客戶端提供了一個(gè)客戶端可以訪問(wèn)系統(tǒng)的接口,本文通過(guò)實(shí)例代碼給大家介紹下java門(mén)面模式的相關(guān)知識(shí),感興趣的朋友一起看看吧2022-09-09
MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱
MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架,MyBatis越來(lái)越受大家的喜愛(ài)了。下面給大家分享MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱,感興趣的朋友一起看看吧2016-10-10
Mybatis一對(duì)多與多對(duì)一查詢處理詳解
這篇文章主要給大家介紹了關(guān)于Mybatis一對(duì)多與多對(duì)一查詢處理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Spring Boot的FailureAnalyzer機(jī)制及如何解救應(yīng)用啟動(dòng)危機(jī)
本文探討了FailureAnalyzer工具,它不僅能幫助我們快速識(shí)別和處理代碼中的錯(cuò)誤,還能極大地提升我們的開(kāi)發(fā)效率,通過(guò)詳細(xì)的實(shí)例分析,我們了解了FailureAnalyzer如何通過(guò)自定義邏輯應(yīng)對(duì)不同類型的異常,讓程序員能夠更好地定位問(wèn)題并迅速找到解決方案,感興趣的朋友一起看看吧2025-01-01
Java使用Swing實(shí)現(xiàn)一個(gè)模擬電腦計(jì)算器
Java Swing 是一個(gè)用于創(chuàng)建 Java GUI(圖形用戶界面)的框架,它提供了一系列的 GUI 組件和工具,可以用于創(chuàng)建桌面應(yīng)用程序,包括按鈕、文本框、標(biāo)簽、表格等等,本文給大家介紹了Java使用Swing實(shí)現(xiàn)一個(gè)模擬計(jì)算器,感興趣的同學(xué)可以自己動(dòng)手嘗試一下2024-05-05

