Skip to content
This repository was archived by the owner on Mar 1, 2022. It is now read-only.

Commit 1a407df

Browse files
committed
Handle user password change, getAll fixed to not return key, haskprefkey made public to be used by pref Activity/Fragments
1 parent 0c351a6 commit 1a407df

File tree

1 file changed

+61
-65
lines changed

1 file changed

+61
-65
lines changed

library/src/main/java/com/securepreferences/SecurePreferences.java

Lines changed: 61 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
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
@@ -54,12 +54,6 @@
5454
*/
5555
public 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

Comments
 (0)