├── .gitignore ├── .travis.yml ├── README.md ├── code-quality ├── android_checks.xml └── suppressions.xml ├── pom.xml ├── secure-preferences ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ ├── com │ │ └── github │ │ │ └── kovmarci86 │ │ │ └── android │ │ │ └── secure │ │ │ └── preferences │ │ │ ├── SecureFactory.java │ │ │ ├── SecureSharedPreferences.java │ │ │ ├── SecuredEditor.java │ │ │ ├── encryption │ │ │ ├── EncryptionAlgorithm.java │ │ │ ├── EncryptionException.java │ │ │ └── EncryptionHelper.java │ │ │ └── util │ │ │ └── SecureUtils.java │ │ └── edu │ │ └── gmu │ │ └── tec │ │ └── scout │ │ └── utilities │ │ └── Encryption.java │ └── test │ └── java │ └── com │ └── github │ └── kovmarci86 │ └── android │ └── secure │ └── preferences │ ├── SecureFactoryTest.java │ ├── SecureSharedPreferencesTest.java │ ├── SecuredEditorTest.java │ ├── encryption │ ├── EncryptionExceptionTest.java │ └── EncryptionHelperTest.java │ └── util │ └── SecureUtilsTest.java ├── secure-storage-it ├── .gitignore ├── AndroidManifest.xml ├── pom.xml ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-ldpi │ │ └── icon.png │ ├── drawable-mdpi │ │ └── icon.png │ ├── layout │ │ └── main.xml │ └── values │ │ └── strings.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── kovmarci86 │ └── android │ └── secure │ └── preferences │ ├── SecureFactoryFunctionalTest.java │ └── SecureSharedPreferencesFunctionalTest.java ├── secure-storage-sample-app ├── .gitignore ├── AndroidManifest.xml ├── androlog │ └── androlog.properties ├── pom.xml ├── proguard.conf ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-ldpi │ │ └── icon.png │ ├── drawable-mdpi │ │ └── icon.png │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_secure_login.xml │ └── values │ │ ├── strings.xml │ │ └── strings_activity_secure_login.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── kovmarci86 │ └── android │ └── secure │ ├── MainActivity.java │ ├── SecureLogin.java │ └── service │ └── settings │ └── UserDataServce.java └── test-key.keystore /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .project 3 | .settings 4 | .classpath 5 | bin/ 6 | gen/ 7 | .checkstyle 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: 3 | # download the latest android sdk and unzip 4 | - wget http://dl.google.com/android/android-sdk_r21-linux.tgz 5 | - tar -zxf android-sdk_r21-linux.tgz 6 | # setup your ANDROID_HOME and PATH environment variables 7 | # use ~/builds/[Github username]/[project]/android-sdk-linux 8 | - export ANDROID_HOME=`pwd`/android-sdk-linux 9 | - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools 10 | - android list sdk -a 11 | - android update sdk --filter 1,2,12 --no-ui --force 12 | # - ls ${ANDROID_HOME}/platform-tools 13 | # - ls -l /home/travis/build/kovmarci86/android-secure-preferences/secure-storage-sample-app 14 | # - ls -l /home/travis/build/kovmarci86/android-secure-preferences/android-sdk-linux/platform-tools/ 15 | # - ls -l /home/travis/build/kovmarci86/android-secure-preferences/android-sdk-linux/platforms/ 16 | # - chmod 777 /home/travis/build/kovmarci86/android-secure-preferences/android-sdk-linux/platform-tools/* 17 | # - /home/travis/build/kovmarci86/android-secure-preferences/android-sdk-linux/platform-tools/aapt 18 | install: 19 | - echo "Building core" 20 | - cd secure-preferences 21 | - mvn test 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-secure-preferences 2 | ========================== 3 | 4 | About 5 | ----- 6 | This project uses the Encryption class from: 7 | http://www.java2s.com/Code/Android/Security/AESEncryption.htm 8 | 9 | Gives an implementation of SharedPreferences, which encrypts given values with AES. 10 | 11 | 12 | Environment setup 13 | ----------------- 14 | - install maven 15 | - install Android sdk 16 | - install sdk deployer ( https://github.com/mosabua/maven-android-sdk-deployer ) 17 | - check out source to a directory. 18 | 19 | Build 20 | ----- 21 | - mvn clean install 22 | 23 | Android compatibility 24 | --------------------- 25 | Project requires API level 8 due to Base64 Android API level requirements. 26 | 27 | Binary 28 | ------ 29 | Until GitHub deprecates download section, use: 30 | 31 | https://github.com/kovmarci86/android-secure-preferences/downloads 32 | -------------------------------------------------------------------------------- /code-quality/android_checks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /code-quality/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.kovmarci86.android.secure 6 | secure-storage-parent 7 | pom 8 | secure-storage - Parent 9 | 10 | 11 | secure-storage-sample-app 12 | secure-storage-it 13 | secure-preferences 14 | 15 | 16 | 17 | 18 | Marcell Kovacs (kovmarci86) 19 | 20 | Developer 21 | 22 | 23 | NoTiCe 24 | 25 | 26 | 27 | Tomas Prochazka (tprochazka) 28 | 29 | Contributor 30 | 31 | 32 | tprochazka 33 | 34 | 35 | 36 | 37 | 38 | scm:git:kovmarci86@github.com/kovmarci86/android-secure-preferences.git 39 | scm:git:kovmarci86@github.com/kovmarci86/android-secure-preferences.git 40 | git:git:kovmarci86@github.com/kovmarci86/android-secure-preferences.git 41 | 42 | 43 | 44 | 45 | starhost-andsecureprefs 46 | ftp://starhost.hu/web/andsecureprefs/ 47 | 48 | 49 | 50 | 51 | 4.1.1.4 52 | github 53 | 54 | android-secure-preferences 55 | kovmarci86 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-javadoc-plugin 63 | 2.9 64 | 65 | public 66 | ApiDocs 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | com.github.kovmarci86.android.secure 76 | secure-storage-sample-app 77 | 0.0.2-SNAPSHOT 78 | jar 79 | provided 80 | 81 | 82 | com.github.kovmarci86.android.secure 83 | secure-storage-sample-app 84 | apk 85 | 0.0.2-SNAPSHOT 86 | ${zipaligned-classifier} 87 | provided 88 | 89 | 90 | com.github.kovmarci86.android.secure 91 | secure-preferences 92 | 0.0.2-SNAPSHOT 93 | 94 | 95 | com.google.android 96 | android 97 | ${platform.version} 98 | provided 99 | 100 | 101 | com.google.android 102 | android-test 103 | ${platform.version} 104 | provided 105 | 106 | 107 | 108 | 109 | de.akquinet.android.androlog 110 | androlog 111 | 1.0.5 112 | 113 | 114 | 115 | junit 116 | junit 117 | 4.10 118 | provided 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | com.github.github 127 | downloads-maven-plugin 128 | 0.5 129 | 130 | ${project.version} release of ${project.name} 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | 138 | 139 | com.jayway.maven.plugins.android.generation2 140 | android-maven-plugin 141 | 3.6.0 142 | true 143 | 144 | ${project.basedir}/AndroidManifest.xml 145 | ${project.basedir}/assets 146 | ${project.basedir}/res 147 | ${project.basedir}/src/main/native 148 | 149 | 8 150 | 151 | true 152 | 153 | 154 | 155 | maven-compiler-plugin 156 | 2.5.1 157 | true 158 | 159 | 1.6 160 | 1.6 161 | 162 | 163 | 164 | maven-enforcer-plugin 165 | 1.0 166 | 167 | 168 | maven-release-plugin 169 | 2.1 170 | 171 | true 172 | 173 | 174 | 175 | maven-jarsigner-plugin 176 | 1.2 177 | true 178 | 179 | true 180 | 181 | ${project.build.directory}/${project.build.finalName}.${project.packaging} 182 | true 183 | true 184 | ${sign.keystore} 185 | ${sign.alias} 186 | ${sign.storepass} 187 | ${sign.keypass} 188 | 189 | 190 | 191 | com.pyx4me 192 | proguard-maven-plugin 193 | 2.0.4 194 | 195 | 196 | net.sf.proguard 197 | proguard 198 | 4.4 199 | runtime 200 | 201 | 202 | 203 | 4.4 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-site-plugin 209 | 3.2 210 | 211 | 212 | org.apache.maven.wagon 213 | wagon-ssh 214 | 2.3 215 | 216 | 217 | org.apache.maven.wagon 218 | wagon-ftp 219 | 1.0-beta-6 220 | 221 | 222 | 223 | 224 | 225 | org.codehaus.mojo 226 | emma-maven-plugin 227 | 1.0-alpha-3 228 | 229 | 230 | org.apache.maven.plugins 231 | maven-checkstyle-plugin 232 | 2.9.1 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-project-info-reports-plugin 237 | 2.6 238 | 239 | true 240 | true 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | release 253 | 254 | 255 | performRelease 256 | true 257 | 258 | 259 | 260 | 261 | 262 | com.jayway.maven.plugins.android.generation2 263 | android-maven-plugin 264 | ${android-plugin} 265 | 266 | true 267 | 268 | 269 | 270 | 271 | maven-enforcer-plugin 272 | 273 | 274 | enforce-signing-properties 275 | 276 | enforce 277 | 278 | 279 | 280 | 281 | sign.keystore 282 | The 'sign.keystore' property is missing. It must 283 | contain the path to the 284 | keystore used to sign the 285 | application. 286 | 287 | 288 | 289 | 290 | ${sign.keystore} 291 | 292 | The 'sign.keystore' property does not point to a 293 | file. It must contain the 294 | path to the keystore used to sign 295 | the application. 296 | 297 | 298 | 299 | sign.alias 300 | The 'sign.alias' property is missing. It must 301 | contain the key alias used to 302 | sign the application. 303 | 304 | 305 | 306 | sign.storepass 307 | The 'sign.storepass' property is missing. It must 308 | contain the password of 309 | the keystore used to sign the 310 | application. 311 | 312 | 313 | 314 | sign.keypass 315 | The 'sign.keypass' property is missing. It must 316 | contain the password of the 317 | key used to sign the application. 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | linux 330 | 331 | 332 | unix 333 | 334 | 335 | 336 | ${java.home}/jre/lib/rt.jar 337 | ${java.home}/jre/lib/jsse.jar 338 | 339 | 340 | 342 | 343 | mac 344 | 345 | 346 | mac 347 | 348 | 349 | 350 | 351 | 352 | 353 | ${java.home}/../Classes/classes.jar 354 | ${java.home}/../Classes/jsse.jar 355 | 356 | 357 | 358 | windows 359 | 360 | 361 | windows 362 | 363 | 364 | 365 | ${java.home}/jre/lib/rt.jar 366 | ${java.home}/jre/lib/jsse.jar 367 | 368 | 369 | 370 | 371 | http://starhost.hu/andsecureprefs/ 372 | 0.0.2-SNAPSHOT 373 | 374 | 375 | 376 | -------------------------------------------------------------------------------- /secure-preferences/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /*.iml 3 | /out/ 4 | -------------------------------------------------------------------------------- /secure-preferences/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.kovmarci86.android.secure 6 | secure-storage-parent 7 | 0.0.2-SNAPSHOT 8 | 9 | secure-preferences 10 | SecureSharedPreferences 11 | 12 | 13 | com.google.android 14 | android 15 | provided 16 | 17 | 18 | org.slf4j 19 | slf4j-api 20 | 1.7.2 21 | 22 | 23 | junit 24 | junit 25 | 26 | 27 | org.easymock 28 | easymock 29 | 3.1 30 | test 31 | 32 | 33 | org.powermock 34 | powermock-module-junit4 35 | 1.5.1 36 | test 37 | 38 | 39 | org.powermock 40 | powermock-api-easymock 41 | 1.5.1 42 | test 43 | 44 | 45 | 46 | 47 | 48 | 49 | com.github.github 50 | downloads-maven-plugin 51 | 0.5 52 | 53 | ${project.version} release of ${project.name} 54 | true 55 | true 56 | ${repositoryName} 57 | ${repositoryOwner} 58 | 59 | 60 | 61 | 62 | AES Encrypted SharedPreferences for Android applications. 63 | Requires API Level 8 - Froyo 64 | 65 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/SecureFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | import javax.crypto.NoSuchPaddingException; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import android.content.Context; 12 | import android.content.SharedPreferences; 13 | 14 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionAlgorithm; 15 | import com.github.kovmarci86.android.secure.preferences.util.SecureUtils; 16 | 17 | import edu.gmu.tec.scout.utilities.Encryption; 18 | 19 | /** 20 | * A factory class to ease the creation of the SecureSharedPreferences instance. 21 | * @author NoTiCe 22 | */ 23 | public final class SecureFactory { 24 | private static final String INITIALIZATION_ERROR = "Can not initialize SecureSharedPreferences"; 25 | public static final int VERSION_1 = 1; 26 | public static final int LATEST_VERSION = VERSION_1; 27 | private static final Logger LOGGER = LoggerFactory.getLogger(SecureFactory.class); 28 | 29 | /** 30 | * Hidden util constructor. 31 | */ 32 | private SecureFactory() { 33 | } 34 | 35 | /** 36 | * Creates the {@link SecureSharedPreferences} instance with a given original and an {@link EncryptionAlgorithm}. 37 | * This function does a version check and the required migrations when the local structure is outdated or not encrypted yet. 38 | * @param original The original {@link SharedPreferences}, which can be also a {@link SecureSharedPreferences} instance. 39 | * @param encryption The {@link EncryptionAlgorithm} to use. 40 | * @return A {@link SecureSharedPreferences} instance. 41 | */ 42 | public static SecureSharedPreferences getPreferences(SharedPreferences original, EncryptionAlgorithm encryption) { 43 | SecureSharedPreferences sharedPreferences; 44 | if (original instanceof SecureSharedPreferences) { 45 | sharedPreferences = (SecureSharedPreferences) original; 46 | } else { 47 | sharedPreferences = new SecureSharedPreferences(original, encryption); 48 | } 49 | if (SecureUtils.getVersion(sharedPreferences) < VERSION_1) { 50 | LOGGER.info("Initial migration to Secure storage."); 51 | SecureUtils.migrateData(original, sharedPreferences, VERSION_1); 52 | } 53 | return sharedPreferences; 54 | } 55 | 56 | /** 57 | * Creates the {@link SecureSharedPreferences} instance with a given original and an {@link EncryptionAlgorithm}. 58 | * This function does a version check and the required migrations when the local structure is outdated or not encrypted yet. 59 | * @param original The original {@link SharedPreferences}, which can be also a {@link SecureSharedPreferences} instance. 60 | * @param password The password to use. This will use the {@link Encryption} implementation of the {@link EncryptionAlgorithm}. 61 | * @return A {@link SecureSharedPreferences} instance. 62 | * @throws SecurityException When the {@link EncryptionAlgorithm} can not be initialized. 63 | */ 64 | public static SecureSharedPreferences getPreferences(SharedPreferences original, String password) throws SecurityException { 65 | try { 66 | EncryptionAlgorithm encryption = new Encryption(password); 67 | return getPreferences(original, encryption); 68 | } catch (UnsupportedEncodingException e) { 69 | throw new SecurityException(INITIALIZATION_ERROR, e); 70 | } catch (NoSuchAlgorithmException e) { 71 | throw new SecurityException(INITIALIZATION_ERROR, e); 72 | } catch (NoSuchPaddingException e) { 73 | throw new SecurityException(INITIALIZATION_ERROR, e); 74 | } 75 | } 76 | 77 | /** 78 | * Creates a {@link SecureSharedPreferences} instance. 79 | * @param context The current context. 80 | * @param preferencesName The name of the {@link SharedPreferences}. 81 | * @param password The password 82 | * @return The initialized {@link SecureSharedPreferences}. 83 | */ 84 | public static SecureSharedPreferences getPreferences(Context context, String preferencesName, String password) { 85 | try { 86 | return getPreferences(context, preferencesName, new Encryption(password)); 87 | } catch (UnsupportedEncodingException e) { 88 | throw new SecurityException(INITIALIZATION_ERROR, e); 89 | } catch (NoSuchAlgorithmException e) { 90 | throw new SecurityException(INITIALIZATION_ERROR, e); 91 | } catch (NoSuchPaddingException e) { 92 | throw new SecurityException(INITIALIZATION_ERROR, e); 93 | } 94 | } 95 | 96 | /** 97 | * Creates a {@link SecureSharedPreferences} instance. 98 | * @param context The current context. 99 | * @param preferencesName The name of the {@link SharedPreferences}. 100 | * @param encryption The {@link EncryptionAlgorithm} to use. 101 | * @return The initialized {@link SecureSharedPreferences}. 102 | */ 103 | public static SecureSharedPreferences getPreferences(Context context, String preferencesName, EncryptionAlgorithm encryption) { 104 | return getPreferences(context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE), encryption); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/SecureSharedPreferences.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import android.annotation.TargetApi; 7 | import android.content.SharedPreferences; 8 | import android.os.Build; 9 | 10 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionAlgorithm; 11 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionHelper; 12 | 13 | /** 14 | * Decorates SharedPreferences with AES Encryption. 15 | * @author NoTiCe 16 | */ 17 | public class SecureSharedPreferences implements SharedPreferences { 18 | private SharedPreferences prefs; 19 | private EncryptionAlgorithm encryption; 20 | private EncryptionHelper helper; 21 | 22 | /** 23 | * Initializes with a single {@link SharedPreferences} 24 | * and the {@link edu.gmu.tec.scout.utilities.Encryption} to use. 25 | * 26 | * @param preferences 27 | * The {@link SharedPreferences} to use. 28 | * @param encryption 29 | * The {@link edu.gmu.tec.scout.utilities.Encryption} to use. 30 | */ 31 | public SecureSharedPreferences(SharedPreferences preferences, EncryptionAlgorithm encryption) { 32 | this.prefs = preferences; 33 | this.encryption = encryption; 34 | helper = new EncryptionHelper(encryption); 35 | } 36 | 37 | @Override 38 | public boolean contains(String key) { 39 | return prefs.contains(key); 40 | } 41 | 42 | @Override 43 | public SecuredEditor edit() { 44 | return new SecuredEditor(helper, prefs.edit()); 45 | } 46 | 47 | @Override 48 | public Map getAll() { 49 | return prefs.getAll(); 50 | } 51 | 52 | @Override 53 | public boolean getBoolean(String key, boolean defValue) { 54 | return helper.readAndDecodeTemplate(prefs, key, defValue); 55 | } 56 | 57 | @Override 58 | public float getFloat(String key, float defValue) { 59 | return helper.readAndDecodeTemplate(prefs, key, defValue); 60 | } 61 | 62 | @Override 63 | public int getInt(String key, int defValue) { 64 | return helper.readAndDecodeTemplate(prefs, key, defValue); 65 | } 66 | 67 | @Override 68 | public long getLong(String key, long defValue) { 69 | return helper.readAndDecodeTemplate(prefs, key, defValue); 70 | } 71 | 72 | @Override 73 | public String getString(String key, String defValue) { 74 | return helper.readAndDecodeTemplate(prefs, key, defValue); 75 | } 76 | 77 | @TargetApi(value = Build.VERSION_CODES.HONEYCOMB) 78 | @Override 79 | public Set getStringSet(String key, Set defValues) { 80 | return helper.readAndDecodeTemplate(prefs, key, defValues); 81 | } 82 | 83 | @Override 84 | public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { 85 | prefs.registerOnSharedPreferenceChangeListener(listener); 86 | } 87 | 88 | @Override 89 | public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { 90 | prefs.unregisterOnSharedPreferenceChangeListener(listener); 91 | } 92 | 93 | protected EncryptionAlgorithm getEncryption() { 94 | return encryption; 95 | } 96 | 97 | protected SharedPreferences getPrefs() { 98 | return prefs; 99 | } 100 | 101 | protected void setHelper(EncryptionHelper helper) { 102 | this.helper = helper; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/SecuredEditor.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import java.util.Set; 4 | 5 | import android.content.SharedPreferences; 6 | import android.content.SharedPreferences.Editor; 7 | import android.os.Build; 8 | 9 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionHelper; 10 | 11 | /** 12 | * An {@link Editor} decorator using AES encription. 13 | * 14 | * @author NoTiCe 15 | */ 16 | public class SecuredEditor implements Editor { 17 | private Editor editor; 18 | private EncryptionHelper helper; 19 | 20 | /** 21 | * Initializes with the {@link EncryptionHelper} an the original 22 | * {@link Editor}. 23 | * @param helper 24 | * The helper to use. 25 | * @param edit 26 | * The editor to use. 27 | */ 28 | public SecuredEditor(EncryptionHelper helper, Editor edit) { 29 | this.helper = helper; 30 | this.editor = edit; 31 | } 32 | 33 | @Override 34 | public SecuredEditor putString(String key, String value) { 35 | editor.putString(key, helper.encode(value)); 36 | return this; 37 | } 38 | 39 | @Override 40 | public SecuredEditor putStringSet(String key, Set values) { 41 | editor.putString(key, helper.encode(values)); 42 | return this; 43 | } 44 | 45 | @Override 46 | public SecuredEditor putInt(String key, int value) { 47 | editor.putString(key, helper.encode(value)); 48 | return this; 49 | } 50 | 51 | @Override 52 | public SecuredEditor putLong(String key, long value) { 53 | editor.putString(key, helper.encode(value)); 54 | return this; 55 | } 56 | 57 | @Override 58 | public SecuredEditor putFloat(String key, float value) { 59 | editor.putString(key, helper.encode(value)); 60 | return this; 61 | } 62 | 63 | @Override 64 | public SecuredEditor putBoolean(String key, boolean value) { 65 | editor.putString(key, helper.encode(value)); 66 | return this; 67 | } 68 | 69 | @Override 70 | public SecuredEditor remove(String key) { 71 | editor.remove(key); 72 | return this; 73 | } 74 | 75 | @Override 76 | public SecuredEditor clear() { 77 | editor.clear(); 78 | return this; 79 | } 80 | 81 | @Override 82 | public boolean commit() { 83 | return editor.commit(); 84 | } 85 | 86 | @Override 87 | public void apply() { 88 | editor.apply(); 89 | } 90 | 91 | /** 92 | * Compatibility version of original {@link android.content.SharedPreferences.Editor#apply()} 93 | * method that simply call {@link android.content.SharedPreferences.Editor#commit()} for pre Android Honeycomb (API 11). 94 | * This method is thread safe also on pre API 11. 95 | * Note that when two editors are modifying preferences at the same time, the last one to call apply wins. (Android Doc) 96 | */ 97 | public void save() { 98 | compatilitySave(this); 99 | } 100 | 101 | /** 102 | * Saves the {@link SharedPreferences}. See save method. 103 | * @param editor The editor to save/commit. 104 | */ 105 | public static void compatilitySave(Editor editor) { 106 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 107 | editor.apply(); 108 | } else { 109 | synchronized (SecuredEditor.class) { 110 | editor.commit(); 111 | } 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/encryption/EncryptionAlgorithm.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.encryption; 2 | 3 | /** 4 | * Encryption algorithm. 5 | * @author NoTiCe 6 | */ 7 | public interface EncryptionAlgorithm { 8 | /** 9 | * Encrypts the given bytes. 10 | * @param bytes 11 | * The bytes to encrypt. 12 | * @return The encrypted bytes. 13 | * @throws EncryptionException When the encryiption fails. 14 | */ 15 | byte[] encrypt(byte[] bytes) throws EncryptionException; 16 | 17 | /** 18 | * Decrypts the given bytes. 19 | * @param bytes 20 | * The bytes to decrypt. 21 | * @return The decrypted bytes. 22 | * @throws EncryptionException When the encryiption fails. 23 | */ 24 | byte[] decrypt(byte[] bytes) throws EncryptionException; 25 | } 26 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/encryption/EncryptionException.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.encryption; 2 | 3 | /** 4 | * A custom exception for ecoding. 5 | * @author NoTiCe 6 | * 7 | */ 8 | public class EncryptionException extends Exception { 9 | 10 | /** 11 | * Inherited from {@link Exception}. 12 | */ 13 | public EncryptionException() { 14 | super(); 15 | } 16 | 17 | /** 18 | * Inherited from {@link Exception}. 19 | * @param message The message. 20 | * @param cause The root cause. 21 | */ 22 | public EncryptionException(String message, Throwable cause) { 23 | super(message, cause); 24 | } 25 | 26 | /** 27 | * Inherited from {@link Exception}. 28 | * @param message The message. 29 | */ 30 | public EncryptionException(String message) { 31 | super(message); 32 | } 33 | 34 | /** 35 | * Inherited from {@link Exception}. 36 | * @param cause The root cause. 37 | */ 38 | public EncryptionException(Throwable cause) { 39 | super(cause); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/encryption/EncryptionHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.encryption; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import android.content.SharedPreferences; 13 | import android.util.Base64; 14 | 15 | /** 16 | * Encrypting / decrypting support algorithms and type conversions. 17 | * @author NoTiCe 18 | * 19 | */ 20 | public class EncryptionHelper { 21 | private static final Logger LOGGER = LoggerFactory.getLogger(EncryptionHelper.class); 22 | private EncryptionAlgorithm encryption; 23 | 24 | /** 25 | * Initializes with ecryption. 26 | * @param encryption The {@link EncryptionAlgorithm} to use. 27 | */ 28 | public EncryptionHelper(EncryptionAlgorithm encryption) { 29 | super(); 30 | this.encryption = encryption; 31 | } 32 | 33 | /** 34 | * Reads a value from a {@link SharedPreferences}. 35 | * @param The type of the result and the default value. 36 | * @param prefs The preferences to use. 37 | * @param key The key to read. 38 | * @param defValue The default value, when the key does not exist. 39 | * @return Return the T type of result. 40 | */ 41 | @SuppressWarnings("unchecked") 42 | public T readAndDecodeTemplate(SharedPreferences prefs, String key, T defValue) { 43 | T result = defValue; 44 | ObjectInputStream ois = readDecoded(prefs, key); 45 | if (ois != null) { 46 | try { 47 | result = (T) ois.readObject(); 48 | } catch (IOException e) { 49 | LOGGER.error("Error reading value by key: {}", key, e); 50 | } catch (ClassNotFoundException e) { 51 | LOGGER.error("Error reading value by key: {}", key, e); 52 | } 53 | } 54 | return result; 55 | } 56 | 57 | /** 58 | * Encodes a single value to string. 59 | * May result null on an internal problem. 60 | * @param The type of the value. 61 | * @param value The T type of value to encrypt. 62 | * @return The encrypted value as string. 63 | */ 64 | public String encode(T value) { 65 | String result = null; 66 | if (value != null) { 67 | try { 68 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 69 | ObjectOutputStream oos = new ObjectOutputStream(baos); 70 | oos.writeObject(value); 71 | byte[] byteArray = baos.toByteArray(); 72 | byte[] encrypt = encryption.encrypt(byteArray); 73 | result = Base64.encodeToString(encrypt, Base64.DEFAULT); 74 | } catch (IOException e) { 75 | LOGGER.error("Error encoding value", e); 76 | } catch (EncryptionException e) { 77 | LOGGER.error("Error encoding value", e); 78 | } 79 | } 80 | return result; 81 | } 82 | 83 | private ObjectInputStream readDecoded(SharedPreferences prefs, String key) { 84 | String stringValue = prefs.getString(key, null); 85 | ObjectInputStream result; 86 | if (stringValue != null) { 87 | try { 88 | result = createDecodedObjectStream(stringValue); 89 | } catch (EncryptionException e) { 90 | LOGGER.error("Error reading from properties. Key: {}", key, e); 91 | result = null; 92 | } 93 | } else { 94 | result = null; 95 | } 96 | return result; 97 | } 98 | 99 | private ObjectInputStream createDecodedObjectStream(String stringValue) throws EncryptionException { 100 | byte[] decodedBytes = Base64.decode(stringValue, Base64.DEFAULT); 101 | byte[] decoded = encryption.decrypt(decodedBytes); 102 | try { 103 | return new ObjectInputStream(new ByteArrayInputStream(decoded)); 104 | } catch (IOException e) { 105 | throw new EncryptionException(e); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/com/github/kovmarci86/android/secure/preferences/util/SecureUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.util; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import android.content.SharedPreferences; 7 | import android.content.SharedPreferences.Editor; 8 | 9 | import com.github.kovmarci86.android.secure.preferences.SecureFactory; 10 | import com.github.kovmarci86.android.secure.preferences.SecuredEditor; 11 | 12 | /** 13 | * Util classes for {@link SecureFactory}. 14 | * @author NoTiCe 15 | */ 16 | public final class SecureUtils { 17 | private static final String VERSION_KEY = "SecurePreferences_version"; 18 | 19 | /** 20 | * Hidden util constructor. 21 | */ 22 | private SecureUtils() { 23 | } 24 | 25 | /** 26 | * Copies data from one {@link SharedPreferences} to another. 27 | * @param from The source. 28 | * @param to The target. 29 | * @param version The version code to write into the preferences for future check. 30 | */ 31 | @SuppressWarnings("unchecked") 32 | public static void migrateData(SharedPreferences from, SharedPreferences to, int version) { 33 | Map all = from.getAll(); 34 | Set keySet = all.keySet(); 35 | Editor edit = to.edit(); 36 | for (String key : keySet) { 37 | Object object = all.get(key); 38 | if (object == null) { 39 | // should not reach here 40 | edit.remove(key); 41 | } else if (object instanceof String) { 42 | edit.putString(key, (String) object); 43 | } else if (object instanceof Integer) { 44 | edit.putInt(key, (Integer) object); 45 | } else if (object instanceof Long) { 46 | edit.putLong(key, (Long) object); 47 | } else if (object instanceof Float) { 48 | edit.putFloat(key, (Float) object); 49 | } else if (object instanceof Boolean) { 50 | edit.putBoolean(key, (Boolean) object); 51 | } else if (object instanceof Set) { 52 | edit.putStringSet(key, (Set) object); 53 | } 54 | } 55 | edit.putInt(VERSION_KEY, version); 56 | SecuredEditor.compatilitySave(edit); 57 | } 58 | 59 | /** 60 | * Gets the version of {@link SharedPreferences} if any. 61 | * @param preferences 62 | * @return The version or -1. 63 | */ 64 | public static int getVersion(SharedPreferences preferences) { 65 | int currentVersion = preferences.getInt(VERSION_KEY, -1); 66 | return currentVersion; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /secure-preferences/src/main/java/edu/gmu/tec/scout/utilities/Encryption.java: -------------------------------------------------------------------------------- 1 | package edu.gmu.tec.scout.utilities; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.InvalidKeyException; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | import javax.crypto.BadPaddingException; 9 | import javax.crypto.Cipher; 10 | import javax.crypto.IllegalBlockSizeException; 11 | import javax.crypto.NoSuchPaddingException; 12 | import javax.crypto.spec.SecretKeySpec; 13 | 14 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionAlgorithm; 15 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionException; 16 | 17 | /** 18 | * This class is from the following site: 19 | * http://www.java2s.com/Code/Android/Security/AESEncryption.htm with some 20 | * modification. 21 | * @author http://www.java2s.com/Code/Android/Security/AESEncryption.htm 22 | */ 23 | public class Encryption implements EncryptionAlgorithm { 24 | /** 25 | * Author of the base of this implementation. 26 | */ 27 | public static final String UTILS_AUTHOR = "http://www.java2s.com/Code/Android/Security/AESEncryption.htm"; 28 | private SecretKeySpec skeySpec; 29 | private Cipher cipher; 30 | 31 | /** 32 | * Initializes with raw key. 33 | * @param keyraw The raw key. Must NOT be null. 34 | * @throws UnsupportedEncodingException When no UTF-8 support found. 35 | * @throws NoSuchAlgorithmException When no MD5 support found. 36 | * @throws NoSuchPaddingException When the AES/ECB/PKCS5Padding is not supported. 37 | */ 38 | public Encryption(byte[] keyraw) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException { 39 | if (keyraw == null) { 40 | throw new IllegalArgumentException("null key given"); 41 | } else { 42 | skeySpec = new SecretKeySpec(keyraw, "AES"); 43 | cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 44 | } 45 | } 46 | 47 | /** 48 | * Initializes with a password to use for encrypt. 49 | * @param passphrase The string password. 50 | * @throws UnsupportedEncodingException When no UTF-8 support found. 51 | * @throws NoSuchAlgorithmException When no MD5 support found. 52 | * @throws NoSuchPaddingException When the AES/ECB/PKCS5Padding is not supported. 53 | */ 54 | public Encryption(String passphrase) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException { 55 | byte[] bytesOfMessage = passphrase.getBytes("UTF-8"); 56 | MessageDigest md = MessageDigest.getInstance("MD5"); 57 | byte[] thedigest = md.digest(bytesOfMessage); 58 | skeySpec = new SecretKeySpec(thedigest, "AES"); 59 | cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 60 | } 61 | 62 | /** 63 | * Encrypts the bytes. 64 | * @param plaintext The bytes to encrypt. 65 | * @return The encrypted value. 66 | * @throws EncryptionException On encryption exception. 67 | */ 68 | @Override 69 | public byte[] encrypt(byte[] plaintext) throws EncryptionException { 70 | // returns byte array encrypted with key 71 | try { 72 | cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 73 | return cipher.doFinal(plaintext); 74 | } catch (InvalidKeyException e) { 75 | throw new EncryptionException(e); 76 | } catch (IllegalBlockSizeException e) { 77 | throw new EncryptionException(e); 78 | } catch (BadPaddingException e) { 79 | throw new EncryptionException(e); 80 | } 81 | } 82 | 83 | /** 84 | * Decrypes the cypher. 85 | * @param ciphertext The data to decrypt. 86 | * @return The decrypted value. 87 | * @throws EncryptionException On ecryption exception. 88 | */ 89 | @Override 90 | public byte[] decrypt(byte[] ciphertext) throws EncryptionException { 91 | // returns byte array decrypted with key 92 | try { 93 | cipher.init(Cipher.DECRYPT_MODE, skeySpec); 94 | return cipher.doFinal(ciphertext); 95 | } catch (InvalidKeyException e) { 96 | throw new EncryptionException(e); 97 | } catch (IllegalBlockSizeException e) { 98 | throw new EncryptionException(e); 99 | } catch (BadPaddingException e) { 100 | throw new EncryptionException(e); 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/SecureFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import static org.easymock.EasyMock.anyObject; 4 | import static org.easymock.EasyMock.eq; 5 | import static org.easymock.EasyMock.expect; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertNotSame; 8 | import static org.junit.Assert.assertTrue; 9 | 10 | import org.easymock.EasyMockSupport; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.powermock.api.easymock.PowerMock; 15 | import org.powermock.core.classloader.annotations.PrepareForTest; 16 | import org.powermock.modules.junit4.PowerMockRunner; 17 | 18 | import android.content.Context; 19 | import android.content.SharedPreferences; 20 | 21 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionAlgorithm; 22 | import com.github.kovmarci86.android.secure.preferences.util.SecureUtils; 23 | 24 | import edu.gmu.tec.scout.utilities.Encryption; 25 | 26 | @RunWith(PowerMockRunner.class) 27 | @PrepareForTest({SecureUtils.class, SecureFactory.class}) 28 | public class SecureFactoryTest extends EasyMockSupport { 29 | 30 | @Before 31 | public void setUp() throws Exception { 32 | } 33 | 34 | @Test 35 | public void testGetPreferencesWithSecurePreferencesCurrentVersion() { 36 | // GIVEN 37 | int currentVersion = SecureFactory.LATEST_VERSION; 38 | SharedPreferences original = createMock(SecureSharedPreferences.class); 39 | EncryptionAlgorithm encryption = createMock(EncryptionAlgorithm.class); 40 | PowerMock.mockStatic(SecureUtils.class); 41 | // EXPECT 42 | expect(SecureUtils.getVersion(original)).andReturn(currentVersion); 43 | PowerMock.replay(SecureUtils.class); 44 | replayAll(); 45 | // WHEN 46 | SecureSharedPreferences preferences = SecureFactory.getPreferences(original, encryption); 47 | // THEN 48 | assertEquals("Must not modify already migrated", original, preferences); 49 | PowerMock.verifyAll(); 50 | verifyAll(); 51 | } 52 | 53 | @Test 54 | public void testGetPreferencesWithSecurePreferencesZeroVersion() { 55 | // GIVEN 56 | int currentVersion = 0; 57 | SharedPreferences original = createMock(SecureSharedPreferences.class); 58 | EncryptionAlgorithm encryption = createMock(EncryptionAlgorithm.class); 59 | PowerMock.mockStatic(SecureUtils.class); 60 | // EXPECT 61 | expect(SecureUtils.getVersion(original)).andReturn(currentVersion); 62 | SecureUtils.migrateData(original, original, SecureFactory.VERSION_1); 63 | PowerMock.replay(SecureUtils.class); 64 | replayAll(); 65 | // WHEN 66 | SecureSharedPreferences preferences = SecureFactory.getPreferences(original, encryption); 67 | // THEN 68 | assertEquals("Must not modify already migrated", original, preferences); 69 | PowerMock.verifyAll(); 70 | verifyAll(); 71 | } 72 | 73 | @Test 74 | public void testGetPreferencesWithSharedPreferencesZeroVersion() throws Exception { 75 | // GIVEN 76 | int currentVersion = 0; 77 | SharedPreferences original = createMock(SharedPreferences.class); 78 | EncryptionAlgorithm encryption = createMock(EncryptionAlgorithm.class); 79 | PowerMock.mockStatic(SecureUtils.class); 80 | // EXPECT 81 | expect(SecureUtils.getVersion(anyObject(SecureSharedPreferences.class))).andReturn(currentVersion); 82 | SecureUtils.migrateData(eq(original), anyObject(SecureSharedPreferences.class), eq(SecureFactory.VERSION_1)); 83 | PowerMock.replay(SecureUtils.class); 84 | replayAll(); 85 | // WHEN 86 | SharedPreferences preferences = SecureFactory.getPreferences(original, encryption); 87 | // THEN 88 | assertNotSame("Must migrate at first use", original, preferences); 89 | assertTrue("Must be instanceof SecureSharedPreferences", preferences instanceof SecureSharedPreferences); 90 | PowerMock.verify(SecureUtils.class); 91 | verifyAll(); 92 | } 93 | 94 | @Test 95 | public void testGetPreferencesWithSecurePreferencesAndPasswordCurrentVersion() throws Exception { 96 | // GIVEN 97 | String password = "fooPass"; 98 | int currentVersion = SecureFactory.LATEST_VERSION; 99 | SharedPreferences original = createMock(SecureSharedPreferences.class); 100 | Encryption encryption = createMock(Encryption.class); 101 | PowerMock.mockStatic(SecureUtils.class); 102 | // EXPECT 103 | PowerMock.expectNew(Encryption.class, password).andReturn(encryption); 104 | expect(SecureUtils.getVersion(original)).andReturn(currentVersion); 105 | PowerMock.replay(SecureUtils.class, Encryption.class); 106 | replayAll(); 107 | // WHEN 108 | SecureSharedPreferences preferences = SecureFactory.getPreferences(original, password); 109 | // THEN 110 | assertEquals("Must not modify already migrated", original, preferences); 111 | PowerMock.verifyAll(); 112 | verifyAll(); 113 | } 114 | 115 | @Test 116 | public void testGetPreferencesWithSecurePreferencesAndPrefNameAndPasswordCurrentVersion() throws Exception { 117 | // GIVEN 118 | Context context = createMock(Context.class); 119 | String prefName = "fooPrefName"; 120 | String password = "fooPass"; 121 | int currentVersion = SecureFactory.LATEST_VERSION; 122 | SharedPreferences original = createMock(SecureSharedPreferences.class); 123 | Encryption encryption = createMock(Encryption.class); 124 | PowerMock.mockStatic(SecureUtils.class); 125 | // EXPECT 126 | PowerMock.expectNew(Encryption.class, password).andReturn(encryption); 127 | expect(context.getSharedPreferences(prefName, Context.MODE_PRIVATE)).andReturn(original); 128 | expect(SecureUtils.getVersion(original)).andReturn(currentVersion); 129 | PowerMock.replay(SecureUtils.class, Encryption.class); 130 | replayAll(); 131 | // WHEN 132 | SecureSharedPreferences preferences = SecureFactory.getPreferences(context, prefName, password); 133 | // THEN 134 | assertEquals("Must not modify already migrated", original, preferences); 135 | PowerMock.verifyAll(); 136 | verifyAll(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/SecureSharedPreferencesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import static org.easymock.EasyMock.expect; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNotNull; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | import org.easymock.EasyMockSupport; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | 17 | import android.content.SharedPreferences; 18 | import android.content.SharedPreferences.Editor; 19 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 20 | 21 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionHelper; 22 | 23 | import edu.gmu.tec.scout.utilities.Encryption; 24 | 25 | /** 26 | * JUnit test for {@link SecureSharedPreferences}. 27 | * @author NoTiCe 28 | */ 29 | public class SecureSharedPreferencesTest extends EasyMockSupport { 30 | private static final float DELTA = 0.1f; 31 | private SharedPreferences preferences = createMock(SecureSharedPreferences.class); 32 | private Encryption encryption = createMock(Encryption.class); 33 | private SecureSharedPreferences sut = new SecureSharedPreferences(preferences, encryption); 34 | private String key = "fooKey"; 35 | 36 | @Before 37 | public void setUp() throws Exception { 38 | resetAll(); 39 | } 40 | 41 | @Test 42 | public void testSecureSharedPreferences() { 43 | // GIVEN 44 | // EXPECT 45 | replayAll(); 46 | // WHEN 47 | // THEN 48 | verifyAll(); 49 | assertEquals("Preferences must be the same", preferences, sut.getPrefs()); 50 | assertEquals("Encryption must be the same", encryption, sut.getEncryption()); 51 | } 52 | 53 | @Test 54 | public void testContains() { 55 | // GIVEN 56 | boolean fooContains = true; 57 | // EXPECT 58 | expect(preferences.contains(key)).andReturn(fooContains); 59 | replayAll(); 60 | // WHEN 61 | boolean contains = sut.contains(key); 62 | verifyAll(); 63 | assertEquals("Must be true", fooContains, contains); 64 | } 65 | 66 | @Test 67 | public void testEdit() { 68 | // GIVEN 69 | Editor fooEditor = createMock(SecuredEditor.class); 70 | // EXPECT 71 | expect(preferences.edit()).andReturn(fooEditor); 72 | replayAll(); 73 | // WHEN 74 | Editor edit = sut.edit(); 75 | // THEN 76 | verifyAll(); 77 | assertNotNull("Editor must not be null", edit); 78 | assertEquals("Editor must be SecuredEditor.", SecuredEditor.class, edit.getClass()); 79 | } 80 | 81 | @SuppressWarnings({"rawtypes", "unchecked"}) 82 | @Test 83 | public void testGetAll() { 84 | // GIVEN 85 | Map fooAll = createMock(Map.class); 86 | // EXPECT 87 | expect(preferences.getAll()).andReturn(fooAll); 88 | replayAll(); 89 | // WHEN 90 | Map all = sut.getAll(); 91 | // THEN 92 | verifyAll(); 93 | assertEquals("Wrong map returned", fooAll, all); 94 | } 95 | 96 | @Test 97 | public void testGetBoolean() { 98 | // GIVEN 99 | EncryptionHelper helper = createMock(EncryptionHelper.class); 100 | boolean defValue = false; 101 | boolean fooResult = true; 102 | String fooKey = "fooKey"; 103 | // EXPECT 104 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 105 | replayAll(); 106 | // WHEN 107 | sut.setHelper(helper); 108 | boolean result = sut.getBoolean(fooKey, defValue); 109 | // THEN 110 | verifyAll(); 111 | assertEquals("Wrong result", fooResult, result); 112 | } 113 | 114 | @Test 115 | public void testGetFloat() { 116 | // GIVEN 117 | EncryptionHelper helper = createMock(EncryptionHelper.class); 118 | float defValue = 1.0f; 119 | float fooResult = 2.0f; 120 | String fooKey = "fooKey"; 121 | // EXPECT 122 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 123 | replayAll(); 124 | // WHEN 125 | sut.setHelper(helper); 126 | float result = sut.getFloat(fooKey, defValue); 127 | // THEN 128 | verifyAll(); 129 | assertTrue("Wrong result", Math.abs(fooResult - result) < DELTA); 130 | } 131 | 132 | @Test 133 | public void testGetInt() { 134 | // GIVEN 135 | EncryptionHelper helper = createMock(EncryptionHelper.class); 136 | int defValue = 1; 137 | int fooResult = 2; 138 | String fooKey = "fooKey"; 139 | // EXPECT 140 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 141 | replayAll(); 142 | // WHEN 143 | sut.setHelper(helper); 144 | int result = sut.getInt(fooKey, defValue); 145 | // THEN 146 | verifyAll(); 147 | assertEquals("Wrong result", fooResult, result); 148 | } 149 | 150 | @Test 151 | public void testGetLong() { 152 | // GIVEN 153 | EncryptionHelper helper = createMock(EncryptionHelper.class); 154 | long defValue = 1l; 155 | long fooResult = 2l; 156 | String fooKey = "fooKey"; 157 | // EXPECT 158 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 159 | replayAll(); 160 | // WHEN 161 | sut.setHelper(helper); 162 | long result = sut.getLong(fooKey, defValue); 163 | // THEN 164 | verifyAll(); 165 | assertEquals("Wrong result", fooResult, result); 166 | } 167 | 168 | @Test 169 | public void testGetString() { 170 | // GIVEN 171 | EncryptionHelper helper = createMock(EncryptionHelper.class); 172 | String defValue = "1f"; 173 | String fooResult = "2f"; 174 | String fooKey = "fooKey"; 175 | // EXPECT 176 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 177 | replayAll(); 178 | // WHEN 179 | sut.setHelper(helper); 180 | String result = sut.getString(fooKey, defValue); 181 | // THEN 182 | verifyAll(); 183 | assertEquals("Wrong result", fooResult, result); 184 | } 185 | 186 | @Test 187 | public void testGetStringSet() { 188 | // GIVEN 189 | EncryptionHelper helper = createMock(EncryptionHelper.class); 190 | Set defValue = new HashSet(); 191 | Set fooResult = new HashSet(Arrays.asList(new String[]{"1f"})); 192 | String fooKey = "fooKey"; 193 | // EXPECT 194 | expect(helper.readAndDecodeTemplate(preferences, key, defValue)).andReturn(fooResult); 195 | replayAll(); 196 | // WHEN 197 | sut.setHelper(helper); 198 | Set result = sut.getStringSet(fooKey, defValue); 199 | // THEN 200 | verifyAll(); 201 | assertEquals("Wrong result", fooResult, result); 202 | } 203 | 204 | @Test 205 | public void testRegisterOnSharedPreferenceChangeListener() { 206 | // GIVEN 207 | OnSharedPreferenceChangeListener listener = createMock(OnSharedPreferenceChangeListener.class); 208 | // EXPECT 209 | preferences.registerOnSharedPreferenceChangeListener(listener); 210 | replayAll(); 211 | // WHEN 212 | sut.registerOnSharedPreferenceChangeListener(listener); 213 | // THEN 214 | verifyAll(); 215 | } 216 | 217 | @Test 218 | public void testUnregisterOnSharedPreferenceChangeListener() { 219 | // GIVEN 220 | OnSharedPreferenceChangeListener listener = createMock(OnSharedPreferenceChangeListener.class); 221 | // EXPECT 222 | preferences.unregisterOnSharedPreferenceChangeListener(listener); 223 | replayAll(); 224 | // WHEN 225 | sut.unregisterOnSharedPreferenceChangeListener(listener); 226 | // THEN 227 | verifyAll(); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/SecuredEditorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import static org.easymock.EasyMock.expect; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | import org.easymock.EasyMockSupport; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import android.content.SharedPreferences.Editor; 14 | import android.os.Build; 15 | 16 | import com.github.kovmarci86.android.secure.preferences.encryption.EncryptionHelper; 17 | 18 | /** 19 | * JUnit test for {@link SecuredEditor}. 20 | * @author NoTiCe 21 | */ 22 | public class SecuredEditorTest extends EasyMockSupport { 23 | private static final String FOO_KEY = "fooKey"; 24 | private static final String FOO_ENCODED_STRING = "encodedString"; 25 | private Editor editor; 26 | private EncryptionHelper helper; 27 | 28 | @Before 29 | public void setUp() throws Exception { 30 | editor = createMock(Editor.class); 31 | helper = createMock(EncryptionHelper.class); 32 | } 33 | 34 | @Test 35 | public void testPutString() { 36 | // GIVEN 37 | String fooValue = "fooString"; 38 | // EXPECT 39 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 40 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 41 | replayAll(); 42 | // WHEN 43 | SecuredEditor sut = new SecuredEditor(helper, editor); 44 | Editor result = sut.putString(FOO_KEY, fooValue); 45 | // THEN 46 | verifyAll(); 47 | assertEquals("Must return self", sut, result); 48 | } 49 | 50 | @Test 51 | public void testPutStringSet() { 52 | // GIVEN 53 | Set fooValue = new HashSet(); 54 | fooValue.add("fooString"); 55 | // EXPECT 56 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 57 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 58 | replayAll(); 59 | // WHEN 60 | SecuredEditor sut = new SecuredEditor(helper, editor); 61 | Editor result = sut.putStringSet(FOO_KEY, fooValue); 62 | // THEN 63 | verifyAll(); 64 | assertEquals("Must return self", sut, result); 65 | } 66 | 67 | @Test 68 | public void testPutInt() { 69 | // GIVEN 70 | int fooValue = 2394578; 71 | // EXPECT 72 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 73 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 74 | replayAll(); 75 | // WHEN 76 | SecuredEditor sut = new SecuredEditor(helper, editor); 77 | Editor result = sut.putInt(FOO_KEY, fooValue); 78 | // THEN 79 | verifyAll(); 80 | assertEquals("Must return self", sut, result); 81 | } 82 | 83 | @Test 84 | public void testPutLong() { 85 | // GIVEN 86 | long fooValue = 2394578L; 87 | // EXPECT 88 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 89 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 90 | replayAll(); 91 | // WHEN 92 | SecuredEditor sut = new SecuredEditor(helper, editor); 93 | Editor result = sut.putLong(FOO_KEY, fooValue); 94 | // THEN 95 | verifyAll(); 96 | assertEquals("Must return self", sut, result); 97 | } 98 | 99 | @Test 100 | public void testPutFloat() { 101 | // GIVEN 102 | float fooValue = 2394578f; 103 | // EXPECT 104 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 105 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 106 | replayAll(); 107 | // WHEN 108 | SecuredEditor sut = new SecuredEditor(helper, editor); 109 | Editor result = sut.putFloat(FOO_KEY, fooValue); 110 | // THEN 111 | verifyAll(); 112 | assertEquals("Must return self", sut, result); 113 | } 114 | 115 | @Test 116 | public void testPutBoolean() { 117 | // GIVEN 118 | boolean fooValue = true; 119 | // EXPECT 120 | expect(helper.encode(fooValue)).andReturn(FOO_ENCODED_STRING); 121 | expect(editor.putString(FOO_KEY, FOO_ENCODED_STRING)).andReturn(editor); 122 | replayAll(); 123 | // WHEN 124 | SecuredEditor sut = new SecuredEditor(helper, editor); 125 | Editor result = sut.putBoolean(FOO_KEY, fooValue); 126 | // THEN 127 | verifyAll(); 128 | assertEquals("Must return self", sut, result); 129 | } 130 | 131 | @Test 132 | public void testRemove() { 133 | // GIVEN 134 | // EXPECT 135 | expect(editor.remove(FOO_KEY)).andReturn(editor); 136 | replayAll(); 137 | // WHEN 138 | SecuredEditor sut = new SecuredEditor(helper, editor); 139 | Editor result = sut.remove(FOO_KEY); 140 | // THEN 141 | verifyAll(); 142 | assertEquals("Must return self", sut, result); 143 | } 144 | 145 | @Test 146 | public void testClear() { 147 | // GIVEN 148 | // EXPECT 149 | expect(editor.clear()).andReturn(editor); 150 | replayAll(); 151 | // WHEN 152 | SecuredEditor sut = new SecuredEditor(helper, editor); 153 | Editor result = sut.clear(); 154 | // THEN 155 | verifyAll(); 156 | assertEquals("Must return self", sut, result); 157 | } 158 | 159 | @Test 160 | public void testCommit() { 161 | // GIVEN 162 | Boolean fooResult = true; 163 | // EXPECT 164 | expect(editor.commit()).andReturn(fooResult); 165 | replayAll(); 166 | // WHEN 167 | SecuredEditor sut = new SecuredEditor(helper, editor); 168 | boolean result = sut.commit(); 169 | // THEN 170 | verifyAll(); 171 | assertEquals("Must return self", fooResult, result); 172 | } 173 | 174 | @Test 175 | public void testApply() { 176 | // GIVEN 177 | // EXPECT 178 | editor.apply(); 179 | replayAll(); 180 | // WHEN 181 | SecuredEditor sut = new SecuredEditor(helper, editor); 182 | sut.apply(); 183 | // THEN 184 | verifyAll(); 185 | } 186 | 187 | @Test 188 | public void testSave() { 189 | // GIVEN 190 | // EXPECT - expectation may vary with build environment 191 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 192 | editor.apply(); 193 | } else { 194 | expect(editor.commit()).andReturn(true); 195 | } 196 | replayAll(); 197 | // WHEN 198 | SecuredEditor sut = new SecuredEditor(helper, editor); 199 | sut.save(); 200 | // THEN 201 | verifyAll(); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/encryption/EncryptionExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.encryption; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * JUnit test for {@link EncryptionException}. 9 | * @author NoTiCe 10 | * 11 | */ 12 | public class EncryptionExceptionTest { 13 | 14 | @Test 15 | public void testEncryptionException() { 16 | // +1 green line in the report 17 | new EncryptionException(); 18 | } 19 | 20 | @Test 21 | public void testEncryptionExceptionStringThrowable() { 22 | String message = "foo"; 23 | Throwable throwable = new Throwable(); 24 | EncryptionException encryptionException = new EncryptionException(message, throwable); 25 | assertEquals("Must be the same", message, encryptionException.getMessage()); 26 | assertEquals("Must be the same", throwable, encryptionException.getCause()); 27 | } 28 | 29 | @Test 30 | public void testEncryptionExceptionString() { 31 | String message = "foo"; 32 | EncryptionException encryptionException = new EncryptionException(message); 33 | assertEquals("Must be the same", message, encryptionException.getMessage()); 34 | 35 | } 36 | 37 | @Test 38 | public void testEncryptionExceptionThrowable() { 39 | Throwable throwable = new Throwable(); 40 | EncryptionException encryptionException = new EncryptionException(throwable); 41 | assertEquals("Must be the same", throwable, encryptionException.getCause()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/encryption/EncryptionHelperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.encryption; 2 | 3 | import static org.easymock.EasyMock.expect; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.io.ObjectInputStream; 10 | import java.io.ObjectOutputStream; 11 | 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.powermock.api.easymock.PowerMock; 15 | import org.powermock.core.classloader.annotations.PrepareForTest; 16 | import org.powermock.modules.junit4.PowerMockRunner; 17 | 18 | import android.content.SharedPreferences; 19 | import android.util.Base64; 20 | 21 | /** 22 | * JUnit test for {@link EncryptionHelper} class. 23 | * @author NoTiCe 24 | * 25 | */ 26 | @RunWith(PowerMockRunner.class) 27 | @PrepareForTest({EncryptionHelper.class, Base64.class}) 28 | public class EncryptionHelperTest extends PowerMock { 29 | 30 | @Test 31 | public void testReadAndDecodeTemplate() throws Exception { 32 | // GIVEN 33 | byte[] mockBytaArray = new byte[]{}; 34 | byte[] mockEncryptedBytaArray = new byte[]{}; 35 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 36 | ByteArrayInputStream mockBais = createMock(ByteArrayInputStream.class); 37 | ObjectInputStream mockOis = createMock(ObjectInputStream.class); 38 | mockStatic(Base64.class); 39 | Object fooResultString = "fooResultString"; 40 | String mockString = "fooString"; 41 | SharedPreferences prefs = createMock(SharedPreferences.class); 42 | String key = "fooKey"; 43 | String defValue = "defValue"; 44 | 45 | // EXPECT 46 | expect(prefs.getString(key, null)).andReturn(mockString); 47 | expect(Base64.decode(mockString, Base64.DEFAULT)).andReturn(mockEncryptedBytaArray); 48 | expect(mockEncryption.decrypt(mockEncryptedBytaArray)).andReturn(mockBytaArray); 49 | expectNew(ByteArrayInputStream.class, mockBytaArray).andReturn(mockBais); 50 | expectNew(ObjectInputStream.class, mockBais).andReturn(mockOis); 51 | expect(mockOis.readObject()).andReturn(fooResultString); 52 | replayAll(); 53 | // WHEN 54 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 55 | String decoded = helper.readAndDecodeTemplate(prefs, key, defValue); 56 | 57 | // THEN 58 | assertEquals("Must equal", fooResultString, decoded); 59 | verifyAll(); 60 | } 61 | 62 | @Test 63 | public void testReadAndDecodeTemplateGetDefaultValue() throws Exception { 64 | // GIVEN 65 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 66 | mockStatic(Base64.class); 67 | SharedPreferences prefs = createMock(SharedPreferences.class); 68 | String key = "fooKey"; 69 | String defValue = "defValue"; 70 | 71 | // EXPECT 72 | expect(prefs.getString(key, null)).andReturn(null); 73 | replayAll(); 74 | // WHEN 75 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 76 | String decoded = helper.readAndDecodeTemplate(prefs, key, defValue); 77 | 78 | // THEN 79 | assertEquals("Must equal", defValue, decoded); 80 | verifyAll(); 81 | } 82 | 83 | @Test 84 | public void testReadAndDecodeTemplateGetDefaultValueOnIOException() throws Exception { 85 | // GIVEN 86 | byte[] mockBytaArray = new byte[]{}; 87 | byte[] mockEncryptedBytaArray = new byte[]{}; 88 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 89 | ByteArrayInputStream mockBais = createMock(ByteArrayInputStream.class); 90 | ObjectInputStream mockOis = createMock(ObjectInputStream.class); 91 | mockStatic(Base64.class); 92 | String mockString = "fooString"; 93 | SharedPreferences prefs = createMock(SharedPreferences.class); 94 | String key = "fooKey"; 95 | String defValue = "defValue"; 96 | IOException ioException = new IOException(); 97 | 98 | // EXPECT 99 | expect(prefs.getString(key, null)).andReturn(mockString); 100 | expect(Base64.decode(mockString, Base64.DEFAULT)).andReturn(mockEncryptedBytaArray); 101 | expect(mockEncryption.decrypt(mockEncryptedBytaArray)).andReturn(mockBytaArray); 102 | expectNew(ByteArrayInputStream.class, mockBytaArray).andReturn(mockBais); 103 | expectNew(ObjectInputStream.class, mockBais).andReturn(mockOis); 104 | expect(mockOis.readObject()).andThrow(ioException); 105 | replayAll(); 106 | // WHEN 107 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 108 | String decoded = helper.readAndDecodeTemplate(prefs, key, defValue); 109 | 110 | // THEN 111 | assertEquals("Must equal", defValue, decoded); 112 | verifyAll(); 113 | } 114 | 115 | @Test 116 | public void testReadAndDecodeTemplateGetDefaultValueOnClassNotFoundException() throws Exception { 117 | // GIVEN 118 | byte[] mockBytaArray = new byte[]{}; 119 | byte[] mockEncryptedBytaArray = new byte[]{}; 120 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 121 | ByteArrayInputStream mockBais = createMock(ByteArrayInputStream.class); 122 | ObjectInputStream mockOis = createMock(ObjectInputStream.class); 123 | mockStatic(Base64.class); 124 | String mockString = "fooString"; 125 | SharedPreferences prefs = createMock(SharedPreferences.class); 126 | String key = "fooKey"; 127 | String defValue = "defValue"; 128 | ClassNotFoundException classNotFoundException = new ClassNotFoundException(); 129 | 130 | // EXPECT 131 | expect(prefs.getString(key, null)).andReturn(mockString); 132 | expect(Base64.decode(mockString, Base64.DEFAULT)).andReturn(mockEncryptedBytaArray); 133 | expect(mockEncryption.decrypt(mockEncryptedBytaArray)).andReturn(mockBytaArray); 134 | expectNew(ByteArrayInputStream.class, mockBytaArray).andReturn(mockBais); 135 | expectNew(ObjectInputStream.class, mockBais).andReturn(mockOis); 136 | expect(mockOis.readObject()).andThrow(classNotFoundException); 137 | replayAll(); 138 | // WHEN 139 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 140 | String decoded = helper.readAndDecodeTemplate(prefs, key, defValue); 141 | 142 | // THEN 143 | assertEquals("Must equal", defValue, decoded); 144 | verifyAll(); 145 | } 146 | 147 | @Test 148 | public void testEncode() throws Exception { 149 | // GIVEN 150 | byte[] mockBytaArray = new byte[]{}; 151 | byte[] mockEncryptedBytaArray = new byte[]{}; 152 | String mockString = "fooString"; 153 | Object value = createMock(Object.class); 154 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 155 | ByteArrayOutputStream mockBaos = createMock(ByteArrayOutputStream.class); 156 | ObjectOutputStream mockOos = createMock(ObjectOutputStream.class); 157 | mockStatic(Base64.class); 158 | 159 | // EXPECT 160 | expectNew(ByteArrayOutputStream.class).andReturn(mockBaos); 161 | expectNew(ObjectOutputStream.class, mockBaos).andReturn(mockOos); 162 | mockOos.writeObject(value); 163 | expect(mockBaos.toByteArray()).andReturn(mockBytaArray); 164 | expect(mockEncryption.encrypt(mockBytaArray)).andReturn(mockEncryptedBytaArray); 165 | expect(Base64.encodeToString(mockEncryptedBytaArray, Base64.DEFAULT)).andReturn(mockString); 166 | replayAll(); 167 | // WHEN 168 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 169 | String encoded = helper.encode(value); 170 | 171 | // THEN 172 | assertEquals("Must equal", mockString, encoded); 173 | verifyAll(); 174 | } 175 | 176 | @Test 177 | public void testEncodeWontHarmOnEncryptionException() throws Exception { 178 | // GIVEN 179 | EncryptionException e = new EncryptionException(); 180 | byte[] mockBytaArray = new byte[]{}; 181 | Object value = createMock(Object.class); 182 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 183 | ByteArrayOutputStream mockBaos = createMock(ByteArrayOutputStream.class); 184 | ObjectOutputStream mockOos = createMock(ObjectOutputStream.class); 185 | mockStatic(Base64.class); 186 | 187 | // EXPECT 188 | expectNew(ByteArrayOutputStream.class).andReturn(mockBaos); 189 | expectNew(ObjectOutputStream.class, mockBaos).andReturn(mockOos); 190 | mockOos.writeObject(value); 191 | expect(mockBaos.toByteArray()).andReturn(mockBytaArray); 192 | expect(mockEncryption.encrypt(mockBytaArray)).andThrow(e); 193 | replayAll(); 194 | // WHEN 195 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 196 | String encoded = helper.encode(value); 197 | 198 | // THEN 199 | assertEquals("Must equal null, was an error inside.", null, encoded); 200 | verifyAll(); 201 | } 202 | 203 | @Test 204 | public void testEncodeWontHarmOnIOException() throws Exception { 205 | // GIVEN 206 | IOException e = new IOException(); 207 | Object value = createMock(Object.class); 208 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 209 | ByteArrayOutputStream mockBaos = createMock(ByteArrayOutputStream.class); 210 | ObjectOutputStream mockOos = createMock(ObjectOutputStream.class); 211 | mockStatic(Base64.class); 212 | 213 | // EXPECT 214 | expectNew(ByteArrayOutputStream.class).andReturn(mockBaos); 215 | expectNew(ObjectOutputStream.class, mockBaos).andReturn(mockOos); 216 | mockOos.writeObject(value); 217 | expectLastCall().andThrow(e); 218 | replayAll(); 219 | // WHEN 220 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 221 | String encoded = helper.encode(value); 222 | 223 | // THEN 224 | assertEquals("Must equal null, was an error inside.", null, encoded); 225 | verifyAll(); 226 | } 227 | 228 | @Test 229 | public void testEncodeNullReturnsNull() throws Exception { 230 | // GIVEN 231 | String mockString = null; 232 | EncryptionAlgorithm mockEncryption = createMock(EncryptionAlgorithm.class); 233 | 234 | // EXPECT 235 | replayAll(); 236 | // WHEN 237 | EncryptionHelper helper = new EncryptionHelper(mockEncryption); 238 | String encoded = helper.encode(mockString); 239 | 240 | // THEN 241 | assertEquals("Must equal", mockString, encoded); 242 | verifyAll(); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /secure-preferences/src/test/java/com/github/kovmarci86/android/secure/preferences/util/SecureUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences.util; 2 | 3 | import static org.easymock.EasyMock.expect; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.LinkedHashMap; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import org.easymock.EasyMockSupport; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.powermock.api.easymock.PowerMock; 17 | import org.powermock.core.classloader.annotations.PrepareForTest; 18 | import org.powermock.modules.junit4.PowerMockRunner; 19 | 20 | import android.content.SharedPreferences; 21 | import android.content.SharedPreferences.Editor; 22 | 23 | import com.github.kovmarci86.android.secure.preferences.SecuredEditor; 24 | 25 | @RunWith(PowerMockRunner.class) 26 | @PrepareForTest({SecuredEditor.class}) 27 | public class SecureUtilsTest extends EasyMockSupport { 28 | private static final String STRING_SET_KEY = "stringSet"; 29 | private static final Set STRING_SET_VAULE = new HashSet(Arrays.asList(new String[]{"fooValue1", "fooValue2"})); 30 | private static final String BOOLEAN_KEY = "boolean"; 31 | private static final boolean BOOLEAN_VALUE = true; 32 | private static final String STRING_KEY = "string"; 33 | private static final String STRING_VALUE = "fooString"; 34 | private static final String FLOAT_KEY = "float"; 35 | private static final float FLOAT_VALUE = 12344.0f; 36 | private static final String INT_KEY = "int"; 37 | private static final int INT_VALUE = 34234; 38 | private static final String LONG_KEY = "long"; 39 | private static final long LONG_VALUE = 3984; 40 | private static final String VERSION_KEY = "SecurePreferences_version"; 41 | 42 | @Before 43 | public void setUp() throws Exception { 44 | } 45 | 46 | @SuppressWarnings("unchecked") 47 | @Test 48 | public void testMigrateData() { 49 | // GIVEN 50 | PowerMock.mockStatic(SecuredEditor.class); 51 | SharedPreferences sourcePreferences = createMock(SharedPreferences.class); 52 | SharedPreferences targetPreference = createMock(SharedPreferences.class); 53 | Editor targetEditor = createMock(Editor.class); 54 | int fooVersion = 8549734; 55 | Map prefs = new LinkedHashMap(); 56 | prefs.put(STRING_SET_KEY, STRING_SET_VAULE); 57 | prefs.put(BOOLEAN_KEY, BOOLEAN_VALUE); 58 | prefs.put(STRING_KEY, STRING_VALUE); 59 | prefs.put(FLOAT_KEY, FLOAT_VALUE); 60 | prefs.put(INT_KEY, INT_VALUE); 61 | prefs.put(LONG_KEY, LONG_VALUE); 62 | // EXPECT 63 | expect((Map) sourcePreferences.getAll()).andReturn(prefs); 64 | expect(targetPreference.edit()).andReturn(targetEditor); 65 | expect(targetEditor.putStringSet(STRING_SET_KEY, STRING_SET_VAULE)).andReturn(targetEditor); 66 | expect(targetEditor.putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE)).andReturn(targetEditor); 67 | expect(targetEditor.putString(STRING_KEY, STRING_VALUE)).andReturn(targetEditor); 68 | expect(targetEditor.putFloat(FLOAT_KEY, FLOAT_VALUE)).andReturn(targetEditor); 69 | expect(targetEditor.putInt(INT_KEY, INT_VALUE)).andReturn(targetEditor); 70 | expect(targetEditor.putLong(LONG_KEY, LONG_VALUE)).andReturn(targetEditor); 71 | expect(targetEditor.putInt(VERSION_KEY, fooVersion)).andReturn(targetEditor); 72 | SecuredEditor.compatilitySave(targetEditor); 73 | replayAll(); 74 | PowerMock.replay(SecuredEditor.class); 75 | // WHEN 76 | SecureUtils.migrateData(sourcePreferences, targetPreference, fooVersion); 77 | //THEN 78 | verifyAll(); 79 | PowerMock.verify(SecuredEditor.class); 80 | } 81 | 82 | @Test 83 | public void testGetVersion() { 84 | // GIVEN 85 | SharedPreferences preferences = createMock(SharedPreferences.class); 86 | int fooVersion = 8549734; 87 | // EXPECT 88 | expect(preferences.getInt(VERSION_KEY, -1)).andReturn(fooVersion); 89 | replayAll(); 90 | // WHEN 91 | int version = SecureUtils.getVersion(preferences); 92 | //THEN 93 | verifyAll(); 94 | assertEquals("Wrong version result", fooVersion, version); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /secure-storage-it/.gitignore: -------------------------------------------------------------------------------- 1 | /*.iml 2 | -------------------------------------------------------------------------------- /secure-storage-it/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /secure-storage-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.kovmarci86.android.secure 7 | secure-storage-parent 8 | 0.0.2-SNAPSHOT 9 | 10 | 11 | secure-storage-it 12 | apk 13 | Integration tests 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | secure-preferences 24 | com.github.kovmarci86.android.secure 25 | 26 | 27 | 28 | com.google.android 29 | android-test 30 | provided 31 | 32 | 33 | 34 | com.github.kovmarci86.android.secure 35 | secure-storage-sample-app 36 | apk 37 | ${zipaligned-classifier} 38 | provided 39 | 40 | 41 | com.github.kovmarci86.android.secure 42 | secure-storage-sample-app 43 | jar 44 | provided 45 | 46 | 47 | junit 48 | junit 49 | 50 | 51 | 52 | 53 | 54 | 55 | com.jayway.maven.plugins.android.generation2 56 | android-maven-plugin 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | release 65 | 66 | 68 | aligned 69 | 70 | 71 | 72 | 73 | maven-jarsigner-plugin 74 | 75 | 76 | sign-application-it-apk 77 | package 78 | 79 | sign 80 | 81 | 82 | 83 | 84 | 85 | com.jayway.maven.plugins.android.generation2 86 | android-maven-plugin 87 | 88 | true 89 | 90 | false 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Integration tests using sample application 100 | 101 | -------------------------------------------------------------------------------- /secure-storage-it/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-11 15 | -------------------------------------------------------------------------------- /secure-storage-it/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-it/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /secure-storage-it/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-it/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /secure-storage-it/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-it/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /secure-storage-it/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /secure-storage-it/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello secure-storage-it! 4 | secure-storage-it - tests 5 | 6 | -------------------------------------------------------------------------------- /secure-storage-it/src/main/java/com/github/kovmarci86/android/secure/preferences/SecureFactoryFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import com.github.kovmarci86.android.secure.preferences.SecureFactory; 8 | import com.github.kovmarci86.android.secure.preferences.SecureSharedPreferences; 9 | 10 | import android.content.Context; 11 | import android.content.SharedPreferences; 12 | import android.content.SharedPreferences.Editor; 13 | import android.test.InstrumentationTestCase; 14 | 15 | /** 16 | * Functional test for {@link SecureFactory}. 17 | * @author NoTiCe 18 | */ 19 | public class SecureFactoryFunctionalTest extends InstrumentationTestCase { 20 | 21 | private static final String SHARED_PREFERENCES_NAME = "fooPreferences"; 22 | private static final String STRING_SET_KEY = "stringSet"; 23 | private static final Set STRING_SET_VAULE = new HashSet(Arrays.asList(new String[]{"fooValue1", "fooValue2"})); 24 | private static final String BOOLEAN_KEY = "boolean"; 25 | private static final boolean BOOLEAN_VALUE = true; 26 | private static final String STRING_KEY = "string"; 27 | private static final String STRING_VALUE = "fooString"; 28 | private static final String FLOAT_KEY = "float"; 29 | private static final float FLOAT_VALUE = 12344.0f; 30 | private static final String INT_KEY = "int"; 31 | private static final int INT_VALUE = 34234; 32 | private static final String LONG_KEY = "long"; 33 | private static final long LONG_VALUE = 3984; 34 | private static final String PASSWORD = "fooPassword"; 35 | 36 | private SharedPreferences unencryptedPreferences; 37 | 38 | public void setUp() throws Exception { 39 | super.tearDown(); 40 | unencryptedPreferences = getInstrumentation().getContext().getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); 41 | Editor edit = unencryptedPreferences.edit(); 42 | edit.putStringSet(STRING_SET_KEY, STRING_SET_VAULE); 43 | edit.putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE); 44 | edit.putString(STRING_KEY, STRING_VALUE); 45 | edit.putFloat(FLOAT_KEY, FLOAT_VALUE); 46 | edit.putInt(INT_KEY, INT_VALUE); 47 | edit.putLong(LONG_KEY, LONG_VALUE); 48 | edit.commit(); 49 | } 50 | 51 | @Override 52 | protected void tearDown() throws Exception { 53 | super.tearDown(); 54 | unencryptedPreferences.edit().clear().commit(); 55 | } 56 | 57 | /** 58 | * Tries to read back values after doing the migration from an unencrypted {@link SharedPreferences}. 59 | */ 60 | public void testMigrationWorks() { 61 | SharedPreferences preferences = SecureFactory.getPreferences(unencryptedPreferences, PASSWORD); 62 | assertEquals(FLOAT_KEY + " not found.", preferences.getFloat(FLOAT_KEY, 0), FLOAT_VALUE); 63 | assertEquals(BOOLEAN_KEY + " not found.", preferences.getBoolean(BOOLEAN_KEY, false), BOOLEAN_VALUE); 64 | assertEquals(INT_KEY + " not found.", preferences.getInt(INT_KEY, 0), INT_VALUE); 65 | assertEquals(STRING_KEY + " not found.", preferences.getString(STRING_KEY, null), STRING_VALUE); 66 | assertEquals(LONG_KEY + " not found.", preferences.getLong(LONG_KEY, 0), LONG_VALUE); 67 | Arrays.equals(STRING_SET_VAULE.toArray(), preferences.getStringSet(STRING_SET_KEY, new HashSet()).toArray()); 68 | } 69 | 70 | /** 71 | * Tries to access the encrypted {@link SecureSharedPreferences} with another {@link SharedPreferences}. 72 | */ 73 | public void testMigrationEncrypts() { 74 | SharedPreferences preferences = SecureFactory.getPreferences(unencryptedPreferences, PASSWORD); 75 | try { 76 | preferences.getFloat(FLOAT_KEY, 0); 77 | fail("not encrypted."); 78 | } catch (Throwable e) { 79 | // success 80 | } 81 | } 82 | 83 | /** 84 | * Tries to access an already encrypted {@link SecureSharedPreferences} again. 85 | */ 86 | public void testNoDoubleMigration() { 87 | testMigrationWorks(); 88 | testMigrationWorks(); 89 | testMigrationWorks(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /secure-storage-it/src/main/java/com/github/kovmarci86/android/secure/preferences/SecureSharedPreferencesFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kovmarci86.android.secure.preferences; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.SharedPreferences.Editor; 6 | import android.test.InstrumentationTestCase; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.github.kovmarci86.android.secure.preferences.SecureSharedPreferences; 12 | import com.github.kovmarci86.android.secure.preferences.SecuredEditor; 13 | 14 | import java.util.LinkedHashSet; 15 | import java.util.Set; 16 | 17 | import edu.gmu.tec.scout.utilities.Encryption; 18 | 19 | /** 20 | * Tests the {@link SecureSharedPreferences} in action. 21 | * @author NoTiCe 22 | */ 23 | public class SecureSharedPreferencesFunctionalTest extends InstrumentationTestCase { 24 | private static final Logger LOGGER = LoggerFactory.getLogger(SecureSharedPreferencesFunctionalTest.class); 25 | private static final String FOO_PREFERENCES_NAME = "fooPreferencesName"; 26 | private static final String FOO_PASSWORD = "fooPassword"; 27 | private static final String STRING_VALUE = "testString"; 28 | private static final Set STRING_SET_VALUE = new LinkedHashSet(); 29 | // test values 30 | private static final String WRONG_VALUE_MESSAGE = "Wrong value"; 31 | private static final String STRING_KEY = "string"; 32 | private static final String STRING_SET_KEY = "set"; 33 | private static final String LONG_KEY = "long"; 34 | private static final String INT_KEY = "int"; 35 | private static final String FLOAT_KEY = "float"; 36 | private static final String BOOLEAN_KEY = "boolean"; 37 | private static final long LONG_VALUE = 1L; 38 | private static final int INT_VALUE = 1; 39 | private static final float FLOAT_VALUE = 1.0f; 40 | private static final boolean BOOLEAN_VALUE = true; 41 | private SecureSharedPreferences sut; 42 | 43 | public SecureSharedPreferencesFunctionalTest() { 44 | STRING_SET_VALUE.add("item1"); 45 | STRING_SET_VALUE.add("item2"); 46 | STRING_SET_VALUE.add("item3"); 47 | } 48 | 49 | /** 50 | * Creates the {@link Encryption} and the {@link SecureSharedPreferences} 51 | * instances for testing. 52 | * @throws Exception In unexpected cases. 53 | */ 54 | @Override 55 | protected void setUp() throws Exception { 56 | super.setUp(); 57 | 58 | SharedPreferences preferences = getInstrumentation().getTargetContext().getSharedPreferences(FOO_PREFERENCES_NAME, Context.MODE_PRIVATE); 59 | Encryption encryption = new Encryption(FOO_PASSWORD); 60 | sut = new SecureSharedPreferences(preferences, encryption); 61 | } 62 | 63 | /** 64 | * Tests if {@link SecureSharedPreferences} can read back it's stored 65 | * values. 66 | */ 67 | public void testSecureSharedPreferencesWorks() throws Exception { 68 | // Put values in it 69 | SecuredEditor edit = sut.edit(); 70 | edit.putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE); 71 | edit.putFloat(FLOAT_KEY, FLOAT_VALUE); 72 | edit.putInt(INT_KEY, INT_VALUE); 73 | edit.putLong(LONG_KEY, LONG_VALUE); 74 | edit.putString(STRING_KEY, STRING_VALUE); 75 | edit.putStringSet(STRING_SET_KEY, STRING_SET_VALUE); 76 | edit.commit(); 77 | 78 | // read back the values with new instance of SharedPreferences 79 | setUp(); 80 | 81 | assertEquals(WRONG_VALUE_MESSAGE, BOOLEAN_VALUE, sut.getBoolean(BOOLEAN_KEY, false)); 82 | assertEquals(WRONG_VALUE_MESSAGE, FLOAT_VALUE, sut.getFloat(FLOAT_KEY, 0f)); 83 | assertEquals(WRONG_VALUE_MESSAGE, INT_VALUE, sut.getInt(INT_KEY, 0)); 84 | assertEquals(WRONG_VALUE_MESSAGE, LONG_VALUE, sut.getLong(LONG_KEY, 0L)); 85 | assertEquals(WRONG_VALUE_MESSAGE, STRING_VALUE, sut.getString(STRING_KEY, null)); 86 | assertEquals(WRONG_VALUE_MESSAGE, STRING_SET_VALUE, sut.getStringSet(STRING_SET_KEY, null)); 87 | } 88 | 89 | /** 90 | * Tests if {@link SecureSharedPreferences} can read back it's stored 91 | * values. 92 | */ 93 | public void testSecureSharedPreferencesFluentAPIWorks() throws Exception { 94 | // Put values in it 95 | sut.edit().putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE).putFloat(FLOAT_KEY, FLOAT_VALUE).putInt(INT_KEY, INT_VALUE).putLong(LONG_KEY, LONG_VALUE) 96 | .putString(STRING_KEY, STRING_VALUE).putStringSet(STRING_SET_KEY, STRING_SET_VALUE).commit(); 97 | 98 | // read back the values with new instance of SharedPreferences 99 | setUp(); 100 | 101 | assertEquals(WRONG_VALUE_MESSAGE, BOOLEAN_VALUE, sut.getBoolean(BOOLEAN_KEY, false)); 102 | assertEquals(WRONG_VALUE_MESSAGE, FLOAT_VALUE, sut.getFloat(FLOAT_KEY, 0f)); 103 | assertEquals(WRONG_VALUE_MESSAGE, INT_VALUE, sut.getInt(INT_KEY, 0)); 104 | assertEquals(WRONG_VALUE_MESSAGE, LONG_VALUE, sut.getLong(LONG_KEY, 0L)); 105 | assertEquals(WRONG_VALUE_MESSAGE, STRING_VALUE, sut.getString(STRING_KEY, null)); 106 | assertEquals(WRONG_VALUE_MESSAGE, STRING_SET_VALUE, sut.getStringSet(STRING_SET_KEY, null)); 107 | } 108 | 109 | /** 110 | * Tests if {@link SecureSharedPreferences} can read back it's stored 111 | * values. 112 | */ 113 | public void testSecureSharedPreferencesApply() throws Exception { 114 | // Put values in it 115 | sut.edit().putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE).putFloat(FLOAT_KEY, FLOAT_VALUE).putInt(INT_KEY, INT_VALUE).putLong(LONG_KEY, LONG_VALUE) 116 | .putString(STRING_KEY, STRING_VALUE).putStringSet(STRING_SET_KEY, STRING_SET_VALUE).apply(); 117 | 118 | // read back the values with new instance of SharedPreferences 119 | assertEquals(WRONG_VALUE_MESSAGE, BOOLEAN_VALUE, sut.getBoolean(BOOLEAN_KEY, false)); 120 | assertEquals(WRONG_VALUE_MESSAGE, FLOAT_VALUE, sut.getFloat(FLOAT_KEY, 0f)); 121 | assertEquals(WRONG_VALUE_MESSAGE, INT_VALUE, sut.getInt(INT_KEY, 0)); 122 | assertEquals(WRONG_VALUE_MESSAGE, LONG_VALUE, sut.getLong(LONG_KEY, 0L)); 123 | assertEquals(WRONG_VALUE_MESSAGE, STRING_VALUE, sut.getString(STRING_KEY, null)); 124 | assertEquals(WRONG_VALUE_MESSAGE, STRING_SET_VALUE, sut.getStringSet(STRING_SET_KEY, null)); 125 | } 126 | 127 | /** 128 | * Writes values to an encoded {@link SecureSharedPreferences}, then tries 129 | * to read it back using a non encoded {@link SharedPreferences}. These 130 | * reads must fail and will trigger a {@link ClassCastException}. String 131 | * reads will return their encoded value. 132 | */ 133 | public void testSecureSharedPreferencesIsEncrypted() { 134 | // Put values in it 135 | Editor edit = sut.edit(); 136 | edit.putBoolean(BOOLEAN_KEY, BOOLEAN_VALUE); 137 | edit.putFloat(FLOAT_KEY, FLOAT_VALUE); 138 | edit.putInt(INT_KEY, INT_VALUE); 139 | edit.putLong(LONG_KEY, LONG_VALUE); 140 | edit.putString(STRING_KEY, STRING_VALUE); 141 | edit.commit(); 142 | // read back the values from simple SharedPreferences instance 143 | SharedPreferences nonSecure = getInstrumentation().getTargetContext().getSharedPreferences(FOO_PREFERENCES_NAME, Context.MODE_PRIVATE); 144 | try { 145 | nonSecure.getBoolean(BOOLEAN_KEY, false); 146 | fail("This command must not work!"); 147 | } catch (Throwable e) { 148 | LOGGER.info("Read did not work, this is normal."); 149 | } 150 | try { 151 | nonSecure.getFloat(FLOAT_KEY, 0f); 152 | fail("This command must not work!"); 153 | } catch (Throwable e) { 154 | LOGGER.info("Read did not work, this is normal."); 155 | } 156 | try { 157 | nonSecure.getInt(INT_KEY, 0); 158 | fail("This command must not work!"); 159 | } catch (Throwable e) { 160 | LOGGER.info("Read did not work, this is normal."); 161 | } 162 | try { 163 | nonSecure.getLong(LONG_KEY, 0L); 164 | fail("This command must not work!"); 165 | } catch (Throwable e) { 166 | LOGGER.info("Read did not work, this is normal."); 167 | } 168 | try { 169 | String result = nonSecure.getString(STRING_KEY, null); 170 | assertFalse("The resulting string may not be empty.", STRING_VALUE.equals(result)); 171 | } catch (Throwable e) { 172 | LOGGER.info("Read did not work, this is normal."); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /secure-storage-sample-app/.gitignore: -------------------------------------------------------------------------------- 1 | /*.iml 2 | -------------------------------------------------------------------------------- /secure-storage-sample-app/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 14 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /secure-storage-sample-app/androlog/androlog.properties: -------------------------------------------------------------------------------- 1 | # Androlog configuration 2 | # this file must be pushed to /sdcard/androlog.properties: 3 | # adb push androlog.properties /sdcard/androlog.properties 4 | # more detail on: https://github.com/akquinet/androlog/wiki/Androlog-configuration 5 | androlog.active = true 6 | 7 | #Configure the default log level: 8 | #androlog.default.level=INFO 9 | 10 | #Configure specific loggers 11 | #com.mkovacs.android.secure.HelloAndroidActivity=INFO 12 | 13 | #Enable the reporting 14 | #More info on: https://github.com/akquinet/androlog/wiki/Androlog-reporting 15 | #androlog.report.active=true 16 | #androlog.report.reporters=de.akquinet.android.androlog.reporter.MailReporter 17 | #androlog.reporter.mail.address=me@my.company.com 18 | #androlog.report.default.level=WARN 19 | #androlog.report.log.items=40 20 | #androlog.report.trigger.level=ERROR -------------------------------------------------------------------------------- /secure-storage-sample-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.github.kovmarci86.android.secure 7 | secure-storage-parent 8 | 0.0.2-SNAPSHOT 9 | 10 | 11 | secure-storage-sample-app 12 | apk 13 | Secure storage sample app 14 | 15 | 16 | 17 | com.google.android 18 | android 19 | 20 | 21 | de.akquinet.android.androlog 22 | androlog 23 | 24 | 25 | com.github.kovmarci86.android.secure 26 | secure-preferences 27 | 28 | 29 | 30 | 31 | 32 | 33 | com.jayway.maven.plugins.android.generation2 34 | android-maven-plugin 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | release 43 | 44 | 45 | 46 | com.pyx4me 47 | proguard-maven-plugin 48 | 49 | 50 | process-classes-with-proguard 51 | process-classes 52 | 53 | proguard 54 | 55 | 56 | 4.4 57 | 256m 58 | classes 59 | 60 | ${rt.jar.path} 61 | ${jsse.jar.path} 62 | 63 | true 64 | false 65 | ${project.basedir}/proguard.conf 66 | 67 | 68 | 69 | 70 | 71 | net.sf.proguard 72 | proguard 73 | 4.4 74 | runtime 75 | 76 | 77 | 78 | 79 | maven-jarsigner-plugin 80 | 81 | 82 | sign-application-apk 83 | package 84 | 85 | sign 86 | verify 87 | 88 | 89 | 90 | 91 | 92 | com.jayway.maven.plugins.android.generation2 93 | android-maven-plugin 94 | 95 | 96 | zipalign-application-apk 97 | package 98 | 99 | zipalign 100 | 101 | 102 | 103 | 104 | true 105 | 106 | true 107 | ${project.build.directory}/${project.artifactId}-${project.version}.apk 108 | ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk 109 | 110 | 111 | false 112 | 113 | 114 | 115 | 116 | org.codehaus.mojo 117 | build-helper-maven-plugin 118 | 119 | 120 | 121 | ${project.build.directory}/proguard_map.txt 122 | map 123 | release 124 | 125 | 126 | 127 | 128 | 129 | attach-signed-aligned 130 | package 131 | 132 | attach-artifact 133 | 134 | 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-compiler-plugin 140 | 141 | 1.6 142 | 1.6 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Sample Android application using the SecureSharedPreferences. 151 | 152 | -------------------------------------------------------------------------------- /secure-storage-sample-app/proguard.conf: -------------------------------------------------------------------------------- 1 | # Configuration for ProGuard 2 | # From http://proguard.sourceforge.net/index.html#/manual/examples.html#androidapplication 3 | 4 | -dontpreverify 5 | -repackageclasses '' 6 | -optimizations !code/simplification/arithmetic 7 | -keepattributes *Annotation* 8 | 9 | -keep public class * extends android.app.Activity 10 | -keep public class * extends android.app.Application 11 | -keep public class * extends android.app.Service 12 | -keep public class * extends android.content.BroadcastReceiver 13 | -keep public class * extends android.content.ContentProvider 14 | 15 | -keep public class * extends android.view.View { 16 | public (android.content.Context); 17 | public (android.content.Context, android.util.AttributeSet); 18 | public (android.content.Context, android.util.AttributeSet, int); 19 | public void set*(...); 20 | } 21 | 22 | -keepclasseswithmembers class * { 23 | public (android.content.Context, android.util.AttributeSet); 24 | } 25 | 26 | -keepclasseswithmembers class * { 27 | public (android.content.Context, android.util.AttributeSet, int); 28 | } 29 | 30 | -keepclassmembers class * implements android.os.Parcelable { 31 | static android.os.Parcelable$Creator CREATOR; 32 | } 33 | 34 | -keepclassmembers class **.R$* { 35 | public static ; 36 | } 37 | 38 | -keep public interface com.android.vending.licensing.ILicensingService 39 | 40 | -keepclasseswithmembernames class * { 41 | native ; 42 | } 43 | 44 | -keepclassmembers class * extends java.lang.Enum { 45 | public static **[] values(); 46 | public static ** valueOf(java.lang.String); 47 | } 48 | 49 | # Removes all calls to Log. Delete the methods you want to keep. 50 | -assumenosideeffects class android.util.Log { 51 | public static int v(...); 52 | public static int d(...); 53 | public static int i(...); 54 | public static int w(...); 55 | public static int e(...); 56 | public static int wtf(...); 57 | } 58 | -------------------------------------------------------------------------------- /secure-storage-sample-app/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-11 15 | -------------------------------------------------------------------------------- /secure-storage-sample-app/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-sample-app/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /secure-storage-sample-app/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-sample-app/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /secure-storage-sample-app/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovmarci86/android-secure-preferences/017b4a01fd71b552c35f2e3b0981a91524b5fe05/secure-storage-sample-app/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /secure-storage-sample-app/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 |