{"id":30639633,"url":"https://github.com/iammehedi/secured-preference-store","last_synced_at":"2025-08-31T00:09:26.072Z","repository":{"id":57738280,"uuid":"66209171","full_name":"iamMehedi/Secured-Preference-Store","owner":"iamMehedi","description":"A cryptography library and a SharedPreferences wrapper for Android that encrypts the content with 256 bit AES encryption. The Encryption key is securely stored in device's KeyStore. ","archived":false,"fork":false,"pushed_at":"2019-02-26T12:25:04.000Z","size":204,"stargazers_count":561,"open_issues_count":24,"forks_count":97,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-03-27T14:55:48.994Z","etag":null,"topics":["android","device-keystore","encrypted-store","encryption","keystore","sharedpreferences"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamMehedi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-08-21T16:39:04.000Z","updated_at":"2024-12-10T11:13:00.000Z","dependencies_parsed_at":"2022-08-24T09:00:15.262Z","dependency_job_id":null,"html_url":"https://github.com/iamMehedi/Secured-Preference-Store","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/iamMehedi/Secured-Preference-Store","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamMehedi%2FSecured-Preference-Store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamMehedi%2FSecured-Preference-Store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamMehedi%2FSecured-Preference-Store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamMehedi%2FSecured-Preference-Store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamMehedi","download_url":"https://codeload.github.com/iamMehedi/Secured-Preference-Store/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamMehedi%2FSecured-Preference-Store/sbom","scorecard":{"id":477292,"data":{"date":"2025-08-11","repo":{"name":"github.com/iamMehedi/Secured-Preference-Store","commit":"02d71303e81d03b58e7b097c4988e257f84da080"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.1,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":2,"reason":"Found 4/15 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 20 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T15:40:05.279Z","repository_id":57738280,"created_at":"2025-08-19T15:40:05.279Z","updated_at":"2025-08-19T15:40:05.279Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272922346,"owners_count":25015769,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","device-keystore","encrypted-store","encryption","keystore","sharedpreferences"],"created_at":"2025-08-31T00:09:25.129Z","updated_at":"2025-08-31T00:09:26.035Z","avatar_url":"https://github.com/iamMehedi.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Secured--Preference--Store-green.svg?style=true)](https://android-arsenal.com/details/1/4226)\n [![Download](https://api.bintray.com/packages/iammehedi/SecuredPreferenceStore/online.devliving%3Asecuredpreferencestore/images/download.svg) ](https://bintray.com/iammehedi/SecuredPreferenceStore/online.devliving%3Asecuredpreferencestore/_latestVersion)\n\n# Secured-Preference-Store\nA `SharedPreferences` wrapper for Android that encrypts the content with 256 bit AES encryption. The Encryption key is securely stored in device's KeyStore. You can also use the `EncryptionManager` class to encrypt \u0026 decrypt data out of the box. \n\n## Setup\n### Maven\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003eonline.devliving\u003c/groupId\u003e\n  \u003cartifactId\u003esecuredpreferencestore\u003c/artifactId\u003e\n  \u003cversion\u003elatest_version\u003c/version\u003e\n  \u003ctype\u003epom\u003c/type\u003e\n\u003c/dependency\u003e\n````\n\n### Gradle\n```\ncompile 'online.devliving:securedpreferencestore:latest_version'\n```\n\n## Usage\nBefore using the store for the first time you must initialize it\n```\n//not mandatory, can be null too\nString storeFileName = \"securedStore\";\n//not mandatory, can be null too\nString keyPrefix = \"vss\";\n//it's better to provide one, and you need to provide the same key each time after the first time\nbyte[] seedKey = \"SecuredSeedData\".getBytes();\nSecuredPreferenceStore.init(getApplicationContext(), storeFileName, keyPrefix, seedKey, new DefaultRecoveryHandler());\n```\nperhaps in `onCreate` of your `Application`  class or launcher `Activity`. \n\n\nYou can use the secured preference store just like the way you use the default `SharedPreferences`\n```java\nSecuredPreferenceStore prefStore = SecuredPreferenceStore.getSharedInstance(getApplicationContext());\n\nString textShort = prefStore.getString(TEXT_1, null);\nString textLong = prefStore.getString(TEXT_2, null);\nint numberInt = prefStore.getInt(NUMBER_1, 0);\n\nprefStore.edit().putString(TEXT_1, text1.length() \u003e 0 ? text1.getText().toString() : null).apply();\nprefStore.edit().putString(TEXT_2, text2.length() \u003e 0 ? text2.getText().toString() : null).apply();\nprefStore.edit().putInt(NUMBER_1, number1.length() \u003e 0 ? Integer.parseInt(number1.getText().toString().trim()) : 0).commit();\n```\nYou can access the underlying encryption manager to encrypt/decrypt data:\n```\nEncryptionManager encryptionManager = SecuredPreferenceStore.getSharedInstance().getEncryptionManager();\n```\n\nYou can also use a standalone `EncryptionManager` to encrypt/decrypt data on your own:\n```java\nSharedPreferences preferences = getSharedPreferences(\"backingStore\", MODE_PRIVATE);\n//not mandatory, can be null too\nString keyAliasPrefix = \"kp\";\n//not mandatory, can be null too\nbyte[] bitShiftKey = \"bitShiftBits\".getBytes();\nEncryptionManager encryptionManager = new EncryptionManager(getApplicationContext(), preferences,\n    keyAliasPrefix, bitShiftKey, new SecuredPreferenceStore.KeyStoreRecoveryNotifier() {\n\t@Override\n\tpublic boolean onRecoveryRequired(Exception e, KeyStore keyStore, List\u003cString\u003e keyAliases) {\n\t    return false;\n\t}\n});\nEncryptionManager.EncryptedData encryptedData = encryptionManager.encrypt(bytesToEncrypt);\nbyte[] decryptedData = encryptionManager.decrypt(encryptedData);\n```\n## Sample file content\nA sample secured preference file will look like:\n\n```xml\n\u003c?xml version='1.0' encoding='utf-8' standalone='yes' ?\u003e\n\u003cmap\u003e\n    \u003cstring name=\"11CD15241CB4D6F953FA27C76F72C10920C5FADF14FF2824104FA5D67D25B43C\"\u003eZMnr87IlDKg81hKw2SQ6Lw==]dhP/ymX7CMSaCkP6jQvNig==\u003c/string\u003e\n    \u003cstring name=\"C8D076EFD8542A5F02F86B176F667B42BFB9B1472E974E6AF31EB27CEA5689D4\"\u003eJQ6Y4TQ/Y3iYw7KtatkqAg==]P+gpavV0MXiy1Qg0UHlBMg==\u003c/string\u003e\n    \u003cstring name=\"F2AA713F406544A3E9ABA20A32364FA29613F01C867B3D922A85DF4FA54FA13D\"\u003ejMH1Wjnk0vehHOogT27HRA==]e8UHX1ihYjtP6Cv8dWdHLBptLwowt6IojKYa+1jkeH4=\u003c/string\u003e\n    \u003cstring name=\"C06C6027E72B7CE947885F6ADE3A73E338881197DBE02D8B7B7248F629BE26DA\"\u003eEAGwO8u2ZPdxwdpAwPlu6A==]797VOGtpzDBO1ZU3m+Sb1A==\u003c/string\u003e\n    \u003cstring name=\"33188AFFEC74B412765C3C86859DE4620B5427C774D92F9026D95A7A8AAE1F96\"\u003es0b5h8XNnerci5AtallCQziSbqpm+ndjIsAQQadSxM+xzw7865sE3P+hbxGmMAQQj0kK35/C//eA\nMXuQ0N/F+oapBiDIKdRt2GJB3wJ+eshuh6TcEv+J8NQhqn1eO2fdao353XthHpRomIeGEWLvB4Yd\n7G5YYIajLWOGWzQVsMTg1eqdcJ7+BAMXdOdWhjTTo91NvhvykgLMC03FsePOZ/X8ej4vByH1i0en\nhJCiChk90AQ9FhSkaF/Oum9KoWqg7NU0PGurK755VZflXfyn1vZ8hhTulW7BrA2o9HvT9tbju+bk\n4yJ5lMxgS6o4b+0tqo+H4TPOUiZPgehTwsrzJg==\n    \u003c/string\u003e\n    \u003cstring name=\"9DCB904DFDA83286B41A329A7D8648B0BFF73C63E844C88800B2AA5119204845\"\u003eXPuUd1t97pnwsOzzHY3OCA==]xqXJrEfcgDhYo2K4TTAvY9IQwP/tGctd4Fa1JT/1sB8=\u003c/string\u003e\n\u003c/map\u003e\n``` \n\n## NOTICE\nThe keys stored in the `KeyStore` aren't encrypted at rest to avoid [the issue](https://code.google.com/p/android/issues/detail?id=61989) where they get deleted when the device's lock screen protection changes. So if the device doesn't have a hardware backed key storage then the keys might be at a vulnerable state. You can read more about it [here](http://doridori.github.io/android-security-the-forgetful-keystore).\n\n## Recovery\nKeys get **deleted/locked in API levels lower than 21** and sometimes on later versions of the API on some devices when the user changes the device's security (screen lock protection). This phenomena is due to few issues in the `Keystore` implementation i.e [61989](https://code.google.com/p/android/issues/detail?id=61989), [177459](https://code.google.com/p/android/issues/detail?id=177459). Until those issues are fixed we need a way to recover from that scenario, otherwise the app itself might become unusable. To enable recovery you can add a `RecoveryHandler` to `SecuredPreferenceStore` before calling `getSharedInstance` for the first time. \n\n```java\nSecuredPreferenceStore.setRecoveryHandler(new RecoveryHandler() {\n            @Override\n            protected void recover(Exception e, KeyStore keyStore, List\u003cString\u003e keyAliases, SharedPreferences preferences) {\n                //Your recovery code goes here\n            }\n        });\n```\nA default recovery handler called `DefaultRecoveryHandler` is included in the library which deletes the keys and data, giving the library a chance to start over. \n\n## License\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n\t   http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n\n\tCopyright 2017 Mehedi Hasan Khan\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiammehedi%2Fsecured-preference-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiammehedi%2Fsecured-preference-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiammehedi%2Fsecured-preference-store/lists"}