欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Token安全存储的几种方式小结,

来源: javaer 分享于  点击 14475 次 点评:25

Token安全存储的几种方式小结,


目录
  • 1. EncryptedSharedPreferences
    • 示例代码
  • 2. SQLCipher
    • 示例代码
  • 3.使用 Android Keystore加密后存储
    • 示例代码
      • 1. 生成密钥对
      • 2. 使用 KeystoreManager
    • 代码说明
      • 安全性建议
        • 加密后的几种存储方式
          • 1. 加密后采用 SharedPreferences存储
        • 2. 加密后采用SQLite数据库存储
          • 1. TokenDatabaseHelper 类
          • 2. MainActivity 中的实现
          • 3. 代码说明
          • 4. 注意事项
        • 3. 加密后采用内部文件存储
        • 4. 云存储服务
          • 示例代码(使用 Firebase)
          • 总结

            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();
                    }
                }
            }
            

            代码说明

            1. 生成密钥对

              • 使用 KeyGenParameterSpec 定义密钥的属性。
              • 使用 KeyGenerator 生成密钥对并存储在 Keystore 中。
            2. 加密数据

              • 使用 Cipher 对数据进行加密。
              • 返回加密后的字节数组。
            3. 解密数据

              • 使用 Cipher 对加密数据进行解密。
              • 返回解密后的字符串。
            4. 存储和读取 Token

              • 将加密后的 Token 存储在应用的私有目录中(例如 SharedPreferences 或文件系统)。
              • 需要时,读取加密数据并解密。

            安全性建议

            1. 密钥管理:确保密钥的生成和使用过程安全,避免密钥泄露。
            2. 存储加密数据:将加密后的 Token 存储在应用的私有目录中,避免被其他应用访问。
            3. 错误处理:在实际应用中,需要对各种异常情况进行处理,确保应用的稳定性和安全性。

            加密后的几种存储方式

            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. 代码说明

            1. 加密和存储 Token

              • 使用 KeystoreManager 加密 Token。
              • 将加密后的 Token(Base64 编码)存储到 SQLite 数据库中。
            2. 读取和解密 Token

              • 从数据库中读取加密后的 Token。
              • 解密 Token 并打印出来。
            3. TokenDatabaseHelper

              • 提供了 saveToken 和 getToken 方法,分别用于存储和读取 Token 数据。

            4. 注意事项

            • 确保 KeystoreManager 类的 generateKeyencryptData 和 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安全存储方式的资料请关注3672js教程其它相关文章!

            您可能感兴趣的文章:
            • 保证Redis中存储的Token安全性的示例详解
            • React配置Redux并结合本地存储设置token方式
            • Vue中使用localStorage存储token并设置时效
            • vue项目中存储与使用后端传递过来的token
            • Redis妙用之存储用户token问题
            相关栏目:

            用户点评