Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encrypt credentials in memory #2723

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
77934f1
Generate keys for KeyStore
avazirna Oct 9, 2023
b6cf455
Refactor
avazirna Oct 10, 2023
c137fab
Generate key during app initialization
avazirna Oct 10, 2023
6547246
Merge branch 'master' into encrypt-credentials-in-memory
avazirna Oct 10, 2023
8fdcbf6
Check is Android KeyStore is supported
avazirna Oct 10, 2023
9722643
Refactor
avazirna Oct 13, 2023
0996c07
Refactor
avazirna Oct 16, 2023
f1201a9
Set Android key store name
avazirna Oct 16, 2023
daf20a3
Refactor
avazirna Nov 1, 2023
9c54fe2
Refactor secret key generation method
avazirna Nov 1, 2023
d149226
Refactor key pair generation method
avazirna Nov 1, 2023
ebb96fd
Remove KeyStore reference from CommCare Platform
avazirna Nov 29, 2023
db3e33a
Refactor
avazirna Nov 30, 2023
5a45854
Add encryption key provider implementation
avazirna Nov 30, 2023
d328a05
Add service providers configuration
avazirna Nov 30, 2023
ecb938b
Refactor
avazirna Dec 5, 2023
d8f3b6c
Move KeyStore name to global constants
avazirna Dec 5, 2023
021f2d9
Refactor
avazirna Dec 5, 2023
7bfd5af
Add mock Key generator and key store
avazirna Dec 6, 2023
8c74193
Add unit test
avazirna Dec 6, 2023
4e137b6
Lint
avazirna Dec 6, 2023
275be76
Added cccStaging build flavor to support Connect production and stagi…
OrangeAndGreen Nov 1, 2023
ce84ea1
Merge branch 'master' into encrypt-credentials-in-memory
avazirna Dec 6, 2023
8609871
Merge branch 'master' into encrypt-credentials-in-memory
avazirna Dec 6, 2023
28493bf
Add gradle task to register service providers
avazirna Dec 7, 2023
6c5caf0
Refactor
avazirna Dec 7, 2023
efe1908
Lint
avazirna Dec 7, 2023
26f6e1f
Add comments to service provider properties
avazirna Dec 7, 2023
5c6f8fd
Rename key alias
avazirna Dec 13, 2023
8c15e6b
Revert "Add gradle task to register service providers"
avazirna Dec 13, 2023
6a805fc
Add key algorithms constants
avazirna Dec 14, 2023
8a33928
Lint
avazirna Dec 14, 2023
cd31c2b
Refactor EncryptionUtils to EncryptionHelper
avazirna Dec 20, 2023
8a97d37
Remove RSA option for encryption with encoded string
avazirna Jan 19, 2024
6edd01d
Add EncryptionKeyHelper
avazirna Jan 22, 2024
13ffe39
Refactor IEncryptionKeyProvider to support KeyStore Key generation only
avazirna Jan 22, 2024
ce30b99
Add key generation in KeyStore when alias is not found
avazirna Jan 22, 2024
ff2102d
Make EncryptionHelper stateless
avazirna Jan 22, 2024
1e9326c
Add EncryptionKeyException to umbrella exceptions related to encrypti…
avazirna Jan 22, 2024
b1af601
Rename IEncrypitonKeyProvider to IKeyStoreEncryptionKeyProvider
avazirna Jan 23, 2024
7d66b9f
Add TestKeyStoreEncryptionProvider
avazirna Jan 23, 2024
48aa5a5
Refactor
avazirna Jan 23, 2024
af8194a
Lint
avazirna Jan 23, 2024
db7ef66
Bubble up encryption key exceptions
avazirna Jan 23, 2024
403fff4
Lint
avazirna Jan 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ app/libs/javarosa-libraries.jar
build/
**/*~
app/google-services.json
app/fabric.properties
app/fabric.properties
app/src/META-INF/
72 changes: 47 additions & 25 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -136,42 +136,50 @@ dependencies {
}

ext {
// Obtained from ~/.gradle/gradle.properties on build server (mobile agent), or your local
// Obtained from ~/.gradle/gradle.properties on build server (mobile agent), or your local
// ~/.gradle/gradle.properties file, or loads default empty strings if neither is present
MAPBOX_SDK_API_KEY = project.properties['MAPBOX_SDK_API_KEY'] ?: ""
ANALYTICS_TRACKING_ID_DEV = project.properties['ANALYTICS_TRACKING_ID_DEV'] ?: ""
ANALYTICS_TRACKING_ID_LIVE = project.properties['ANALYTICS_TRACKING_ID_LIVE'] ?: ""
GOOGLE_PLAY_MAPS_API_KEY = project.properties['GOOGLE_PLAY_MAPS_API_KEY'] ?: ""
RELEASE_STORE_FILE = project.properties['RELEASE_STORE_FILE'] ?: "."
RELEASE_STORE_PASSWORD = project.properties['RELEASE_STORE_PASSWORD'] ?: ""
RELEASE_KEY_ALIAS = project.properties['RELEASE_KEY_ALIAS'] ?: ""
RELEASE_KEY_PASSWORD = project.properties['RELEASE_KEY_PASSWORD'] ?: ""
MAPBOX_SDK_API_KEY = project.properties['MAPBOX_SDK_API_KEY'] ?: ''
ANALYTICS_TRACKING_ID_DEV = project.properties['ANALYTICS_TRACKING_ID_DEV'] ?: ''
ANALYTICS_TRACKING_ID_LIVE = project.properties['ANALYTICS_TRACKING_ID_LIVE'] ?: ''
GOOGLE_PLAY_MAPS_API_KEY = project.properties['GOOGLE_PLAY_MAPS_API_KEY'] ?: ''
RELEASE_STORE_FILE = project.properties['RELEASE_STORE_FILE'] ?: '.'
RELEASE_STORE_PASSWORD = project.properties['RELEASE_STORE_PASSWORD'] ?: ''
RELEASE_KEY_ALIAS = project.properties['RELEASE_KEY_ALIAS'] ?: ''
RELEASE_KEY_PASSWORD = project.properties['RELEASE_KEY_PASSWORD'] ?: ''
TRUSTED_SOURCE_PUBLIC_KEY = project.properties['TRUSTED_SOURCE_PUBLIC_KEY'] ?:
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHiuy2ULV4pobkuQN2TEjmR1tn" +
"HJ+F335hm/lVdaFQzvBmeq64MUMbumheVLDJaSUiAVzqSHDKJWH01ZQRowqBYjwo" +
"ycVSQSeO2glc6XZZ+CJudAPXe8iFWLQp3kBBnBmVcBXCOQFO7aLgQMv4nqKZsLW0" +
"HaAJkjpnc165Os+aYwIDAQAB"
GOOGLE_SERVICES_API_KEY = project.properties['GOOGLE_SERVICES_API_KEY'] ?: ""
QA_BETA_APP_ID = ""
STANDALONE_APP_ID = ""
LTS_APP_ID = ""
COMMCARE_APP_ID = ""
HQ_API_USERNAME = project.properties['HQ_API_USERNAME'] ?: ""
HQ_API_PASSWORD = project.properties['HQ_API_PASSWORD'] ?: ""
TEST_BUILD_TYPE = project.properties['TEST_BUILD_TYPE'] ?: "debug"
FIREBASE_DATABASE_URL = project.properties['FIREBASE_DATABASE_URL'] ?: ""
GOOGLE_SERVICES_API_KEY = project.properties['GOOGLE_SERVICES_API_KEY'] ?: ''
QA_BETA_APP_ID = ''
STANDALONE_APP_ID = ''
LTS_APP_ID = ''
COMMCARE_APP_ID = ''
HQ_API_USERNAME = project.properties['HQ_API_USERNAME'] ?: ''
HQ_API_PASSWORD = project.properties['HQ_API_PASSWORD'] ?: ''
TEST_BUILD_TYPE = project.properties['TEST_BUILD_TYPE'] ?: 'debug'
FIREBASE_DATABASE_URL = project.properties['FIREBASE_DATABASE_URL'] ?: ''

// properties related to Service providers part of the Java SPI pattern
SERVICE_PROVIDERS_REL_DIR = 'META-INF/services'
/**
* Service provider implementations and respective service interfaces should be added to this
* map. The task registerServiceProviders is responsible for iterating over it and create the
* configuration files under META-INF/services
*/
SERVICE_PROVIDERS = ['org.commcare.util.IEncryptionKeyProvider' : 'org.commcare.utils.EncryptionKeyProvider']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning behind specifying it here instead of Java - registerServiceKeyProvider(new EncryptionKeyProvider() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shubham1g5 this is just to create the static configuration files, the instantiation is done by the JRE using the ServiceLoader. The reasoning here is to decouple the service interface from its implementations, more concretely, it removes any dependency between commcare-android and commcare-core when it comes to IEncryptionKeyProvider implementations.

}

afterEvaluate {
// Hack to get assets to show up in robolectric tests; try to eventually remove this
preCommcareDebugUnitTestBuild.dependsOn mergeCommcareDebugAssets
processStandaloneDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
processStandaloneReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
processLtsDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
processLtsReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
processCommcareDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
processCommcareReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile
preCommcareDebugUnitTestBuild.dependsOn mergeCommcareDebugAssets, registerServiceProviders
processStandaloneDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
processStandaloneReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
processLtsDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
processLtsReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
processCommcareDebugGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
processCommcareReleaseGoogleServices.dependsOn injectPropertiesIntoFirebaseConfigFile, registerServiceProviders
}

/**
Expand Down Expand Up @@ -632,3 +640,17 @@ downloadLicenses {
includeProjectDependencies = true
dependencyConfiguration = 'compile'
}

task registerServiceProviders {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add it at build time instead of just having a static file ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shubham1g5 that was the initial approach but I thought about making it easy for future additions and also to put this on others radar, a static file can very easily be overlooked. So, not necessarily strong reasons.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I would still prefer a static file if we go with service provider approach as this adds an unnecessary build step without any strong advantages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted

doLast {
def servProvAbsPath = android.sourceSets.main.java.srcDirs[0].path + File.separator + project.ext.SERVICE_PROVIDERS_REL_DIR
project.ext.SERVICE_PROVIDERS.each { servProv ->
println(servProvAbsPath + File.separator + "$servProv.key")
def file = new File(servProvAbsPath + File.separator + "$servProv.key")
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs()
}
file.write("$servProv.value")
}
}
}
5 changes: 5 additions & 0 deletions app/src/org/commcare/CommCareApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@
import okhttp3.MultipartBody;
import okhttp3.RequestBody;

import static org.commcare.util.EncryptionUtils.USER_CREDENTIALS_KEY_ALIAS;
import static org.commcare.util.EncryptionUtils.getEncryptionKeyProvider;

public class CommCareApplication extends MultiDexApplication {

private static final String TAG = CommCareApplication.class.getSimpleName();
Expand Down Expand Up @@ -228,6 +231,8 @@ public void onCreate() {
setRoots();
prepareTemporaryStorage();

getEncryptionKeyProvider().generateCryptographicKeyInKeyStore(USER_CREDENTIALS_KEY_ALIAS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this key is not user specific, can we call it something general like cc_in_memory_encryption_key ?


if (LegacyInstallUtils.checkForLegacyInstall(this)) {
dbState = STATE_LEGACY_DETECTED;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@

import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
* This is a record of a key that CommCare ODK has shared with another app
Expand Down Expand Up @@ -52,18 +49,10 @@ public AndroidSharedKeyRecord(String keyId, byte[] privateKey, byte[] publicKey)
}

public static AndroidSharedKeyRecord generateNewSharingKey() {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(512, new SecureRandom());
KeyPair pair = generator.genKeyPair();
byte[] encodedPrivate = pair.getPrivate().getEncoded();
String privateEncoding = pair.getPrivate().getFormat();
byte[] encodedPublic = pair.getPublic().getEncoded();
String publicencoding = pair.getPublic().getFormat();
return new AndroidSharedKeyRecord(PropertyUtils.genUUID(), pair.getPrivate().getEncoded(), pair.getPublic().getEncoded());
} catch (NoSuchAlgorithmException nsae) {
return null;
}
KeyPair pair = CryptUtil.generateRandomKeyPair(512);
byte[] encodedPrivate = pair.getPrivate().getEncoded();
byte[] encodedPublic = pair.getPublic().getEncoded();
return new AndroidSharedKeyRecord(PropertyUtils.genUUID(), encodedPrivate, encodedPublic);
}

private String getKeyId() {
Expand Down
6 changes: 2 additions & 4 deletions app/src/org/commcare/android/nfc/NfcManager.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package org.commcare.android.nfc;

import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.os.Build;

import org.apache.commons.lang3.StringUtils;
import org.commcare.util.EncryptionUtils;
Expand Down Expand Up @@ -60,7 +58,7 @@ public String decryptValue(String message) throws EncryptionUtils.EncryptionExce
if (message.startsWith(payloadTag)) {
message = message.replace(payloadTag, "");
if (!StringUtils.isEmpty(encryptionKey)) {
message = EncryptionUtils.decrypt(message, encryptionKey);
message = EncryptionUtils.decryptWithBase64EncodedKey("AES", message, encryptionKey);
}
} else if (!allowUntaggedRead && !isEmptyPayloadTag(payloadTag)) {
throw new InvalidPayloadTagException();
Expand Down Expand Up @@ -97,7 +95,7 @@ public String tagAndEncryptPayload(String message) throws EncryptionUtils.Encryp
}
String payload = message;
if (!StringUtils.isEmpty(encryptionKey)) {
payload = EncryptionUtils.encrypt(payload, encryptionKey);
payload = EncryptionUtils.encryptWithBase64EncodedKey("AES", payload, encryptionKey);
}
if (payload.contains(PAYLOAD_DELIMITER)) {
throw new InvalidPayloadException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private void createAndWriteKeyRecordAndUser() {
int userCount = keyRecordDB.getIDsForValue(UserKeyRecord.META_USERNAME, username).size();

if (userCount == 0) {
SecretKey secretKey = CryptUtil.generateSemiRandomKey();
SecretKey secretKey = CryptUtil.generateRandomSecretKey();
if (secretKey == null) {
throw new RuntimeException("Error setting up user's encrypted storage");
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/org/commcare/tasks/DataPullTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private byte[] getEncryptionKey() {

private void initUKRForLogin() {
if (blockRemoteKeyManagement || shouldGenerateFirstKey()) {
SecretKey newKey = CryptUtil.generateSemiRandomKey();
SecretKey newKey = CryptUtil.generateRandomSecretKey();
if (newKey == null) {
return;
}
Expand Down
163 changes: 163 additions & 0 deletions app/src/org/commcare/utils/EncryptionKeyProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package org.commcare.utils;

import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;

import org.commcare.CommCareApplication;
import org.commcare.util.EncryptionKeyAndTransformation;
import org.commcare.util.EncryptionUtils;
import org.commcare.util.IEncryptionKeyProvider;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.GregorianCalendar;

import javax.crypto.KeyGenerator;
import javax.security.auth.x500.X500Principal;

import androidx.annotation.RequiresApi;

import static org.commcare.utils.GlobalConstants.KEYSTORE_NAME;

/**
* Class for providing encryption keys backed by Android Keystore
*
* @author dviggiano
*/
public class EncryptionKeyProvider implements IEncryptionKeyProvider {

@RequiresApi(api = Build.VERSION_CODES.M)
private static final String ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
@RequiresApi(api = Build.VERSION_CODES.M)
private static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM;
@RequiresApi(api = Build.VERSION_CODES.M)
private static final String PADDING = KeyProperties.ENCRYPTION_PADDING_NONE;
private static KeyStore keystoreSingleton = null;

private static KeyStore getKeyStore() throws KeyStoreException, CertificateException,
IOException, NoSuchAlgorithmException {
if (keystoreSingleton == null) {
keystoreSingleton = KeyStore.getInstance(KEYSTORE_NAME);
keystoreSingleton.load(null);
}
return keystoreSingleton;
}

@Override
public EncryptionKeyAndTransformation retrieveKeyFromKeyStore(String keyAlias,
EncryptionUtils.CryptographicOperation operation)
throws KeyStoreException, UnrecoverableEntryException, NoSuchAlgorithmException,
CertificateException, IOException {
Key key;
if (getKeyStore().containsAlias(keyAlias)) {
KeyStore.Entry keyEntry = getKeyStore().getEntry(keyAlias, null);
if (keyEntry instanceof KeyStore.PrivateKeyEntry) {
if (operation == EncryptionUtils.CryptographicOperation.Encryption) {
key = ((KeyStore.PrivateKeyEntry)keyEntry).getCertificate().getPublicKey();
} else {
key = ((KeyStore.PrivateKeyEntry)keyEntry).getPrivateKey();
}
} else {
key = ((KeyStore.SecretKeyEntry)keyEntry).getSecretKey();
}
} else {
throw new KeyStoreException("Key not found in KeyStore");
}
if (key != null) {
return new EncryptionKeyAndTransformation(key, getTransformationString(key.getAlgorithm()));
} else {
return null;
}
}

// Generates a cryptrographic key and adds it to the Android KeyStore
public void generateCryptographicKeyInKeyStore(String keyAlias) {
if (isKeyStoreAvailable()) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyGenerator keyGenerator = KeyGenerator
.getInstance(getAESKeyAlgorithmRepresentation(), KEYSTORE_NAME);
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(PADDING)
.build();
keyGenerator.init(keyGenParameterSpec);
keyGenerator.generateKey();
} else {
// Because KeyGenParameterSpec was introduced in Android SDK 23, prior versions
// need to resource to KeyPairGenerator which only generates asymmetric keys,
// hence the need to switch to a correspondent algorithm as well, RSA
// TODO: Add link to StackOverflow page
KeyPairGenerator keyGenerator = KeyPairGenerator
.getInstance(getRSAKeyAlgorithmRepresentation(), KEYSTORE_NAME);
GregorianCalendar start = new GregorianCalendar();
GregorianCalendar end = new GregorianCalendar();
end.add(GregorianCalendar.YEAR, 100);

KeyPairGeneratorSpec keySpec = new KeyPairGeneratorSpec.Builder(CommCareApplication.instance())
// Key alias to be used to retrieve it from the KeyStore
.setAlias(keyAlias)
// The subject used for the self-signed certificate of the generated pair
.setSubject(new X500Principal(String.format("CN=%s", keyAlias)))
// The serial number used for the self-signed certificate of the
// generated pair
.setSerialNumber(BigInteger.valueOf(1337))
// Date range of validity for the generated pair
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();

keyGenerator.initialize(keySpec);
keyGenerator.generateKeyPair();
}

} catch (NoSuchAlgorithmException | NoSuchProviderException |
InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException("KeyStore not available");
}
}

@Override
public boolean isKeyStoreAvailable() {
return Security.getProvider(KEYSTORE_NAME) != null;
}

@Override
public String getAESKeyAlgorithmRepresentation() {
return ALGORITHM;
}

@Override
public String getRSAKeyAlgorithmRepresentation() {
return "RSA";
}

@Override
public String getTransformationString(String algorithm) {
String transformation = null;
if (algorithm.equals(getRSAKeyAlgorithmRepresentation())) {
transformation = "RSA/ECB/PKCS1Padding";
} else if (algorithm.equals(getAESKeyAlgorithmRepresentation())) {
transformation = String.format("%s/%s/%s", algorithm, BLOCK_MODE, PADDING);
}
// This will cause an error if null
return transformation;
}

}
1 change: 0 additions & 1 deletion app/src/org/commcare/utils/EncryptionUtils.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.commcare.utils;

import org.commcare.util.Base64;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

Expand Down
2 changes: 2 additions & 0 deletions app/src/org/commcare/utils/GlobalConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ public class GlobalConstants {
public static final String TRUSTED_SOURCE_PUBLIC_KEY = BuildConfig.TRUSTED_SOURCE_PUBLIC_KEY;

public static final String SMS_INSTALL_KEY_STRING = "[commcare app - do not delete]";

public static final String KEYSTORE_NAME = "AndroidKeyStore";
}
2 changes: 1 addition & 1 deletion app/src/org/commcare/utils/TemplatePrinterUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
public abstract class TemplatePrinterUtils {

private static final String FORMAT_REGEX_WITH_DELIMITER = "((?<=%2$s)|(?=%1$s))";
private static final SecretKey KEY = CryptUtil.generateSemiRandomKey();
private static final SecretKey KEY = CryptUtil.generateRandomSecretKey();

/**
* Concatenate all Strings in a String array to one String.
Expand Down
Loading