im trying to implement SSL connection on android, but i have a problem when i try to load my keystore, but i when i call KeyStore.getInstance("RSA") i got this exception.
03-26 12:19:28.660: E/AndroidRuntime(6465): Caused by: java.lang.RuntimeException: java.security.KeyStoreException: java.security.NoSuchAlgorithmException: KeyStore RSA implementation not found
private KeyStore loadKeyStore() {
if (keyStore != null) {
Log.i("WSclient::KeyStore", " keyStore!=null");
return keyStore;
}
try {
Log.i("WSclient::KeyStore", " keyStore.getInstancel");
keyStore = KeyStore.getInstance("RSA");
Log.i("WSclient::KeyStore", " keyStore:: inputStream");
InputStream in = context.getResources().openRawResource(R.raw.file);
try {
Log.i("WSclient::KeyStore", " keyStore.load");
keyStore.load(in, KEYSTORE_PASSWORD.toCharArray());
} finally {
in.close();
}
return keyStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
The exception says it all. There is no such thing as an RSA keystore. There are JKS keystores, PCKS#11 keystores, WindowsMY keystores, all kinds of things. What kind yours is, only you know.
You can create a private/public key pair for the RSA algorithm as follows:
Calendar calendarValidityStart = Calendar.getInstance();
Calendar calendarValidityEnd = Calendar.getInstance();
calendarValidityEnd.add(Calendar.YEAR, 99);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias("MyKeyAlias")
.setSubject(new X500Principal("CN=" + "MyKeyAlias"))
.setSerialNumber(BigInteger.valueOf(1337))
.setStartDate(calendarValidityStart.getTime())
.setEndDate(calendarValidityEnd.getTime())
.build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(
"RSA",
"AndroidKeyStore");
kpGenerator.initialize(spec);
KeyPair keyPair = kpGenerator.generateKeyPair();
You can later retrieve the key pair from the KeyStore as follows:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry keyStoreEntry = keyStore.getEntry(alias, null);
See the Android Developers BasicAndroidKeyStore Sample for more info.
Be sure to have
private KeyFactory keyStore;
keyStore must be KeyFactory type.
//keyStore = KeyStore.getInstance("RSA");
keyStore = KeyFactory.getInstance("RSA");
See this example:
How to use keystore in Java to store private key?
Related
I have a code in android to:
Generate ECDSA key-pair (Public Key and Private Key)
Generate Certificate Signing Request (CSR) from public key
Store Private Key
Then I send CSR to CA server. The CA server generate X.509 certificate.
Now I want to encrypt a string with public key from that X.509 certificate above. And then I will write android code to decrypt encrypted string using stored Private Key.
I have code to encrypt/ decrypt ECIES in android:
Here is my code:
///Gen Key
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
try {
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA","SC");
g.initialize(spec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
Toast.makeText(MainActivity.this, "GEN KEY SUCCESS!!", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
/////Encrypt
String origin = txtOrigin.getText().toString();
try {
Cipher c = Cipher.getInstance("ECIES","SC");
c.init(Cipher.ENCRYPT_MODE,publicKey);
encodeBytes = c.doFinal(origin.getBytes());
txtEncrypt.setText(Base64.encodeToString(encodeBytes,Base64.DEFAULT));
Toast.makeText(MainActivity.this, "ENCRYPT SUCCESS!!", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
////Decrypt
byte[] decodeBytes = null;
try
{
Cipher c = Cipher.getInstance("ECIES","SC");
c.init(Cipher.DECRYPT_MODE,privateKey);
decodeBytes = c.doFinal(encodeBytes);
String deCrypt = new String(decodeBytes,"UTF-8");
txtDecrypt.setText(deCrypt);
Toast.makeText(MainActivity.this, "DECRYPT SUCCESS!!", Toast.LENGTH_SHORT).show();
}
catch (Exception ex)
{
ex.printStackTrace();
}
But I want to encrypt string using python with public key from X.509 certificate on my CA server and decrypt using my code above in android.
I use a SecretKey to encrypt sensitive data in my application. Currently I am storing my SecretKey in Base64 encoded format in DB or SharedPrefs which is not a safe place to store Secret on a rooted phone. Hence, I want to move my SecretKey to Android KeyStore. The problem I am facing is when I try this sample code from Google, it expects a PrivateKey instead of SecretKey. I couldn't figure out a way to store my SecretKey in KeyStore and fetch it for later use. I tried this:
private static void writeSecretKeyToKeystore(SecretKey secretKey, Context context) {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
keyStore.setKeyEntry("Key", secretKeyEntry.getSecretKey().getEncoded(), null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
When I try above code, it throws an exception Operation not supported because encoding is unknown.
Any sample code would be of great help.
WRONG
java.security.KeyStore can store both symmetric and asymmetric keys. You just need to instantiate KeyStore.SecretKeyEntry passing it your SecretKey in the constructor and then use the KeyStore#setEntry method to save it:
keyStore.setEntry(
"key1",
new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockMode(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
To get it back out use:
SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
UPDATE
After some research I was surprised to find out, that AndroidKeyStore doesn't support symmetric keys. (see the discussion: https://groups.google.com/forum/#!topic/android-developers/gbmIRKRbfq8)
The work-around would be to encrypt your SecretKey and store it in SharedPreferences. Then store the key to decrypt your key in the Keystore. Here's an implementation using scytale.
public static String getBase64EncodedSecretKey(){
Store store = new Store(context);
Crypto crypto = new Crypto(Options.TRANSFORMATION_SYMMETRIC);
SecretKey key = store.getSymmetricKey("key_alias", null);
String encryptedData = PreferenceManager.getDefaultSharedPreferences(context).getString("myEncryptedSecretKey", "");
return crypto.decrypt(encryptedData, key);
}
public static void storeKey(String base64EncodedSecretKey){
Store store = new Store(context);
if (store.hasKey("key_alias")) {
store.deleteKey("key_alias");
}
SecretKey key = store.generateSymmetricKey("key_alias", null);
Crypto crypto = new Crypto(Options.TRANSFORMATION_SYMMETRIC);
String encryptedData = crypto.encrypt(base64EncodedSecretKey, key);
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("myEncryptedSecretKey",encryptedData).apply();
}
// Usage:
//store SecretKey
byte[] encodedKey = secretKeyEntry.getSecretKey().getEncoded();
String base64EncodedKey = Base64.encodeToString(encodedKey);
storeKey(base64EncodedKey);
//get SecretKey
String base64EncodedKey = getBase64EncodedSecretKey();
byte[] encodedKey = Base64.decode(base64EncodedKey);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
I want to check if installed android application has self signed or trusted certificate. The problem is that my code listed below raises CertificateException for all installed (including Google made) apps. Could you help me to find a problem why it doesn't work correctly?
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
List<ResolveInfo> runningServices = getPackageManager().queryIntentActivities(intent, 0);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("AndroidCaStore");
ks.load(null, null);
tmf.init(ks);
X509TrustManager trustManager = (X509TrustManager)tmf.getTrustManagers()[0];
PackageManager manager = getPackageManager();
try {
for (ResolveInfo runningService : runningServices) {
PackageInfo info = manager.getPackageInfo(runningService.activityInfo.packageName,
PackageManager.GET_SIGNATURES);
Signature signature = info.signatures[0];
Signature[] arrSignatures = info.signatures;
for (Signature sig : arrSignatures) {
byte[] rawCert = sig.toByteArray();
InputStream certStream = new ByteArrayInputStream(rawCert);
CertificateFactory certFactory;
X509Certificate x509Cert;
try {
certFactory = CertificateFactory.getInstance("X509");
x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);
trustManager.checkServerTrusted(new X509Certificate[] { x509Cert }, "RSA");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
The answer is pretty obvious: we can't validate android app certificate using trusted certificates chain (what normally do ssl guts to check web resource certificate), because they are self-signed. That is why checkServerTrusted function raises CertificateException during such check. This book gives following information:
Because a process is tied to a package name, and a package name is
tied to its signature, signatures play a role in securing the data
belonging to a package. A package is typically signed with a
self-signed PKI (Public Key Infrastructure) certificate. A certificate
identifies who the author of the package is. These certificates need
not be issued by a certificate authority. This means the information
in the certificate is not approved or validated by any authority. This
means one can create a certificate that says that their name is
Google. The only assurance is that this package name is reserved to
that user if no one had claimed it in the marketplace before, and any
subsequent updates to that package are given only to that user
(identified by that certificate).
The user has saved a .p12-file (e.g. his S/MIME certificate) on SD-Card. I want to load this certificate (or the extracted private and public key) into the AndroidKeyStore.
File file = new File(pathToP12File);
Certificate c = null; // TODO: convert file into something I can load into the keystore
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
ks.setCertificateEntry("myCertAlias", c);
What's the best way to convert the file into something which can be set as a certificate entry in the keystore?
It's possible to interpret the p12-file as a keystore, extract the certificate and load it into the AndroidKeyStore.
private void getCertsFromP12(String pathToFile, String passphrase){
try {
KeyStore p12 = KeyStore.getInstance("pkcs12");
p12.load(new FileInputStream(pathToFile), passphrase.toCharArray());
Enumeration e = p12.aliases();
while (e.hasMoreElements()) {
String alias = (String) e.nextElement();
X509Certificate c = (X509Certificate) p12.getCertificate(alias);
addCertificateToKeyStore(c);
}
} catch (Exception e) {}
}
private void addCertificateToKeyStore(X509Certificate c) {
try {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
ks.setCertificateEntry("myCertAlias", c);
} catch (Exception e){}
}
If you want to install your certificate into the android KeyChain you can use your P12 to install it directly like in the next method:
InputStream is = new ByteArrayInputStream(pkcs12);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] keychainP12 = new byte[bis.available()];
bis.read(keychainP12);
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychainP12);
context.startActivity(installIntent);
I am trying to encode and decode Strings on Android using a Private Key generated and stored using the Android Key Store Provider that was introduced in Android 4.3
I can successfully generate and get the private key using the following code:
private void generatePrivateKey(Activity context, String alias){
/** Generate a new entry in the KeyStore by using the * KeyPairGenerator API. We have to specify the attributes for a * self-signed X.509 certificate here so the KeyStore can attach * the public key part to it. It can be replaced later with a * certificate signed by a Certificate Authority (CA) if needed. */
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
kpg.initialize(new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=" + alias))
.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
KeyPair kp = kpg.generateKeyPair();
/*
* Load the Android KeyStore instance using the the
* "AndroidKeyStore" provider to list out what entries are
* currently stored.
*/
KeyStore ks = null;
try {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
/*
* Use a PrivateKey in the KeyStore to create a signature over
* some data.
*/
KeyStore.Entry entry = null;
try {
entry = ks.getEntry(alias, null);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
Log.w("E", "Not an instance of a PrivateKeyEntry");
}
else{
Log.w("E", "Got Key!");
privateKeyEntry = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
}
}
And here is the code I am using for encrypt (encode) and decrypt (decode):
private String encryptString(String value){
byte[] encodedBytes = null;
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
cipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry );
encodedBytes = cipher.doFinal(value.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}
private String decryptString(String value){
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
c.init(Cipher.DECRYPT_MODE, privateKeyEntry );
decodedBytes = c.doFinal(Base64.decode(value, Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return new String(decodedBytes);
}
The Encryption appears to work fine but when I try to decrypt it I get the following error:
javax.crypto.BadPaddingException: error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
Googling this seems to suggest that the private key used for decryption is different to the one used for decryption but in my code I use the exact same private key for both. I have also seen it suggested to set the key size manually but doing this in the KeyPairGenerator builder like this:
.setKeySize(1024);
Did not work and seems to be only available on API 19, I need to target API 18.
Can anyone help point me in the right direction as to a solution?
You are not using the public key for encryption.
When you are using asymmetric encryption algorithms, you need to use the public key to encrypt your data, and the private key only to decrypt it again.
Besides encryption, you can also use the private key for signing, but that's not what you want here, so let's forget about that for the moment.
If you take the public key from the generated pair, when you encrypt your string, and the private key when decrypting, you should get the desired result. The public key you can extract by accessing the certificate from the keystore-object that holds your private key.
Alternatively you could also use a symmetric algorithm like AES and by that make your work a lot easier. Plus, symmetric algorithms are usually much faster, which is why asymmetric algorithms are never used purely, but in conjunction with symmetric algorithms, building so-called hybrid algorithms.
Signature generation is not the same thing as encryption. You need to encrypt with the public key and decrypt with the private key if you want encryption. If you want signature generation, you need to sign with the private key and verify with the public key. This order cannot be reversed nor can it be mixed (securely).