Spongy Castle RSA Encryption/Decryption with Android Keystore - android

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).

Related

How to configure Android RSA key generation (or key use) so that it works like Delphi TurboPower Lockbox 2 RSA key generation/use?

I have the following code for Delphi 10.2 TurboPower LockBox 2 RSA keys generation and their represenation as some string:
//Object properties
object LbRSA1024: TLbRSA
PrimeTestIterations = 20
KeySize = aks1024
Left = 416
Top = 248
end
//Key generation - so simple!
LbRSA1024.GenerateKeyPair;
//Getting generated key as string
function TMainForm.GetPublicKey1024AsString: string;
var
str1, str2: TStringStream;
begin
Result:='';
if (LbRSA1024.PublicKey.Exponent.Int.dwUsed = 0)
or (LbRSA1024.PublicKey.Modulus.Int.dwUsed = 0) then
exit;
str1:= TStringStream.Create('');
str2:= TStringStream.Create('');
try
LbRSA1024.PublicKey.StoreToStream(str1);
str1.Position:=0;
//LbEncodeBase64(str1,str2);
TLbBase64.LbEncodeBase64(str1,str2);
Result:=str2.DataString;
finally
str1.Free;
str2.Free;
end;
end;
I got public key as ~200 character string (e.g. ...dh2dMTy/ab...) and I assigned his string to Android kotlin variable publicKeyString and tried to encrypt some other string with this public key generated from Delphi. Android Kotlin code is:
val publicKeyBytes: ByteArray = Base64.decode(publicKeyString, Base64.DEFAULT)
val X509PublicKey: X509EncodedKeySpec = X509EncodedKeySpec(publicKeyBytes)
val kf: KeyFactory = KeyFactory.getInstance("RSA")
val publicKey: PublicKey = kf.generatePublic(X509PublicKey)
val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val bytes = cipher.doFinal(s.toByteArray())
val result: String = String(bytes, Charsets.UTF_8)
Generally it is not working - the key is not accepted and the error messages are (I experimented with some variations of the code above):
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic (:-1)
...
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic (:-1)
at java.security.KeyFactory.generatePublic (:-1)
So, it may be possible that Delphi LockBox and Android/javax RSA has different key formats. So - I tried to do 2 things. First - I checked the code for Delphi key generation - specificaly - LbRsa.pas class procedure TRSA.GenerateRSAKeysEx(var PrivateKey, PublicKey : TLbRSAKey; KeySize : TLbAsymKeySize; PrimeTestIterations : Byte; Callback : TLbRSACallback); But this code is completely generic - large integers are generated and stored as the member variables and the then streamed to the strings with the code I have already provided above.
Then I tried to generate RSA keys in Android/javax and check whether they are the same as LockBox generated keys. I used the following code:
val REG_KEY: String = "REG_KEY"
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA /*, ANDROID_KEYSTORE */)
val builder = KeyGenParameterSpec.Builder(REG_KEY,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setKeySize(1024)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
//.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
val keys = generator.generateKeyPair()
Log.i( "Cryptic", keys.private.toString())
Log.i( "Cryptic", keys.public.toString())
Log.i( "Cryptic", keys.private.encoded.contentToString())
Log.i( "Cryptic", keys.public.encoded.contentToString())
Log.i( "Cryptic", keys.private.encoded.toString(Charsets.UTF_8))
Log.i( "Cryptic", keys.public.encoded.toString(Charsets.UTF_8))
I em experiencing error message, because the private key is null, but the public key is generated:
java.lang.NullPointerException: keys.private.encoded must not be null
at com.batsoft.stockmobile.service.Cryptography.encryptString(Cryptography.kt:35)
I am still seeking to convert it to the string to look like LockBox generated key - just for comparison.
But at the present stage I am already confused - why I have to provide chaining mode and padding scheme for the key generation? My understanding is that keys are just encoded large integer. And the chaining mode, padding scheme are used for the encryption/decryption only? Of course, I need to provide key size, this is understandable.
So - my goal is to configure Android/javax RSA key generation and RSA key use so that it conform exactly to the RSA key generation and use in Delphi 10.2 LockBox 2. My aim is to use LockBox generated keys in the Android program. I have described the number of paths I have already taken but still I have not managed to generate javax keys in the same format as Delphi keys. As I don't know the exact configuration of they keys in LockBox (I guess - there is none, except for the key size) I can not configure my encryption/decryption on Android/javax as well.
How to achieve this conformity by changing Android/javax code?
Additional info: Delphi LbAsym.pas procedure TLbAsymmetricKey.StoreToStream(aStream : TStream); is very important, because it saves the key-integer into stream. In the case when passphrase is not used, the code is very straingtforward:
aStream.Write(KeyBuf, Len);
and the comment in the Pascal code reads as:
save key to ASN.1 format stream (encrypt if necessary)
So, maybe my question my be reformulated - how to save Android/javax generated keys in ASN.1 format or to read keys from ASN.1 format?
Additional Info 2: I am looking for the way to save Java-generated keys as PEM strings, and this can be done by the Android Kotlin code:
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA /*, ANDROID_KEYSTORE */)
val builder = KeyGenParameterSpec.Builder(REG_KEY,
KeyProperties.PURPOSE_ENCRYPT and KeyProperties.PURPOSE_DECRYPT)
.setKeySize(1024)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
//.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
val keys = generator.generateKeyPair()
//https://stackoverflow.com/questions/25129822/export-rsa-public-key-to-pem-string-using-java
val writer = StringWriter()
val pemWriter = PemWriter(writer)
pemWriter.writeObject(PemObject("PUBLIC KEY", keys.public.encoded))
pemWriter.flush()
pemWriter.close()
Log.i( "Cryptic", writer.toString())
Note, this requires to add gradle dependencies:
implementation 'org.bouncycastle:bcpkix-jdk15to18:1.68'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.68'
In order to use Delphi-generated keys (apparently LockBox 2 generates and saves them as PEM strings) I need to parse PEM strings and assign them as keys javax Cipher.
Additional Info 3: This is strange. Java generated string (from Additional Info 2) is a about 25 characters longer gan Delphi generated (based64 encoded) string (of course, I have removed initial and trailing strings that somes with PEM file and are just constants like '===PUBLIC KEY===') and it can be used perfectly as publicKeyString in my initial code - the encrytion with such public key works perfectly. Delphi public key string is some 25 characters shorter and does not work.
So - I have figure out what happens inside every step and tried to establish parallel steps, but ultimately I have failed to find solution.
Additional Info 4: Here are base64 encoded public keys:
Generated from Delphi:
MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aEwyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz+bsD9UqS0w5CUh3KHwqhPv+HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg==
Generated from Android Kotlin/java:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCuQi7gMZwWL1iEhNVgdu23S/rYYhtntXQlfVVBjcGiSE8EXzjjnZHxcYHcIszV0F6F20msGK8MFernJpWg8k7J3GLH4TYkQwEEy6jWnRdEB3uqQWFCNQ/CflCHtq1o1iSS0qmXcHQuI7zZ0cHd5FNDg4Bl/DveftEje9yTgUXN3wIDAQAB
I am not sure, but maybe there is some online service that can base64 decode those strings and then extract big integer from them according to some scheme and detecte the format.
As stated in the comments, the Delphi code generates a public key in PKCS#1 format, while the Kotlin code expects a key in X.509/SPKI format.
With BouncyCastle it is possible for the Kotlin code to import a PKCS#1 public key.
This requires the classes PEMParser and JcaPEMKeyConverter.
Example:
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import javax.crypto.Cipher
import java.security.PublicKey
import java.io.FileReader
import java.util.Base64
...
val inputFile: String = "<path to PKCS#1 PEM file>"
// Key import
var publicKey: PublicKey? = null
FileReader(inputFile).use { fileReader ->
PEMParser(fileReader).use { pemParser ->
val spki: SubjectPublicKeyInfo = pemParser.readObject() as SubjectPublicKeyInfo
val converter = JcaPEMKeyConverter()
converter.setProvider(BouncyCastleProvider())
publicKey = converter.getPublicKey(spki)
}
}
// Encryption
val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val ciphertext: ByteArray = cipher.doFinal("The quick brown fox jumps over the lazy dog".toByteArray())
val ciphertextB64: String = Base64.getEncoder().encodeToString(ciphertext);
println(ciphertextB64)
The code imports a PEM encoded public key in PKCS#1 format. The PEM encoding includes a format-specific header and footer, and in the body, there are line breaks after every 64 characters. The PEM encoded Delphi key is:
-----BEGIN RSA PUBLIC KEY-----
MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aE
wyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz+bsD9UqS0w5CUh3KHwqhPv
+HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg==
-----END RSA PUBLIC KEY-----
Unfortunately the import of your Delphi key does not work. There is a provider dependent error message displayed, e.g. in the case of the BouncyCastleProvider:
PEMException: unable to convert key pair: encoded key spec not recognized: RSA publicExponent is even
Indeed, the key generated by the Delphi code has an even public exponent with the value 9514 (0x252A):
0:d=0 hl=3 l= 136 cons: SEQUENCE
3:d=1 hl=3 l= 129 prim: INTEGER :BB443155F11C15B3071FC0CCEFDA387AD8110641DD596A601BEA5FF487112EDE8444576826D43B9684C3265C0EB761D9D313CBF69B34368524AE1B12A821A42EB2BC236F9D3AA3033F9BB03F54A92D30E42521DCA1F0AA13EFF87146705EEB02E53DBA825C5976D30BDB540441BBADD99D98C6AD2202F563879B3800901E7D71
135:d=1 hl=2 l= 2 prim: INTEGER :252A
This should not be the case (φ(n) or λ(n) and e would then not be coprime), s. here. You should therefore check the key generation in the Delphi code.
Another issue is in the line:
val result: String = String(bytes, Charsets.UTF_8)
The UTF-8 decoding of a ciphertext (which generally contains non-UTF-8 compliant byte sequences) corrupts this ciphertext.
If a ciphertext is to be converted to a string, a binary-to-text encoding such as Base64 must be applied.

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);

Storing a secret key in Android KeyStore using API 18 and Above

Security is not my area of expertise but I have a question regarding storing a secret key in the Android KeyStore using API 18 and above. I use the following code to try and store my key:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.SecretKeyEntry sKeyEntry = new KeyStore.SecretKeyEntry(ivKey);
ks.setEntry(ALIAS, sKeyEntry, null); // This is where the issue is
I understand that "null" should be a KeyProtection param that I build but this isn't available for API 18. Is there a workaround for this issue? I have been having difficulty finding anything that works.
EDIT I should mention that leaving it as null throws the error:
java.security.KeyStoreException: Protection parameters must be specified when importing a symmetric key
AndroidKeyStore did not support secret keys until API level 23. To do what you're trying to do you have to target 23 or above.
What you could do is use AndroidKeyStore's support for public/private key pairs (e.g. RSA) to encrypt the secret key material, then store it in a local file. When you want to use it, you'd need to use the private key to decrypt it, then once you have the key material use the normal Java crypto APIs (i.e. don't specify "AndroidKeyStore") to do cipher operations with it.
To see how to use AndroidKeyStore RSA key pairs to encrypt and decrypt, take a look at http://www.androidauthority.com/use-android-keystore-store-passwords-sensitive-information-623779/
However, I don't think that actually achieves anything from a security perspective. What security goals are you trying to achieve?
For creating a RSA(Public/Private) key using AndroidKeyStore you can use following methods.
Getting a KeyStore
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
Check if KeyStore contains our key or not
If KeyStore does not contains key, then create keys
if (!keyStore.containsAlias(KEY_NAME)) {
val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")
keyPairGenerator.initialize(
KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setAlgorithmParameterSpec(RSAKeyGenParameterSpec(2048, F4))
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build()
)
val keyPair = keyPairGenerator.generateKeyPair()
val publicKey = keyPair.public
val privateKey = keyPair.private
}
Retrieve keys using KEY_NAME
val privateKeyEntry = keyStore.getEntry(KEY_NAME, null) as KeyStore.PrivateKeyEntry
val privateKey = privateKeyEntry.privateKey
val publicKey = privateKeyEntry.certificate.publicKey
For more information you can refer this:

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