Przeglądaj źródła

SharedPreference 本地存储加密

fancy 5 lat temu
rodzic
commit
23a602b255

+ 2 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2.kt

@@ -114,6 +114,8 @@ object O2 {
     //////////////////////////////////SharedPreferences KEY /////////////////////////////////////////////////
 
     val PREFERENCE_FILE = "API_DISTRIBUTE_FILE"
+    val SECURITY_PREFERENCE_FILE = "API_DIST_FILE_SECURITY"
+    val SECURITY_IS_UPDATE = "SECURITY_IS_UPDATE"
     val PRE_ASSEMBLESJSON_KEY = "ASSEMBLESJSON_KEY"
     val PRE_WEBSERVERJSON_KEY = "WEBSERVERJSON_KEY"
     val PRE_CENTER_HOST_KEY = "PRE_CENTER_HOST_KEY"//中心服务器地址

+ 11 - 1
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2SDKManager.kt

@@ -7,6 +7,7 @@ import android.text.TextUtils
 import android.util.Log
 import com.google.gson.Gson
 import com.google.gson.GsonBuilder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2.SECURITY_IS_UPDATE
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.LaunchState
@@ -19,6 +20,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.portal.PortalData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.SharedPreferencesHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.edit
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security.SecuritySharedPreference
 import rx.Observable
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
@@ -107,6 +109,13 @@ class O2SDKManager private constructor()  {
         //初始化RetrofitClient
         this.context = context
         spHelper = SharedPreferencesHelper(context)
+        //检查老的sp 是否要更新
+        val isUpdate = prefs().getBoolean(SECURITY_IS_UPDATE, false)
+        if (!isUpdate) {
+            Log.i(TAG, "过渡老的sp文件!")
+            prefs().handleTransition() //执行过渡程序把老的sp文件读取覆盖一下
+            prefs().edit().putBoolean(SECURITY_IS_UPDATE, true).apply()
+        }
 
         RetrofitClient.instance().init(context)
         cId = prefs().getString(CURRENT_PERSON_ID_KEY, "") ?: ""
@@ -132,8 +141,9 @@ class O2SDKManager private constructor()  {
 
     }
 
-    fun prefs(): SharedPreferences = spHelper.prefs()
+//    fun prefs(): SharedPreferences = spHelper.prefs()
 
+    fun prefs(): SecuritySharedPreference = spHelper.securityPrefs()
 
     /**
      * 启动  整个启动过程,检查绑定 连接中心服务器 下载配置 登录

+ 8 - 4
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/SharedPreferencesHelper.kt

@@ -1,9 +1,8 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils
 
-import android.annotation.SuppressLint
 import android.content.Context
-import android.content.SharedPreferences
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security.SecuritySharedPreference
 
 /**
  * Created by fancyLou on 2018/11/22.
@@ -15,7 +14,12 @@ class SharedPreferencesHelper(cxt: Context)   {
 
     private var context: Context = cxt
 
-    fun prefs(): SharedPreferences = context.getSharedPreferences(O2.PREFERENCE_FILE, Context.MODE_PRIVATE)
+//    fun prefs(): SharedPreferences = context.getSharedPreferences(O2.PREFERENCE_FILE, Context.MODE_PRIVATE)
 
-    
+    /**
+     * 加密的SharedPreference
+     */
+    fun securityPrefs(): SecuritySharedPreference {
+        return SecuritySharedPreference(context, O2.PREFERENCE_FILE, Context.MODE_PRIVATE)
+    }
 }

+ 10 - 3
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/extension/Ext.kt

@@ -11,6 +11,8 @@ import androidx.appcompat.app.AppCompatActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.high_order_func.KTXDrawerListener
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.high_order_func._OnPageChangeListener
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.high_order_func._OnSubscribe
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security.SecurityEditor
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security.SecuritySharedPreference
 import rx.Observable
 
 /**
@@ -39,13 +41,18 @@ fun Long.friendlyFileLength(): String {
     }
 }
 
-inline fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {
-    val editor = edit()
+//inline fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) {
+//    val editor = edit()
+//    editor.func()
+//    editor.apply()
+//}
+
+inline fun SecuritySharedPreference.edit(func: SecurityEditor.() -> Unit) {
+    val editor  = edit()
     editor.func()
     editor.apply()
 }
 
-
 inline fun ViewPager.addOnPageChangeListener(func: _OnPageChangeListener.() -> Unit) {
     val listener = _OnPageChangeListener()
     listener.func()

+ 147 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/security/EncryptUtil.java

@@ -0,0 +1,147 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * AES加密解密工具
+ * @author Max
+ * 2016年11月25日15:25:17
+ */
+public class EncryptUtil {
+
+    private String key;
+    private static EncryptUtil instance;
+    private static final String TAG = EncryptUtil.class.getSimpleName();
+
+
+    private EncryptUtil(Context context){
+        String serialNo = getDeviceSerialNumber(context);
+        //加密随机字符串生成AES key
+        key = SHA(serialNo + "#$ERDTS$D%F^Gojikbh").substring(0, 16);
+        Log.e(TAG, key);
+    }
+
+    /**
+     * 单例模式
+     * @param context context
+     * @return
+     */
+    public static EncryptUtil getInstance(Context context){
+        if (instance == null){
+            synchronized (EncryptUtil.class){
+                if (instance == null){
+                    instance = new EncryptUtil(context);
+                }
+            }
+        }
+
+        return instance;
+    }
+
+    /**
+     * Gets the hardware serial number of this device.
+     *
+     * @return serial number or Settings.Secure.ANDROID_ID if not available.
+     */
+    @SuppressLint("HardwareIds")
+    private String getDeviceSerialNumber(Context context) {
+        // We're using the Reflection API because Build.SERIAL is only available
+        // since API Level 9 (Gingerbread, Android 2.3).
+        try {
+            String deviceSerial = (String) Build.class.getField("SERIAL").get(null);
+            if (TextUtils.isEmpty(deviceSerial)) {
+                return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
+            } else {
+                return deviceSerial;
+            }
+        } catch (Exception ignored) {
+            // Fall back  to Android_ID
+            return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
+        }
+    }
+
+
+    /**
+     * SHA加密
+     * @param strText 明文
+     * @return
+     */
+    private String SHA(final String strText){
+        // 返回值
+        String strResult = null;
+        // 是否是有效字符串
+        if (strText != null && strText.length() > 0){
+            try{
+                // SHA 加密开始
+                MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+                // 传入要加密的字符串
+                messageDigest.update(strText.getBytes());
+                byte byteBuffer[] = messageDigest.digest();
+                StringBuffer strHexString = new StringBuffer();
+                for (int i = 0; i < byteBuffer.length; i++){
+                    String hex = Integer.toHexString(0xff & byteBuffer[i]);
+                    if (hex.length() == 1){
+                        strHexString.append('0');
+                    }
+                    strHexString.append(hex);
+                }
+                strResult = strHexString.toString();
+            } catch (NoSuchAlgorithmException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return strResult;
+    }
+
+
+    /**
+     * AES128加密
+     * @param plainText 明文
+     * @return
+     */
+    public String encrypt(String plainText) {
+        try {
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
+            cipher.init(Cipher.ENCRYPT_MODE, keyspec);
+            byte[] encrypted = cipher.doFinal(plainText.getBytes());
+            return Base64.encodeToString(encrypted, Base64.NO_WRAP);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * AES128解密
+     * @param cipherText 密文
+     * @return
+     */
+    public String decrypt(String cipherText) {
+        try {
+            byte[] encrypted1 = Base64.decode(cipherText, Base64.NO_WRAP);
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
+            cipher.init(Cipher.DECRYPT_MODE, keyspec);
+            byte[] original = cipher.doFinal(encrypted1);
+            String originalString = new String(original);
+            return originalString;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 130 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/security/SecurityEditor.java

@@ -0,0 +1,130 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security;
+
+/**
+ * Created by fancyLou on 2020-09-29.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 自动加密Editor
+ */
+public class SecurityEditor implements SharedPreferences.Editor {
+
+    private SharedPreferences mSharedPreferences;
+    private Context mContext;
+    private SharedPreferences.Editor mEditor;
+
+    /**
+     * constructor
+     */
+    public SecurityEditor(SharedPreferences sharedPreferences, Context context){
+        this.mContext = context;
+        this.mSharedPreferences = sharedPreferences;
+        this.mEditor = mSharedPreferences.edit();
+    }
+
+    @Override
+    public SharedPreferences.Editor putString(String key, String value) {
+        mEditor.putString(encryptPreference(key), encryptPreference(value));
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor putStringSet(String key, Set<String> values) {
+        final Set<String> encryptSet = new HashSet<>();
+        for (String value : values){
+            encryptSet.add(encryptPreference(value));
+        }
+        mEditor.putStringSet(encryptPreference(key), encryptSet);
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor putInt(String key, int value) {
+        mEditor.putString(encryptPreference(key), encryptPreference(Integer.toString(value)));
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor putLong(String key, long value) {
+        mEditor.putString(encryptPreference(key), encryptPreference(Long.toString(value)));
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor putFloat(String key, float value) {
+        mEditor.putString(encryptPreference(key), encryptPreference(Float.toString(value)));
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor putBoolean(String key, boolean value) {
+        mEditor.putString(encryptPreference(key), encryptPreference(Boolean.toString(value)));
+        return this;
+    }
+
+    @Override
+    public SharedPreferences.Editor remove(String key) {
+        mEditor.remove(encryptPreference(key));
+        return this;
+    }
+
+    /**
+     * encrypt function
+     * @return cipherText base64
+     */
+    private String encryptPreference(String plainText){
+        return EncryptUtil.getInstance(mContext).encrypt(plainText);
+    }
+
+    /**
+     * decrypt function
+     * @return plainText
+     */
+    private String decryptPreference(String cipherText){
+        return EncryptUtil.getInstance(mContext).decrypt(cipherText);
+    }
+
+    /**
+     * Mark in the editor to remove all values from the preferences.
+     * @return this
+     */
+    @Override
+    public SharedPreferences.Editor clear() {
+        mEditor.clear();
+        return this;
+    }
+
+    /**
+     * 提交数据到本地
+     * @return Boolean 判断是否提交成功
+     */
+    @Override
+    public boolean commit() {
+
+        return mEditor.commit();
+    }
+
+    /**
+     * Unlike commit(), which writes its preferences out to persistent storage synchronously,
+     * apply() commits its changes to the in-memory SharedPreferences immediately but starts
+     * an asynchronous commit to disk and you won't be notified of any failures.
+     */
+    @Override
+    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+    public void apply() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
+            mEditor.apply();
+        } else {
+            commit();
+        }
+    }
+}

+ 170 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/security/SecuritySharedPreference.java

@@ -0,0 +1,170 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.security;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 自动加密SharedPreference
+ * Created by Max on 2016/11/23.
+ */
+
+public class SecuritySharedPreference implements SharedPreferences {
+
+    private SharedPreferences mSharedPreferences;
+    private static final String TAG = SecuritySharedPreference.class.getName();
+    private Context mContext;
+
+    /**
+     * constructor
+     * @param context should be ApplicationContext not activity
+     * @param name file name
+     * @param mode context mode
+     */
+    public SecuritySharedPreference(Context context, String name, int mode){
+        mContext = context;
+        if (TextUtils.isEmpty(name)){
+            mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+        } else {
+            mSharedPreferences =  context.getSharedPreferences(name, mode);
+        }
+
+    }
+
+    @Override
+    public Map<String, String> getAll() {
+        final Map<String, ?> encryptMap = mSharedPreferences.getAll();
+        final Map<String, String> decryptMap = new HashMap<>();
+        for (Map.Entry<String, ?> entry : encryptMap.entrySet()){
+            Object cipherText = entry.getValue();
+            if (cipherText != null){
+                decryptMap.put(entry.getKey(), entry.getValue().toString());
+            }
+        }
+        return decryptMap;
+    }
+
+    /**
+     * encrypt function
+     * @return cipherText base64
+     */
+    private String encryptPreference(String plainText){
+        return EncryptUtil.getInstance(mContext).encrypt(plainText);
+    }
+
+    /**
+     * decrypt function
+     * @return plainText
+     */
+    private String decryptPreference(String cipherText){
+        return EncryptUtil.getInstance(mContext).decrypt(cipherText);
+    }
+
+    @Nullable
+    @Override
+    public String getString(String key, String defValue) {
+        final String encryptValue = mSharedPreferences.getString(encryptPreference(key), null);
+        return encryptValue == null ? defValue : decryptPreference(encryptValue);
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getStringSet(String key, Set<String> defValues) {
+        final Set<String> encryptSet = mSharedPreferences.getStringSet(encryptPreference(key), null);
+        if (encryptSet == null){
+            return defValues;
+        }
+        final Set<String> decryptSet = new HashSet<>();
+        for (String encryptValue : encryptSet){
+            decryptSet.add(decryptPreference(encryptValue));
+        }
+        return decryptSet;
+    }
+
+    @Override
+    public int getInt(String key, int defValue) {
+        final String encryptValue = mSharedPreferences.getString(encryptPreference(key), null);
+        if (encryptValue == null) {
+            return defValue;
+        }
+        return Integer.parseInt(decryptPreference(encryptValue));
+    }
+
+    @Override
+    public long getLong(String key, long defValue) {
+        final String encryptValue = mSharedPreferences.getString(encryptPreference(key), null);
+        if (encryptValue == null) {
+            return defValue;
+        }
+        return Long.parseLong(decryptPreference(encryptValue));
+    }
+
+    @Override
+    public float getFloat(String key, float defValue) {
+        final String encryptValue = mSharedPreferences.getString(encryptPreference(key), null);
+        if (encryptValue == null) {
+            return defValue;
+        }
+        return Float.parseFloat(decryptPreference(encryptValue));
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defValue) {
+        final String encryptValue = mSharedPreferences.getString(encryptPreference(key), null);
+        if (encryptValue == null) {
+            return defValue;
+        }
+        return Boolean.parseBoolean(decryptPreference(encryptValue));
+    }
+
+    @Override
+    public boolean contains(String key) {
+        return mSharedPreferences.contains(encryptPreference(key));
+    }
+
+    @Override
+    public SecurityEditor edit() {
+        return new SecurityEditor(mSharedPreferences, mContext);
+    }
+
+    @Override
+    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+        mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
+    }
+
+    @Override
+    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
+    }
+
+    /**
+     * 处理加密过渡
+     */
+    public void handleTransition(){
+        Map<String, ?> oldMap = mSharedPreferences.getAll();
+        Map<String, String> newMap = new HashMap<>();
+        for (Map.Entry<String, ?> entry : oldMap.entrySet()){
+            Log.i(TAG, "key:"+entry.getKey()+", value:"+ entry.getValue());
+            newMap.put(encryptPreference(entry.getKey()), encryptPreference(entry.getValue().toString()));
+        }
+        Editor editor = mSharedPreferences.edit();
+        editor.clear().commit();
+        for (Map.Entry<String, String> entry : newMap.entrySet()){
+            editor.putString(entry.getKey(), entry.getValue());
+        }
+        editor.commit();
+    }
+
+
+}