How Do I Protect Decryption Password And Salt in APK - android

I am creating a product whose firmware is updated using an android phone. The android application automatically downloads an encrypted version of the firmware, decrypts it, and sends it to the devices boot-loader. In order to generate the same secret key I specificy the password and salt in the code. I'm worried the apk will be decompiled and someone will be able to decrypt our firmware.
Is there a better way to decrypt/encrypt files or protect the code?
Code:
private byte[] DecryptFile(byte[] encryptedFileBuffer) {
final int iterationCount = 10;
byte[] dataDecrypted = null;
SecretKey secKey = null;
try {
byte[] salt = "salt1234".getBytes();
String accessThingy = "Password";
KeySpec keySpec = new PBEKeySpec(accessThingy.toCharArray(), salt, iterationCount);
secKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
Cipher desCipher;
// Create the cipher
desCipher = Cipher.getInstance(secKey.getAlgorithm());
desCipher.init(Cipher.DECRYPT_MODE, secKey,paramSpec);
dataDecrypted = desCipher.doFinal(encrptedFileBuffer);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return dataDecrypted;
}

Yes and no.
No, if the decryption routine can be executed by an attacker (and why wouldn't it be) then the firmware would be compromised. The only way to avoid this is to add protection to the key on the device. You can think of OS/hardware support for this, or about storing the key/password outside of the device for instance. But a single compromised device would leak the firmware. This is the DRM conundrum.
And yes as you seem to use PBKDF1, MD5 and DES, none of which is particularly safe. MD5 is the most broken algorithm in that list, but it is the one that is least likely to actually become a problem. You should be using PBKDF2, SHA-2 and AES instead. Try this answer, Java 8 also has added support for PBKDF2 with SHA-2. Or you could actually use a fully random key instead of using password based encryption (PBE).
You may also want to consider asymmetric primitives (ECDSA/RSA) for encryption and code signing.

Would it be possible to move the decryption to the device itself? This way the code would be less accessible to the end user assuming that there wasn't any way to read back the program from the device (which would also cause an issue here).

Related

PLCCrypto Encryption Hanging

I'm playing around with adding some encryption to a Xamarin Forms app.
The project is targeting .Net standard 2.0, building for Android 11.0 - API30.
I'm using the PCLCrypto 2.1.40-Aplha nuget package and attempting to encrypt with the following code:
public static void AES_Encrypt(byte[] data, byte[] keyMaterial)
{
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var key = provider.CreateSymmetricKey(keyMaterial);
// The IV may be null, but supplying a random IV increases security.
// The IV is not a secret like the key is.
// You can transmit the IV (w/o encryption) alongside the ciphertext.
var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength);
byte[] cipherText = WinRTCrypto.CryptographicEngine.Encrypt(key, data, iv);
// When decrypting, use the same IV that was passed to encrypt.
//byte[] plainText = WinRTCrypto.CryptographicEngine.Decrypt(key, cipherText, iv);
}
Where both data and key-material are utf8 bytes of a string (so not-null terminated in the array).
My issue is the call to WinRTCrypto.CryptographicEngine.Encrypt is hanging and causing the app to hang.
Am I missing something with these calls ?
Edit 2 - This is occuring on every build version of PLCCrypto I have attempted so doesnt seem to be an alpha issue.

How do I require user authentication only for decryption but not encryption

I have a public/private keypair in AndroidKeyStore which I generated as follows:
val spec = KeyGenParameterSpec.Builder(alias(username), KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT)
.setKeySize(keySize)
.setUserAuthenticationRequired(true)
.setBlockModes(ablockMode)
.setEncryptionPaddings(apaddingMode)
.setCertificateSubject(X500Principal("CN=Itsami Mario, OU=Adventure Unit, O=Plumber Bros, C=US"))
.setKeyValidityStart(Date())
.setKeyValidityEnd(Date(Date().time + 1000 * 60 * 60 * 24 * 7))
.setCertificateSerialNumber(BigInteger(64, SecureRandom()))
.setDigests(digest)
.build()
keyPairGen.initialize(spec)
return keyPairGen.genKeyPair()
I want to require biometric authentication every time the private key is used, but I don't want to require a biometric prompt when encrypting with the public key. However, when use I use setUserAuthenticationRequired(true) in the KeyGeneratior and then I try to encrypt without first showing the BiometricPrompt, I get an android.security.KeyStoreException with the message: Key user not authenticated
How can I require authentication for decryption but not encryption?
You must be testing on a device running Android 6, Marshmallow. This is a known issue in that release, which was fixed in Android 7.
To work around the problem, you can extract the encoding of the public key and create a new PublicKey object from it, like so:
PublicKey publicKey = keyPair.getPublicKey();
PublicKey unrestrictedPublicKey =
KeyFactory.getInstance(publicKey.getAlgorithm()).generatePublic(
new X509EncodedKeySpec(publicKey.getEncoded()));
This will work on all versions.
Note that it's also possible to create AES keys that require authentication when decrypting but not when encrypting, which is rather cool (AES is much, much faster than RSA). The trick is to generate the key outside of AndroidKeyStore and then import it twice, once with PURPOSE_ENCRYPT and once with PURPOSE_DECRYPT, under two different aliases, and specifying user authentication requirements on the DECRYPT version. Something like:
// Note that we do *not* specify "AndroidKeyStore" when we call getInstance()
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
// This time we do specify "AndroidKeyStore".
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
// Now we import the encryption key, with no authentication requirements.
keyStore.setEntry(
"encrypt_key",
new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockMode(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
// And the decryption key, this time requiring user authentication.
keyStore.setEntry(
"decrypt_key",
new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockMode(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthentication(true)
.build());
Now, you can encrypt with the key alias "encrypt_key" at any time, no user authentication required, and you can decrypt with the key alias "decrypt_key", but only when you do the BiometricPrompt thing.
The downside of this is that the secret exists briefly in non-secure memory. In practice, this matters only if an attacker has already compromised the device when the key is created, and in that case you have most likely already lost.

How to employ keyed-hash message authentication code (HMAC) with Android Keystore

I am investigating the use of the Android KeyStore for Marshmallow and above.
I would like to simultaneously verify both the data integrity and the authentication of my data by employing HMAC's.
How do I go about achieving this?
I am current generating an Encrypt/Decrypt key as follows:-
mKeyStore = KeyStore.getInstance(keyStoreName);
mKeyStore.load(mKeyStoreLoadStoreParameter);
if (mKeyStore.containsAlias(keyStoreAlias)) {
mSecretKey = (SecretKey) mKeyStore.getKey(keyStoreAlias, KEY_STORE_PASSWORD);
} else {
final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, keyStoreName);
final int keyPurpose = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT;
keyGenerator.init(
new KeyGenParameterSpec.Builder(keyStoreAlias, keyPurpose)
.setKeySize(KEY_STORE_KEY_SIZE)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setRandomizedEncryptionRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
mSecretKey = keyGenerator.generateKey();
I have found this sample for generating HMAC's
SecretKey key = ...; // HMAC key of algorithm "HmacSHA512".
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setEntry(
"key1",
new KeyStore.SecretKeyEntry(key),
new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build());
// Key imported, obtain a reference to it.
SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
// The original key can now be discarded.
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(keyStoreKey);
However, how do I use this when encrypting/decrypting my data?
EXPLANATION
I have a number of choices/decisions to make when implementing security/cryptography within any Android application.
1). Do I implement cryptography of any sort Yes or No?
2). If Yes then... I should attempt to achieve the "most" secure solution possible.
If I am going to employ cryptography then I need to ensure the following.
a). I store passwords/secret keys in a "Safe Place" e.g. Android Key Store.
b). I use the "strongest" cryptography available.
c). I would like to simultaneously verify both the data integrity and the authentication of my data, e.g. I would like to detect if my encrypted data has been tampered with.
As I understand what I have read about HMAC's, they provide this functionality. I would like to know how I code the use of HMAC's into my Android application to ensure both the data integrity and the authentication of my data.
You can apply HMAC to the plain text HMAC(plain text) before encrypting and recompute the HMAC after decrypting to check that the original message is the same.
It may be redundant because if the cipher text is altered you will not be able to decrypt it.
First generate a HMAC key inside AndroidKeyStore. I found an example here
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
keyGenerator.initialize(
new KeyGenParameterSpec.Builder(hmacKeyAlias, KeyProperties.PURPOSE_SIGN).build());
SecretKey key = keyGenerator.generateKey();
Then Apply HMAC to the original data and store the result somewhere
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
byte hmacOriginalData[] = mac.doFinal(dataToEncrypt);
//Store hmacOriginalData
After decrypting, get HMAC key from AndroidKeyStore, recompute HMAC and check both macs are equal
Key key = keyStore.getKey(hmacKeyAlias, null);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
byte hmacDecryptedData[] = mac.doFinal(decryptedData);
//Check equals(hmacDecryptedData, hmacOriginalData);

SecureRandom provider "Crypto" unavailable in Android N for deterministially generating a key

Users can purchase a "Pro" version of my app. When they do, I store and verify their purchase as follows.
Combine the user's UUID and another unique string.
The resulting string is then encrypted using a static seed. I do this using SecureRandom.getInstance("SHA1PRNG", "Crypto")- This is the problem!
The resulting encrypted string is then the "unlock code".
Therefore, I always know the expected unique unlock code value for the user.
When the user purchases "Pro", I store the "unlock code" in the database.
I check to see whether the user has "Pro" by seeing if the stored "unlock code" in the database matches the expected code based on their unique info.
So, not the best system, but everything is obfuscated enough for my humble app.
The problem is that SecureRandom.getInstance("SHA1PRNG", "Crypto") fails on N because "Crypto" is not supported. I have learned that relying on specific providers is bad practice and Crypto is not supported on N. Oops.
So I have a problem: I rely on the encryption of a value-seed pair to always have the same output. Android N does not support the encryption provider I use, so I don't know how to ensure that the encryption output will be the same on N as it is on other devices.
My questions:
Is it possible to include "Crypto" in my APK so that it is always available?
Can I otherwise ensure the same output when encrypting a value-seed pair on Android N?
My code:
public static String encrypt(String seed, String cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes(), seed);
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result); // "unlock code" which must always be the same for the same seed and clearText accross android versions
}
private static byte[] getRawKey(byte[] seed, String seedStr) throws Exception {
SecureRandom sr;
sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); // what used to work
KeyGenerator kgen = KeyGenerator.getInstance("AES");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
I had a discussion with the Android Security team about this recently.
In Android N, SHA1PRNG was removed because we don't have a secure implementation of it. Specifically, calling .setSeed(long) before requesting output from the PRNG replaces all of the entropy in the SecureRandom instance.
This behavior has long been pointed to as a security failure (read: frequently causes subtle bugs in apps), so we chose not to replicate it when the SecureRandom provider was replaced.
If you need a PRNG, then just use new SecureRandom().
That said... SecureRandom() is not designed to be used as a key derivation function, as you've done in your example. Please don't do this! Instead, use an algorithm such as PBKDF2, available via SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").
We've been warning developers about this for a while. Please see these posts:
Android Developers Blog: Using Cryptography to Store Credentials Safely
Android 4.2 broke my encrypt/decrypt code and the provided solutions don't work
IF YOU REALLY NEED SHA1PRNG, EVEN AFTER ALL OF THAT... then the workaround is to copy the implementation out of the Android source, like #artjom-b mentioned in his answer.
But please, only do this if you need compatibility while migrating to PBKDF2 or similar.
Using a PRNG such as SecureRandom for deriving data deterministically is generally a bad idea, because there is a history of breaking changes. It is always a good idea to use a specific implementation and include that with your app. It is possible to just copy the implementation code in your case.
SecureRandom.getInstance("SHA1PRNG", "Crypto"); looks up the "Crypto" provider which is org.apache.harmony.security.provider.crypto.CryptoProvider in Android 5.1.1. It redirects to org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl as the actual implementation. You can easily copy the code into your project under a different package and be sure to comply with the code license.
Then you can use it like this:
sr = new SecureRandom(new your.pkg.SHA1PRNG_SecureRandomImpl(), null);
The second provider argument is not used according to the code, but you can create a dummy provider.
The proper way to generate a key from some seed is to use a key derivation function (KDF). If seed is password-like, then PBKDF2 is a good KDF when a lot of iterations are specified. If seed is key-like, then a KBKDF like HKDF is recommended.
I added one class for CryptoProvider you can replace SecureRandom.getInstance("SHA1PRNG", "Crypto"); to SecureRandom.getInstance("SHA1PRNG", new CryptoProvider());
you can refer following link for solution, it working for me;
Security "Crypto" provider deprecated in Android N

Android create RSA 1024 .NET compatible keys

I am developing an Android Application and I need to generate some RSA private and public keys to use for secure communication with web services. To do this I need to have the public key in a .NET compatible form.
Like:
<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>
So far I managed to to this:
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
keypair = keyGen.genKeyPair();
privateKey = keypair.getPrivate();
publicKey = keypair.getPublic();
// Get the bytes of the public and private keys
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
I've got no clue how to continue. Could you please provide some help ?
For anybody else interested, a very good tutorial can be found in here
http://www.codeproject.com/KB/security/porting_java_public_key.aspx?msg=3407475
If you need Base64 encoding/decoding, because it's not included in Android (at least in API 4) you could use the class from here: iharder.sourceforge.net/current/java/base64/
You don't show the type publicKey. If is not already, you should cast to an RSAPublicKey, then use the getPublicExponent() and getModulus() methods to extract the BigInteger. Then simply use standard Java IO, e.q. PrintStream.println() or printf() to generate the XML components.

Categories

Resources