Moving null checks from CipherStorageFacebookConceal and PrefsStorage to KeychainModule and introducing checks for empty service in CipherStorageKeystoreAESCBC.
This commit is contained in:
parent
71f567d65a
commit
ef9b5256ef
|
@ -30,6 +30,7 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||||
public static final String E_CRYPTO_FAILED = "E_CRYPTO_FAILED";
|
public static final String E_CRYPTO_FAILED = "E_CRYPTO_FAILED";
|
||||||
public static final String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR";
|
public static final String E_KEYSTORE_ACCESS_ERROR = "E_KEYSTORE_ACCESS_ERROR";
|
||||||
public static final String KEYCHAIN_MODULE = "RNKeychainManager";
|
public static final String KEYCHAIN_MODULE = "RNKeychainManager";
|
||||||
|
public static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
private final Map<String, CipherStorage> cipherStorageMap = new HashMap<>();
|
private final Map<String, CipherStorage> cipherStorageMap = new HashMap<>();
|
||||||
private final PrefsStorage prefsStorage;
|
private final PrefsStorage prefsStorage;
|
||||||
|
@ -57,6 +58,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||||
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
|
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
|
||||||
throw new EmptyParameterException("you passed empty or null username/password");
|
throw new EmptyParameterException("you passed empty or null username/password");
|
||||||
}
|
}
|
||||||
|
service = getDefaultServiceIfNull(service);
|
||||||
|
|
||||||
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
|
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
|
||||||
|
|
||||||
EncryptionResult result = currentCipherStorage.encrypt(service, username, password);
|
EncryptionResult result = currentCipherStorage.encrypt(service, username, password);
|
||||||
|
@ -75,6 +78,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void getGenericPasswordForOptions(String service, Promise promise) {
|
public void getGenericPasswordForOptions(String service, Promise promise) {
|
||||||
try {
|
try {
|
||||||
|
service = getDefaultServiceIfNull(service);
|
||||||
|
|
||||||
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
|
CipherStorage currentCipherStorage = getCipherStorageForCurrentAPILevel();
|
||||||
|
|
||||||
final DecryptionResult decryptionResult;
|
final DecryptionResult decryptionResult;
|
||||||
|
@ -121,6 +126,8 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void resetGenericPasswordForOptions(String service, Promise promise) {
|
public void resetGenericPasswordForOptions(String service, Promise promise) {
|
||||||
try {
|
try {
|
||||||
|
service = getDefaultServiceIfNull(service);
|
||||||
|
|
||||||
// First we clean up the cipher storage (using the cipher storage that was used to store the entry)
|
// First we clean up the cipher storage (using the cipher storage that was used to store the entry)
|
||||||
ResultSet resultSet = prefsStorage.getEncryptedEntry(service);
|
ResultSet resultSet = prefsStorage.getEncryptedEntry(service);
|
||||||
if (resultSet != null) {
|
if (resultSet != null) {
|
||||||
|
@ -176,4 +183,9 @@ public class KeychainModule extends ReactContextBaseJavaModule {
|
||||||
private CipherStorage getCipherStorageByName(String cipherStorageName) {
|
private CipherStorage getCipherStorageByName(String cipherStorageName) {
|
||||||
return cipherStorageMap.get(cipherStorageName);
|
return cipherStorageMap.get(cipherStorageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getDefaultServiceIfNull(String service) {
|
||||||
|
return service == null ? EMPTY_STRING : service;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package com.oblador.keychain;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
@ -10,7 +11,6 @@ import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal;
|
||||||
|
|
||||||
public class PrefsStorage {
|
public class PrefsStorage {
|
||||||
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
||||||
public static final String EMPTY_STRING = "";
|
|
||||||
|
|
||||||
static public class ResultSet {
|
static public class ResultSet {
|
||||||
public final String cipherStorageName;
|
public final String cipherStorageName;
|
||||||
|
@ -30,9 +30,7 @@ public class PrefsStorage {
|
||||||
this.prefs = reactContext.getSharedPreferences(KEYCHAIN_DATA, Context.MODE_PRIVATE);
|
this.prefs = reactContext.getSharedPreferences(KEYCHAIN_DATA, Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultSet getEncryptedEntry(String service) {
|
public ResultSet getEncryptedEntry(@NonNull String service) {
|
||||||
service = service == null ? EMPTY_STRING : service;
|
|
||||||
|
|
||||||
byte[] bytesForUsername = getBytesForUsername(service);
|
byte[] bytesForUsername = getBytesForUsername(service);
|
||||||
byte[] bytesForPassword = getBytesForPassword(service);
|
byte[] bytesForPassword = getBytesForPassword(service);
|
||||||
String cipherStorageName = getCipherStorageName(service);
|
String cipherStorageName = getCipherStorageName(service);
|
||||||
|
@ -46,9 +44,7 @@ public class PrefsStorage {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeEntry(String service) {
|
public void removeEntry(@NonNull String service) {
|
||||||
service = service == null ? EMPTY_STRING : service;
|
|
||||||
|
|
||||||
String keyForUsername = getKeyForUsername(service);
|
String keyForUsername = getKeyForUsername(service);
|
||||||
String keyForPassword = getKeyForPassword(service);
|
String keyForPassword = getKeyForPassword(service);
|
||||||
String keyForCipherStorage = getKeyForCipherStorage(service);
|
String keyForCipherStorage = getKeyForCipherStorage(service);
|
||||||
|
@ -59,9 +55,7 @@ public class PrefsStorage {
|
||||||
.remove(keyForCipherStorage).apply();
|
.remove(keyForCipherStorage).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeEncryptedEntry(String service, EncryptionResult encryptionResult) {
|
public void storeEncryptedEntry(@NonNull String service, @NonNull EncryptionResult encryptionResult) {
|
||||||
service = service == null ? EMPTY_STRING : service;
|
|
||||||
|
|
||||||
String keyForUsername = getKeyForUsername(service);
|
String keyForUsername = getKeyForUsername(service);
|
||||||
String keyForPassword = getKeyForPassword(service);
|
String keyForPassword = getKeyForPassword(service);
|
||||||
String keyForCipherStorage = getKeyForCipherStorage(service);
|
String keyForCipherStorage = getKeyForCipherStorage(service);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.oblador.keychain.cipherStorage;
|
package com.oblador.keychain.cipherStorage;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.oblador.keychain.exceptions.CryptoFailedException;
|
import com.oblador.keychain.exceptions.CryptoFailedException;
|
||||||
import com.oblador.keychain.exceptions.KeyStoreAccessException;
|
import com.oblador.keychain.exceptions.KeyStoreAccessException;
|
||||||
|
|
||||||
|
@ -29,11 +31,11 @@ public interface CipherStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException;
|
EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException;
|
||||||
|
|
||||||
DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException;
|
DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException;
|
||||||
|
|
||||||
void removeKey(String service) throws KeyStoreAccessException;
|
void removeKey(@NonNull String service) throws KeyStoreAccessException;
|
||||||
|
|
||||||
String getCipherStorageName();
|
String getCipherStorageName();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.oblador.keychain.cipherStorage;
|
package com.oblador.keychain.cipherStorage;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.facebook.android.crypto.keychain.AndroidConceal;
|
import com.facebook.android.crypto.keychain.AndroidConceal;
|
||||||
import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain;
|
import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain;
|
||||||
|
@ -16,7 +17,6 @@ import java.nio.charset.Charset;
|
||||||
public class CipherStorageFacebookConceal implements CipherStorage {
|
public class CipherStorageFacebookConceal implements CipherStorage {
|
||||||
public static final String CIPHER_STORAGE_NAME = "FacebookConceal";
|
public static final String CIPHER_STORAGE_NAME = "FacebookConceal";
|
||||||
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
public static final String KEYCHAIN_DATA = "RN_KEYCHAIN";
|
||||||
public static final String EMPTY_STRING = "";
|
|
||||||
private final Crypto crypto;
|
private final Crypto crypto;
|
||||||
|
|
||||||
public CipherStorageFacebookConceal(ReactApplicationContext reactContext) {
|
public CipherStorageFacebookConceal(ReactApplicationContext reactContext) {
|
||||||
|
@ -35,12 +35,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException {
|
public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException {
|
||||||
if (!crypto.isAvailable()) {
|
if (!crypto.isAvailable()) {
|
||||||
throw new CryptoFailedException("Crypto is missing");
|
throw new CryptoFailedException("Crypto is missing");
|
||||||
}
|
}
|
||||||
service = service == null ? EMPTY_STRING : service;
|
|
||||||
|
|
||||||
Entity usernameEntity = createUsernameEntity(service);
|
Entity usernameEntity = createUsernameEntity(service);
|
||||||
Entity passwordEntity = createPasswordEntity(service);
|
Entity passwordEntity = createPasswordEntity(service);
|
||||||
|
|
||||||
|
@ -55,12 +53,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException {
|
public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException {
|
||||||
if (!crypto.isAvailable()) {
|
if (!crypto.isAvailable()) {
|
||||||
throw new CryptoFailedException("Crypto is missing");
|
throw new CryptoFailedException("Crypto is missing");
|
||||||
}
|
}
|
||||||
service = service == null ? EMPTY_STRING : service;
|
|
||||||
|
|
||||||
Entity usernameEntity = createUsernameEntity(service);
|
Entity usernameEntity = createUsernameEntity(service);
|
||||||
Entity passwordEntity = createPasswordEntity(service);
|
Entity passwordEntity = createPasswordEntity(service);
|
||||||
|
|
||||||
|
@ -77,9 +73,10 @@ public class CipherStorageFacebookConceal implements CipherStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeKey(String service) {
|
public void removeKey(@NonNull String service) {
|
||||||
// Facebook Conceal stores only one key across all services, so we cannot delete the key (otherwise decryption will fail for encrypted data of other services).
|
// Facebook Conceal stores only one key across all services, so we cannot delete the key (otherwise decryption will fail for encrypted data of other services).
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entity createUsernameEntity(String service) {
|
private Entity createUsernameEntity(String service) {
|
||||||
String prefix = getEntityPrefix(service);
|
String prefix = getEntityPrefix(service);
|
||||||
return Entity.create(prefix + "user");
|
return Entity.create(prefix + "user");
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.annotation.TargetApi;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.security.keystore.KeyGenParameterSpec;
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
import android.security.keystore.KeyProperties;
|
import android.security.keystore.KeyProperties;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.oblador.keychain.exceptions.CryptoFailedException;
|
import com.oblador.keychain.exceptions.CryptoFailedException;
|
||||||
import com.oblador.keychain.exceptions.KeyStoreAccessException;
|
import com.oblador.keychain.exceptions.KeyStoreAccessException;
|
||||||
|
@ -30,7 +31,7 @@ import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
||||||
public static final String CIPHER_STORAGE_NAME = "KeystoreAESCBC";
|
public static final String CIPHER_STORAGE_NAME = "KeystoreAESCBC";
|
||||||
public static final String DEFAULT_ALIAS = "RN_KEYCHAIN_DEFAULT_ALIAS";
|
public static final String DEFAULT_SERVICE = "RN_KEYCHAIN_DEFAULT_ALIAS";
|
||||||
public static final String KEYSTORE_TYPE = "AndroidKeyStore";
|
public static final String KEYSTORE_TYPE = "AndroidKeyStore";
|
||||||
public static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
|
public static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
|
||||||
public static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
|
public static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
|
||||||
|
@ -53,8 +54,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
@Override
|
@Override
|
||||||
public EncryptionResult encrypt(String service, String username, String password) throws CryptoFailedException {
|
public EncryptionResult encrypt(@NonNull String service, @NonNull String username, @NonNull String password) throws CryptoFailedException {
|
||||||
service = service == null ? DEFAULT_ALIAS : service;
|
service = getDefaultServiceIfEmpty(service);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = getKeyStoreAndLoad();
|
KeyStore keyStore = getKeyStoreAndLoad();
|
||||||
|
@ -91,8 +92,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptionResult decrypt(String service, byte[] username, byte[] password) throws CryptoFailedException {
|
public DecryptionResult decrypt(@NonNull String service, @NonNull byte[] username, @NonNull byte[] password) throws CryptoFailedException {
|
||||||
service = service == null ? DEFAULT_ALIAS : service;
|
service = getDefaultServiceIfEmpty(service);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = getKeyStoreAndLoad();
|
KeyStore keyStore = getKeyStoreAndLoad();
|
||||||
|
@ -111,8 +112,8 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeKey(String service) throws KeyStoreAccessException {
|
public void removeKey(@NonNull String service) throws KeyStoreAccessException {
|
||||||
service = service == null ? DEFAULT_ALIAS : service;
|
service = getDefaultServiceIfEmpty(service);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = getKeyStoreAndLoad();
|
KeyStore keyStore = getKeyStoreAndLoad();
|
||||||
|
@ -183,4 +184,9 @@ public class CipherStorageKeystoreAESCBC implements CipherStorage {
|
||||||
throw new KeyStoreAccessException("Could not access Keystore", e);
|
throw new KeyStoreAccessException("Could not access Keystore", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getDefaultServiceIfEmpty(@NonNull String service) {
|
||||||
|
return service.isEmpty() ? DEFAULT_SERVICE : service;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue