4545 * <p>
4646 * This class provides important - but nevertheless imperfect - protection
4747 * against simple attacks by casual snoopers. It is crucial to remember that
48- * even encrypted data may still be susceptible to attacks, especially on rooted
48+ * even encrypted data may still be susceptible to attacks, especially on rooted devices
4949 * <p>
5050 *
5151 * TODO: handle migration of keys
5454 */
5555public class SecurePreferences implements SharedPreferences {
5656
57- /**
58- * Used to handle updates to the way the secure
59- */
60- private static final int VERSION = 1 ;
61- private static final String VERSION_PREF_KEY = "SECURE_PREF_VERSION_KEY" ;
62-
6357 private static SharedPreferences sFile ;
6458 private static AesCbcWithIntegrity .SecretKeys sKeys ;
6559 private static boolean sLoggingEnabled = false ;
@@ -72,60 +66,43 @@ public class SecurePreferences implements SharedPreferences {
7266
7367 private static final String TAG = SecurePreferences .class .getName ();
7468
75-
7669 /**
77- * Check the internal version with the stored version, to see if upgrade needed.
78- * @return
70+ * Cycle through the unencrypt all the current prefs to mem cache, clear, then encypt with key generated from new password
71+ * @param newPassword
7972 */
80- private boolean isUpgradeNeeded (){
81- int currentVersion = SecurePreferences .sFile .getInt (VERSION_PREF_KEY , 0 );
82- if (currentVersion <VERSION ){
83- return true ;
84- }
85- return false ;
86- }
87-
88- private void upgradePrefsAndRenewKeys (){
89- int currentVersion = SecurePreferences .sFile .getInt (VERSION_PREF_KEY , 0 );
90- if (currentVersion == 0 && VERSION ==1 ){
91-
92- //get the current key name and key
93-
94- //gen new key name
95-
96- //gen new key
97-
98- Map <String , ?> allOfThePrefs = SecurePreferences .sFile .getAll ();
99- Iterator <String > keys = allOfThePrefs .keySet ().iterator ();
100- for (int i = 0 ; keys .hasNext (); i ++) {
101- String prefKey = keys .next ();
102- Object prefValue = allOfThePrefs .get (prefKey );
103- if (prefValue instanceof String ){
104- //all the encrypted values will be Strings
105- final String prefValueString = (String )prefValue ;
106-
107- final String plainTextPrefKey = decrypt (prefKey );
108- final String plainTextPrefValue = decrypt (prefValueString );
109-
110-
111-
112- }
73+ public void handlePasswordChange (String newPassword , Context context ) throws GeneralSecurityException {
74+
75+ AesCbcWithIntegrity .SecretKeys newKey = AesCbcWithIntegrity .generateKeyFromPassword (newPassword , getDeviceSerialNumber (context ));
76+
77+ Map <String , ?> allOfThePrefs = SecurePreferences .sFile .getAll ();
78+ Map <String , String > unencryptedPrefs = new HashMap <String , String >(allOfThePrefs .size ());
79+ Iterator <String > keys = allOfThePrefs .keySet ().iterator ();
80+ while (keys .hasNext ()) {
81+ String prefKey = keys .next ();
82+ Object prefValue = allOfThePrefs .get (prefKey );
83+ if (prefValue instanceof String ){
84+ //all the encrypted values will be Strings
85+ final String prefValueString = (String )prefValue ;
86+ final String plainTextPrefValue = decrypt (prefValueString );
87+ unencryptedPrefs .put (prefKey , plainTextPrefValue );
11388 }
114-
115-
116-
117-
11889 }
90+ destoryKeys ();
91+ SharedPreferences .Editor editor = edit ();
92+ editor .clear ();
93+ editor .commit ();
94+
95+ sKeys = newKey ;
96+ Iterator <String > unencryptedPrefsKeys = allOfThePrefs .keySet ().iterator ();
97+ while (unencryptedPrefsKeys .hasNext ()) {
98+ String prefKey = unencryptedPrefsKeys .next ();
99+ String prefPlainText = unencryptedPrefs .get (prefKey );
100+ editor .putString (prefKey , encrypt (prefPlainText ));
101+ }
102+ editor .commit ();
119103 }
120104
121105
122- /**
123- * This assumed the SecurePrefs is
124- * @param newPassword
125- */
126- public void handlePasswordChange (String newPassword ){
127- //TODO:
128- }
129106
130107
131108 /**
@@ -266,7 +243,7 @@ private static String getDeviceSerialNumber(Context context) {
266243 * @param prefKey
267244 * @return
268245 */
269- private static String hashPrefKey (String prefKey ) {
246+ public static String hashPrefKey (String prefKey ) {
270247 final MessageDigest digest ;
271248 try {
272249 digest = MessageDigest .getInstance ("SHA-256" );
@@ -308,6 +285,11 @@ private static String encrypt(String cleartext) {
308285 return null ;
309286 }
310287
288+ /**
289+ *
290+ * @param ciphertext
291+ * @return decrypted plain text, unless decryption fails, in which case null
292+ */
311293 private static String decrypt (final String ciphertext ) {
312294 if (TextUtils .isEmpty (ciphertext )) {
313295 return ciphertext ;
@@ -328,21 +310,35 @@ private static String decrypt(final String ciphertext) {
328310 return null ;
329311 }
330312
313+ /**
314+ *
315+ * @return map of with decrypted values (excluding the key if present)
316+ */
331317 @ Override
332318 public Map <String , String > getAll () {
319+ //wont be null as per http://androidxref.com/5.1.0_r1/xref/frameworks/base/core/java/android/app/SharedPreferencesImpl.java
333320 final Map <String , ?> encryptedMap = SecurePreferences .sFile .getAll ();
334321 final Map <String , String > decryptedMap = new HashMap <String , String >(
335322 encryptedMap .size ());
336- for (Entry <String , ?> entry : encryptedMap .entrySet ()) {
337- try {
338- //they should all be strings
339- decryptedMap .put (entry .getKey (),
340- SecurePreferences .decrypt (entry .getValue ().toString ()));
341- } catch (Exception e ) {
342- // Ignore unencrypted key/value pairs
343- }
344- }
345- return decryptedMap ;
323+ for (Entry <String , ?> entry : encryptedMap .entrySet ()) {
324+ try {
325+ Object cipherText = entry .getValue ();
326+ //don't include the key
327+ if (cipherText !=null && !cipherText .equals (sKeys .toString ())){
328+ //the prefs should all be strings
329+ decryptedMap .put (entry .getKey (),
330+ SecurePreferences .decrypt (cipherText .toString ()));
331+ }
332+ } catch (Exception e ) {
333+ if (sLoggingEnabled ) {
334+ Log .w (TAG , "error during getAll" , e );
335+ }
336+ // Ignore issues that unencrypted values and use instead raw cipher text string
337+ decryptedMap .put (entry .getKey (),
338+ entry .getValue ().toString ());
339+ }
340+ }
341+ return decryptedMap ;
346342 }
347343
348344 @ Override
0 commit comments