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.
Related
Attempting to use SpongyCastle to provide the preferred encryption algorithm of RSA/ECB/OAEPwithSHA-512andMGF1Padding for Asymmetric encryption/decryption tasks on all supported Android device versions and having issues.
Encryption appears to be working fine.
But decryption is proving some trouble:
No provider for RSA/ECB/OAEPwithSHA-512andMGF1Padding
KeyGen spec is as follows:
val generatorSpec = KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(X500Principal(ASYMMETRIC_KEY_COMMON_NAME_PREFIX + ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(creationDate.time)
.setEndDate(expiryDate.time)
.build()
val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore")
keyPairGenerator.initialize(generatorSpec)
keyPairGenerator.generateKeyPair()
I'm now grabbing this value from the keyStore and trying to use it for decryption/encryption:
private fun rsaEncrypt(data: ByteArray, key: KeyStore.PrivateKeyEntry): ByteArray {
val encryptionCipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-512andMGF1Padding", "SC")
encryptionCipher.init(Cipher.ENCRYPT_MODE, key.certificate.publicKey)
val outputStream = ByteArrayOutputStream()
val cipherOutputStream = CipherOutputStream(outputStream as OutputStream, encryptionCipher)
cipherOutputStream.write(data)
cipherOutputStream.close()
return outputStream.toByteArray()
}
This appears to work fine, however decryption is where my issue lies:
private fun rsaDecrypt(data: ByteArray, key: KeyStore.PrivateKeyEntry): ByteArray {
val decryptionCipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-512andMGF1Padding", "SC")
decryptionCipher.init(Cipher.DECRYPT_MODE, key.privateKey)
// Rest of code for cipher streaming etc. etc.
}
initialising the decryptionCipher is giving me:
java.security.ProviderException: No provider for RSA/ECB/OAEPwithSHA-512andMGF1Padding
Which is strange due to my cipher instance returning fine and encryption working fine.
Also tried specifying the provider as “BC” rather than “SC” which gives a private exponent cannot be extracted error which I’m thinking is by design.
Trying to give a algorithm that isn't supported will break on the cipher initialisation and Encryption via Provider SC doesn't provide xxx so what gives?
TLDR: The encryption cipher has the same provider as decryption. But only decryption breaks....
There has to be something I’m missing here but can’t seem to put my finger on it. I've been working on this a while so any help is appreciated!
Edit: For interest I'm providing SpongyCastle through:
init {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(BouncyCastleProvider())
keyStore.load(null)
}
You can not decrypt with BC/SC using a key managed by AndroidKeyStore because the private key content is protected and its parameters (such as private exponent) are hidden, so any attempt to initialize a cipher with that key will fail.
The error message No provider for RSA/ECB/OAEPwithSHA-512andMGF1Padding using SC is probably due to an incorrect error handling by the library, but the private exponent cannot be extracted error for BC is clear. Encryption works because it uses the public key, which is not protected.
You need to use AndroidKeyStore for decryption (or use SC/BC also to generate the keys).
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);
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
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).
I have a C application, within this application I generate a RSA key pair with the following code (error checking left due to readability):
void generateKeyPair(char* pass) {
EVP_PKEY *pkey = NULL;
RSA* r;
OpenSSL_add_all_algorithms();
RAND_load_file("/dev/urandom", 1024);
r = RSA_generate_key(KEY_LENGTH, RSA_F4, NULL, NULL);
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, r);
FILE* fp = fopen("private.key", "w");
PEM_write_PrivateKey(fp, pkey, EVP_aes_256_cbc(), NULL, 0, NULL, pass);
fclose(fp);
fp = fopen("public.key", "w");
PEM_write_PUBKEY(fp, pkey);
fclose(fp);
}
I synchronize the public PEM keys through a server between the devices. Now I have to write a compatible Android application though I have to generate the public key in the same format then PEM_write_PUBKEY does.
I know that I should have done the synchronisation in DER format or something but now I can't change the design any more. There is no way around generating the keys in the similar format.
I guess that it is a #PCKS1 base64 encoded key but I am not sure and I don't know how to generate a similar one in Android - Androids standard is #PCKS8. However I would prefer not to include spongy castle in my project if there is a way around.
You are in luck; it seems you only have to PEM encode your public key as both Java and OpenSSL use the same SubjectPublicKey structure used for X5.09 certificates. You can get to this by running RSAPublicKey.encode().
Unfortunately I don't know any other library that performs PEM encoding, but I'm sure you can strip out the code from Bouncy or Spongy if required; the Bouncy Castle libraries have a very liberal licensing structure.