How to store private key in android keystore? - java

I want to store private key in android KeyStore, but I have problem with KeyStore.getInstance("JKS") is error like this:
java.security.KeyStoreException: JKS not found.
Please help me why it's not know for this instance?
I have file load my keystore: mykeystore.jks
My code as below:
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.genKeyPair();
X509Certificate certificate = generateCertificate(keyPair);
KeyStore ks = KeyStore.getInstance("JKS");
char[] password = "xxxxxx".toCharArray();
try (FileInputStream fis = new FileInputStream("path\\mykeystore.jks")) {
ks.load(fis, password);
}
Certificate[] certChain = new Certificate[1];
certChain[0] = certificate;
ks.setKeyEntry("key1", keyPair.getPrivate(), password, certChain);
} catch(Exception e) {
e.printStackTrace();
}
Result error:
java.security.KeyStoreException: JKS not found

A Java KeyStore (JKS) is not available on Android.
The recommended keystore on android is AndroidKeyStore. For your case, get keystore instance using AndroidKeyStore instead of JKS and set password as null because AndroidKeyStore does not accepts any password:
//...
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
//...
ks.setKeyEntry("key1", keyPair.getPrivate(), null, certChain);
In an AndroidKeyStore, you cannot set password for keys or keystore because keystore file(s) managed by Android system itself. Also, generating keys inside to the AndroidKeyStore instead of importing (eg. calling setKeyEntry) is more secure and recommended way.(In supported devices, Android generates keys in separated secure hardware. Which means, nobody, including app itself cannot export keys from that device)
So, generate keys using KeyGenParameterSpec.Builder instead of KeyPairGenerator.
You can check documentation here: https://developer.android.com/training/articles/keystore
But if you think AndroidKeyStore is still not suitable for you, you can list available keystore types in Android via:
// Iterate in security providers
for(Provider provider: Security.getProviders()) {
for(Object item: provider.keySet()) {
if(item.toString().startsWith("KeyStore.")) { // grep KeyStores
Log.d(TAG, "Keystore: " + item.toString() + " available in provider: " + provider.getName());
}
}
}
In an Android Emulator Pixel 2 API 28 this code outputs:
Keystore: KeyStore.BouncyCastle available in provider: BC
Keystore: KeyStore.PKCS12 available in provider: BC
Keystore: KeyStore.BKS available in provider: BC
Keystore: KeyStore.AndroidCAStore available in provider: HarmonyJSSE
Keystore: KeyStore.AndroidKeyStore available in provider: AndroidKeyStore
Which means, available KeyStore's are:
BouncyCastle
PKCS12
BKS
AndroidCAStore
AndroidKeyStore

Related

SSL Connection With Private Keys in HSM

I would like to implement a ssl connection which the RSA key pair are kept in the Hardware Security Module (HSM) in Java.
By using openssl pkcs11 engine, I achieved the TLS connection with the following engine configuration.
[openssl_def]
engines = engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
MODULE_PATH = /home/ubuntu/Desktop/vendorlib.so
PIN = "123456"
init = 0
[req]
distinguished_name = test_name
[req_distinguished_name]
and then I create a certificate using this engine with the following command where ssl_key is kept in HSM device.
OPENSSL_CONF=engine.conf openssl req -new -x509 -days 365 -subj '/CN=test/' -sha256 -config engine.conf -engine pkcs11 -keyform engine -key slot_2-label_ssl_key -out cert.pem
I can confirm the certificate and key pair with the following openssl server and client commands.
For the server:
OPENSSL_CONF=engine.conf openssl s_server -engine pkcs11 -keyform engine -key slot_2-label_ssl_key -cert cert.pem -accept 44330
For the client:
OPENSSL_CONF=engine.conf openssl s_client -connect localhost:44330 -engine pkcs11
However, I need this connection to be established in Vertx(3.81) Java 8 connection.
When I look at the documentation of the vertx, there is an OpenSSLEngineOptions which can be set as serverOptions but I couldn't figure where to put parameters such as key name, slot number and engine id etc.
In fact, OpenSSLEngineOptions has a constructor which takes JsonObject, but I couldn't find any sample for this JsonObject.
Here is a code snipped where I instantiate the Vertx ssl option
HttpServerOptions serverOptions = jerseyServerOptions.getServerOptions();
OpenSSLEngineOptions openSSLOptions = new OpenSSLEngineOptions();
serverOptions.setOpenSslEngineOptions(openSSLOptions);
serverOptions.setPort(1234);
serverOptions.setSsl(true);
String certCopy = "-----BEGIN CERTIFICATE-----
MIICMzCCAZygAwIBAgIJALiPnVsvq8dsMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
BAYTAlVTMQwwCgYDVQQIEwNmb28xDDAKBgNVBAcTA2ZvbzEMMAoGA1UEChMDZm9v
MQwwCgYDVQQLEwNmb28xDDAKBgNVBAMTA2ZvbzAeFw0xMzAzMTkxNTQwMTlaFw0x
ODAzMTgxNTQwMTlaMFMxCzAJBgNVBAYTAlVTMQwwCgYDVQQIEwNmb28xDDAKBgNV
BAcTA2ZvbzEMMAoGA1UEChMDZm9vMQwwCgYDVQQLEwNmb28xDDAKBgNVBAMTA2Zv
bzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzdGfxi9CNbMf1UUcvDQh7MYB
OveIHyc0E0KIbhjK5FkCBU4CiZrbfHagaW7ZEcN0tt3EvpbOMxxc/ZQU2WN/s/wP
xph0pSfsfFsTKM4RhTWD2v4fgk+xZiKd1p0+L4hTtpwnEw0uXRVd0ki6muwV5y/P
+5FHUeldq+pgTcgzuK8CAwEAAaMPMA0wCwYDVR0PBAQDAgLkMA0GCSqGSIb3DQEB
BQUAA4GBAJiDAAtY0mQQeuxWdzLRzXmjvdSuL9GoyT3BF/jSnpxz5/58dba8pWen
v3pj4P3w5DoOso0rzkZy2jEsEitlVM2mLSbQpMM+MUVQCQoiG6W9xuCFuxSrwPIS
pAqEAuV4DNoxQKKWmhVv+J0ptMWD25Pnpxeq5sXzghfJnslJlQND
-----END CERTIFICATE-----";
PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions().setCertValue(Buffer.buffer(certValue));
serverOptions.setPemKeyCertOptions(pemKeyCertOptions);
TLDR;
How can we establish a SSL connection in Vertx where the private key stored in only HSM and could't be extracted?
Edit:
I found the parser of the jsonObject in the constructor of OpenSSLEngineOptions here. Unfortunately, it only reads the sessionCacheEnabled option.
I came up with a solution using Security Provider and Java Keystore. Here is the sample code:
/*
pkcs11.cfg example:
name=PKCS11
library=vendor provided library absolute path
slot=0
*/
String propertyPath = "/root/IdeaProjects/ssl/pkcs11.cfg;
char[] pin = "1234".toCharArray();
Provider p = new SunPKCS11(propertyPath);
Security.removeProvider("IAIK");
Security.addProvider(p);
KeyStore ks = KeyStore.getInstance("PKCS11",p);
ks.load(null,pin);
After the initialization of KeyStore, you can direct your SSLContext to KeyStore. But according to Java 8 PKCS11 Documentation, the CKA_ID attribute of the certificate and the private key should match. If pkcs11 engine matches the pair of certificate and key, it should demonstrate as alias.
//Showing aliases
Enumeration<String> aliases = ks.aliases();
for (; aliases.hasMoreElements(); ){
System.out.println(aliases.nextElement());
}
After verifying aliases, we'll use SSLContext to bind our KeyManager.
SSLContext ctx = SSLContext.getInstane("TLS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(ks,pin);
ctx.init(keyManagerFactory.getKeyManagers(), null, null);

Insert Public Key into a KeyStore programatically

I want to generate a key pair and insert the public and private keys into different keystores, to be used as KeyStore and TrustStore in an SSL Socket connection. I generate the KeyPair using a KeyPairGenerator, and I already have generated a signing certificate for the chain using java's keytool. Then I can insert the private key into a KeyStore like this:
PublicKey publicKey;
PrivateKey privateKey;
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "storepass".toCharArray());
keyStore.setKeyEntry("privatekey", privateKey, "keypass".toCharArray(), chain);
Using this keystore I can create a KeyManager for the SSL connection. But to create a TrustManager, I need the public key stored into a KeyStore as well, so I tried storing the public key in a key store:
PublicKey publicKey;
KeyStore trustStore = KeyStore.getInstance("JKS");
keyStore.load(null, "storepass".toCharArray());
trustStore.setKeyEntry("publickey", publicKey, "public".toCharArray(), chain);
This returns an error: java.security.KeyStoreException: Cannot store non-PrivateKeys
So I tried using setCertificateEntry(), but this doesn't work since it's a PublicKey object and I need a Certificate. So my question is, how can I store the PublicKey in a KeyStore programatically? Using keytool is not an option.

Rename JKS alias with java programmatically

I would like to know how can I rename an alias of a keystore, programmatically in java, not using keytool.
I have my java.security.KeyStore object, containing an certain alias. How can I rename it?
The KeyStore API does not provide a rename operation for aliases. But what you can do is:
Save the content (key pair, certificates) of the keystore entry that you want to rename.
Delete the entry.
Create a new entry with the saved content and the new alias.
As Java code:
Key privateKey = keyStore.getKey(alias, password.toCharArray());
Certificate[] certs = keyStore.getCertificateChain(alias);
keyStore.setKeyEntry(newAlias, privateKey, password.toCharArray(), certs);
keyStore.deleteEntry(alias);
Of course this does not work if the private key is stored on a hardware device (smartcard or HSM) and therefore is not readable.
If the keystore entry contains a trusted certificate, the code looks a bit different:
Certificate cert = keyStore.getCertificate(alias);
keyStore.setCertificateEntry(newAlias, cert);
keyStore.deleteEntry(alias);

Create signature with bouncycastle api. Key always null

I'm using bouncycastle for generating detached signature for XML's signing. For key initialize I use this code:
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(CERT_ALIAS, KEYSTORE_PWD.toCharArray());
I have JKS keystore with certificate. But if I do this:
Key key = ks.getKey(CERT_ALIAS, KEYSTORE_PWD.toCharArray());
key stay always null and I have InvalidKeyException
Where's my mistake? I new in crypto
I can't comment due to too low reputation. So I'll answer/edit instead.
The above example works fine, the error is probably in one of the constants being used. What are they, and what is the exact error you're getting?
I mean something like: java.security.InvalidKeyException: Illegal key size
Here is the working example I tried while loading a KeyStore from a file:
'secret1' is the store password, 'secret2' is the key password and 'myKey' is the key alias.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keyStoreFile.getAbsolutePath()), "secret1".toCharArray());
Key key = keyStore.getKey("myKey", "secret2".toCharArray());

Storing an X.509 certificate into a keystore using java code

I have an X.509 certificate created using bouncycastle library. How can I store it into a java Keystore?
I tried this code
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
java.io.FileInputStream fis =
new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
fis.close();
I found this code here, but the key store created using this way does not work with keytool, it tells me the keystore is corrupted.
Use KeyStore.setCertificateEntry(alias, cert) and give it an alias name of your choice. Then, use KeyStore.store(...) to save the keystore (typically using a FileOutputStream).

Categories