/** * Constructor. * * @param context the caller's context */ public SecurePreferencesOld(Context context) { // Proxy design pattern if (SecurePreferencesOld.sFile == null) { SecurePreferencesOld.sFile = PreferenceManager.getDefaultSharedPreferences(context); } // Initialize encryption/decryption key try { final String key = SecurePreferencesOld.generateAesKeyName(context); String value = SecurePreferencesOld.sFile.getString(key, null); if (value == null) { value = SecurePreferencesOld.generateAesKeyValue(); SecurePreferencesOld.sFile.edit().putString(key, value).commit(); } SecurePreferencesOld.sKey = SecurePreferencesOld.decode(value); } catch (Exception e) { if (sLoggingEnabled) { Log.e(TAG, "Error init:" + e.getMessage()); } throw new IllegalStateException(e); } // initialize OnSecurePreferencesChangeListener HashMap sOnSharedPreferenceChangeListeners = new HashMap<OnSharedPreferenceChangeListener, OnSharedPreferenceChangeListener>(10); }
@Override @TargetApi(Build.VERSION_CODES.HONEYCOMB) public SharedPreferences.Editor putStringSet(String key, Set<String> values) { final Set<String> encryptedValues = new HashSet<String>(values.size()); for (String value : values) { encryptedValues.add(SecurePreferencesOld.encrypt(value)); } mEditor.putStringSet(SecurePreferencesOld.encrypt(key), encryptedValues); return this; }
@Override public boolean getBoolean(String key, boolean defaultValue) { final String encryptedValue = SecurePreferencesOld.sFile.getString(SecurePreferencesOld.encrypt(key), null); if (encryptedValue == null) { return defaultValue; } try { return Boolean.parseBoolean(SecurePreferencesOld.decrypt(encryptedValue)); } catch (NumberFormatException e) { throw new ClassCastException(e.getMessage()); } }
@Override @TargetApi(Build.VERSION_CODES.HONEYCOMB) public Set<String> getStringSet(String key, Set<String> defaultValues) { final Set<String> encryptedSet = SecurePreferencesOld.sFile.getStringSet(SecurePreferencesOld.encrypt(key), null); if (encryptedSet == null) { return defaultValues; } final Set<String> decryptedSet = new HashSet<String>(encryptedSet.size()); for (String encryptedValue : encryptedSet) { decryptedSet.add(SecurePreferencesOld.decrypt(encryptedValue)); } return decryptedSet; }
@Override public Map<String, String> getAll() { final Map<String, ?> encryptedMap = SecurePreferencesOld.sFile.getAll(); final Map<String, String> decryptedMap = new HashMap<String, String>(encryptedMap.size()); for (Entry<String, ?> entry : encryptedMap.entrySet()) { try { decryptedMap.put( SecurePreferencesOld.decrypt(entry.getKey()), SecurePreferencesOld.decrypt(entry.getValue().toString())); } catch (Exception e) { // Ignore unencrypted key/value pairs } } return decryptedMap; }
private static String generateAesKeyName(Context context) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException { final char[] password = context.getPackageName().toCharArray(); final byte[] salt = getDeviceSerialNumber(context).getBytes(); SecretKey key; try { // TODO: what if there's an OS upgrade and now supports the primary // PBE key = SecurePreferencesOld.generatePBEKey( password, salt, PRIMARY_PBE_KEY_ALG, ITERATIONS, KEY_SIZE); } catch (NoSuchAlgorithmException e) { // older devices may not support the have the implementation try // with a weaker // algorthm key = SecurePreferencesOld.generatePBEKey( password, salt, BACKUP_PBE_KEY_ALG, ITERATIONS, KEY_SIZE); } return SecurePreferencesOld.encode(key.getEncoded()); }
@Deprecated private static String decrypt(String ciphertext) { if (ciphertext == null || ciphertext.length() == 0) { return ciphertext; } try { final Cipher cipher = Cipher.getInstance(AES_KEY_ALG, PROVIDER); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(SecurePreferencesOld.sKey, AES_KEY_ALG)); return new String(cipher.doFinal(SecurePreferencesOld.decode(ciphertext)), "UTF-8"); } catch (Exception e) { if (sLoggingEnabled) { Log.w(TAG, "decrypt", e); } return null; } }
@Deprecated private static String generateAesKeyValue() throws NoSuchAlgorithmException { // Do *not* seed secureRandom! Automatically seeded from system entropy final SecureRandom random = new SecureRandom(); // Use the largest AES key length which is supported by the OS final KeyGenerator generator = KeyGenerator.getInstance("AES"); try { generator.init(KEY_SIZE, random); } catch (Exception e) { try { generator.init(192, random); } catch (Exception e1) { generator.init(128, random); } } return SecurePreferencesOld.encode(generator.generateKey().getEncoded()); }
@Override public boolean contains(String key) { return SecurePreferencesOld.sFile.contains(SecurePreferencesOld.encrypt(key)); }
@Override public SharedPreferences.Editor remove(String key) { mEditor.remove(SecurePreferencesOld.encrypt(key)); return this; }
@Override public SharedPreferences.Editor putBoolean(String key, boolean value) { mEditor.putString( SecurePreferencesOld.encrypt(key), SecurePreferencesOld.encrypt(Boolean.toString(value))); return this; }
@Override public SharedPreferences.Editor putFloat(String key, float value) { mEditor.putString( SecurePreferencesOld.encrypt(key), SecurePreferencesOld.encrypt(Float.toString(value))); return this; }
@Override public SharedPreferences.Editor putLong(String key, long value) { mEditor.putString( SecurePreferencesOld.encrypt(key), SecurePreferencesOld.encrypt(Long.toString(value))); return this; }
@Override public SharedPreferences.Editor putInt(String key, int value) { mEditor.putString( SecurePreferencesOld.encrypt(key), SecurePreferencesOld.encrypt(Integer.toString(value))); return this; }
/** * This is useful for storing values that have be encrypted by something else * * @param key - encrypted as usual * @param value will not be encrypted * @return */ public SharedPreferences.Editor putStringNoEncrypted(String key, String value) { mEditor.putString(SecurePreferencesOld.encrypt(key), value); return this; }
@Override public String getString(String key, String defaultValue) { final String encryptedValue = SecurePreferencesOld.sFile.getString(SecurePreferencesOld.encrypt(key), null); return (encryptedValue != null) ? SecurePreferencesOld.decrypt(encryptedValue) : defaultValue; }
/** * Added to get a values as as it can be useful to store values that are already encrypted and * encoded * * @param key * @param defaultValue * @return Unencrypted value of the key or the defaultValue if */ public String getStringUnencrypted(String key, String defaultValue) { final String nonEncryptedValue = SecurePreferencesOld.sFile.getString(SecurePreferencesOld.encrypt(key), null); return (nonEncryptedValue != null) ? nonEncryptedValue : defaultValue; }