2017-02-20 162 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