2017-02-20 156 views
0

在我的應用程序中,我使用用戶密碼作爲加密介質的加密密鑰。我正在使用PBEWithMD5AndDES加密媒體,並且這與使用存儲在共享首選項中的密碼正常工作。現在爲了達到一定程度的安全性,我從共享首選項中刪除密碼,並使用只在應用程序會話期間保持活動的單例(因爲應用程序會自動註銷,要求輸入密碼)。下面是我的單身:單例實例返回null

public class Credentials { 

private static Credentials dataObject = null; 

private Credentials() { 
// left blank intentionally 
} 

public static Credentials getInstance() { 
if (dataObject == null) 
    dataObject = new Credentials(); 
return dataObject; 
} 

private char[] user_password; 

public char[] getUser_password() { 

return user_password; 
} 

public void setUser_password(char[] user_password) { 

this.user_password = user_password; 
} 
} 

密碼從記憶清零,如果應用程序註銷,或由用戶註銷或也被打爛。然而有時我在獲取密碼時會得到一個空指針。

char[] pswd = Credentials.getInstance().getUser_password(); 

什麼可能導致這種情況?除了單身人士以外,還有其他方法可以使用嗎?

+1

'Credentials.getInstance()'不能返回'null'。這就是你從來沒有在該實例上調用過'setUser_password',所以'Credentials.getInstance()。getUser_password()'做。如果你認爲你已經調用了'setUser_password'(帶有一個非空參數),問題是你的單例類不是線程安全的。 –

+0

@James Wahome:你的'密碼'是空的而不是單身! – AndiGeeky

+0

爲單例實例添加適當的空對象檢查,並通過其函數返回值。 –

回答

-1

或者,您可以使用內置的Sqlite數據庫存儲密碼,但我仍然建議您將其保存爲加密以獲得最大程度的保護。

2)創建一個實體對象來存儲密碼:

public class Password { 
    int password_id; // will be auto-increamted 
    String password; 

    public Password(int password_id, String password) { 
     this.password_id = password_id; 
     this.password = password; 
    } 
// getter/setters ... 
} 

2)創建一個SQLite實用對象:

public class SQLiteDBAdapter { 

    protected static final String DATABASE_NAME = "mydb"; 
    protected static final int DATABASE_VERSION = 1; 

    protected Context context; 
    protected static DatabaseHelper mDbHelper; 

    public static final String TABLE_PASSWORD = "tbl_password"; 
    // columns 
    public static final String PASSWORD_ID = "_id"; 
    public static final String PASSWORD = "password"; 
    // create table string 
    private static final String CREATE_TABLE_PASSWORD = 
      "CREATE TABLE if not exists " + TABLE_PASSWORD + " (" + 
        PASSWORD_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + 
        PASSWORD + " TEXT NOT NULL);"; 

    public SQLiteDBAdapter(Context context) { 
     context = context.getApplicationContext(); 
    } 

    public SQLiteDatabase openDb() { 
     if (mDbHelper == null) { 
      mDbHelper = new DatabaseHelper(mContext); 
     } 
     return mDbHelper.getWritableDatabase(); 
    } 

    protected static class DatabaseHelper extends SQLiteOpenHelper { 
     // ------------------------------------------------------------------------------------------- 
     public DatabaseHelper(Context context) { 
      super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     } 
     // ------------------------------------------------------------------------------------------- 
     @Override 
     public void onCreate(SQLiteDatabase db) { 
      db.execSQL(CREATE_TABLE_PASSWORD); 
     } 
     // ------------------------------------------------------------------------------------------- 
     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
      Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + 
        newVersion + ", which will destroy all old data"); 
      db.execSQL("DROP TABLE IF EXISTS routes"); 
      onCreate(db); 
     } 
    } 
} 

3)延長一個SQLite您可以在4個步驟執行此操作對象來操作表格(CRUD操作):

public class PasswordDbAdapter extends SQLiteDBAdapter { 

    private SQLiteDatabase db; 

    // these are column corresponding indices 
    public static final int INDEX_PASSWORD_ID = 0; // an auto-increment 
    public static final int INDEX_PASSWORD = 1; 

    public PasswordDbAdapter(Context context) { 
     super(context); 
    } 

    public void addPassword(String password) { 
     db = openDb(); 
     ContentValues values = new ContentValues(); 
     values.put(PASSWORD, password); 
     db.insert(TABLE_PASSWORD, null, values); 
    } 

    public void updatePassword(String password) { 
     db = openDb(); 
     ContentValues values = new ContentValues(); 
     values.put(PASSWORD, password); 
     db.update(TABLE_PASSWORD, values, null); 
    } 

    public void deletePassword() { 
     db = openDb(); 
     db.delete(TABLE_PASSWORD, null, null); 
    } 

    public boolean isEmpty() { 
     db = openDb(); 
     boolean empty = true; 
     Cursor cur = db.rawQuery("SELECT COUNT(*) FROM " + TABLE_PASSWORD, null); 
     if (cur != null && cur.moveToFirst()) { 
      empty = (cur.getInt (0) == 0); 
     } 
     cur.close(); 
     return empty; 
    } 

    public Password fetchPassword() { // ok because there's only one password record 
     db = openDb(); 
     Cursor cursor = db.query(TABLE_PASSWORD, new String[]{PASSWORD_ID, PASSWORD}, 
       null, null, null, null, null, null); 
     if (cursor != null && 
      cursor.moveToFirst()) { 
      return new Password(
        cursor.getString(INDEX_PASSWORD_ID), 
        cursor.getInt(INDEX_PASSWORD)); 
     } 
     return null; 
    } 
} 

4)最後,保存/更新/檢索passwor d根據需要:

public class MainActivity extends AppCompatActivity { 
    private PasswordDbAdapter passwordDB; 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     ... 
     // initialize the password db 
     passwordDB = new PasswordDbAdapter(this); 

     // check if password record exists 
     if (passwordDB.isEmpty() { 
      // save a new copy 
      passwordDB.addPassword("the_password"); // more secure if it is saved encrypted 
     } else { 
      // update it 
      passwordDB.updatePassword("the_password"); 
     } 

    } 
    ... 
    public String fetchPassword() { 
     return passwordDB.fetchPassword(); // or first decrypt it, then return it 
    } 
} 
+0

這是錯誤的......通讀這個問題「現在實現安全級別我從共享首選項中刪除密碼並使用只在應用會話期間保持活動狀態的單例」我的目標是將其存儲在運行時內存,而不是將其保存在磁盤存儲的共享首選項中。在任何情況下,您都不應該以純文本格式存儲密碼。 –

+0

Android單身人士可能會遇到問題。但是,請參閱http://stackoverflow.com/questions/16517702/singleton-in-android。現在,關於使用Sqlite,我的建議是存儲加密的密碼。更簡單的是,您可以散列密碼,然後使用散列代替。 – nkmuturi