Storing AES Secret key using keystore in java - java

I am using Java keystore to store the secret key for AES encryption.
final String strToEncrypt = "Hello World";
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey sk = kg.generateKey();
String secretKey = String.valueOf(Hex.encodeHex(sk.getEncoded()));
//Storing AES Secret key in keystore
KeyStore ks = KeyStore.getInstance("JCEKS");
char[] password = "keystorepassword".toCharArray();
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
} finally {
if (fis != null) {
fis.close();
}
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(password);
KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(sk);
ks.setEntry("secretKeyAlias", skEntry, protParam);
But i am getting following Exception.
Exception in thread "main" java.security.KeyStoreException: Uninitialized keystore
at java.security.KeyStore.setEntry(Unknown Source)
How to fix this error? Thanks in advance

According to the KeyStore documentation ,
Before a keystore can be accessed, it must be loaded.
so you are loading the KeyStore but what if a FileNotFoundException occures at
fis = new java.io.FileInputStream("keyStoreName"); , hence if file does not exist we load the KeyStore with null values ,like , ks.load(null,null); .

Related

java.security.KeyStoreException: Cannot store non-PrivateKeys

I have generated a keypair of Public-private keys, and am trying to store the privatekey in Java keyStore.
But I am getting error everytime.
My piece of code:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String ss = Base64.encodeBase64String(cipher.doFinal(ppp.getBytes("UTF-8")));
System.out.println(ss);
// Creating the KeyStore object
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Loading the KeyStore object
char[] ksPassword = "changeit".toCharArray();
String path = "C:/Program Files/Java/jre1.8.0_201/lib/security/cacerts";
java.io.FileInputStream fis = new FileInputStream(path);
keyStore.load(fis, ksPassword);
// Creating the KeyStore.ProtectionParameter object
KeyStore.ProtectionParameter protectionParam = new
KeyStore.PasswordProtection(ksPassword);
// Creating SecretKey object
SecretKey mySecretKey = new SecretKeySpec(ppp.getBytes(), "RSA");
// Creating SecretKeyEntry object
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(mySecretKey);
keyStore.setEntry("mykeyalias", secretKeyEntry, protectionParam);
// Storing the KeyStore object
java.io.FileOutputStream fos = null;
fos = new java.io.FileOutputStream("newKeyStoreName");
keyStore.store(fos, ksPassword);
But i'm getting below exception at the line keyStore.setEntry while running:
java.security.KeyStoreException: Cannot store non-PrivateKeys
at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:261)
at sun.security.provider.JavaKeyStore$JKS.engineSetKeyEntry(JavaKeyStore.java:56)
at java.security.KeyStoreSpi.engineSetEntry(KeyStoreSpi.java:550)
at sun.security.provider.KeyStoreDelegator.engineSetEntry(KeyStoreDelegator.java:179)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineSetEntry(JavaKeyStore.java:70)
at java.security.KeyStore.setEntry(KeyStore.java:1557)
at com.sprint.neo.bc4j.util.TestMain.StoringKeys(TestMain.java:33)
Can anyone help to resolve this issue so that I can store Privatekeys to the java keyStore along with an alias name, to be used later. Where is the exact wrong in the above piece of code. Thanks a lot.

Java Key Store always ends up with null aliases

I've been trying on this for a couple of days and I'm hopelessly stuck.
To fully understand how java key store works, i've been trying to create my own keystore, put some stuff inside it, then retrieve them from another program.
Here's my keystore generator :
{
//generate a X509 certificate
Security.addProvider(new BouncyCastleProvider());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(new FileInputStream("certificate.cer"));
LOGGER.debug("BouncyCastle provider & X509 certificate added.");
//generate a private & a public key
KeyPair keyPair = generateRSAKeyPair();
RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey pub = (RSAPublicKey) keyPair.getPublic();
//generate a keystore
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = "keystore_password".toCharArray();
ks.load(null, keyStorePassword);
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.jks")) {
ks.store(fos, keyStorePassword);
}
ks.load(new FileInputStream("TestKeyStore.jks"), keyStorePassword);
//Symmetric key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry((secretKey));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(keyStorePassword);
ks.setEntry("symmetric_key", secretKeyEntry, protectionParameter);
//Asymmetric key
X509Certificate[] x509Certificates = new X509Certificate[1];
x509Certificates[0] = certificate;
ks.setKeyEntry("asymmetric key", priv, keyStorePassword, x509Certificates);
//certificate
ks.setCertificateEntry("test_certif", certificate);
Key key = ks.getKey("symmetric_key", keyStorePassword);
System.out.println("I have this symmetric key : " + key);
X509Certificate certificate1 = (X509Certificate) ks.getCertificate("test_certif");
System.out.println("I have this certificate : " + certificate1);
System.out.println(ks.aliases().nextElement());
LOGGER.debug("all went well");
}
As you probably noticed, it's not all polished: my goal for now is only to put some stuff inside the keystore. But the point here, from the last System.out.println(ks.aliases().nextElement());, is just to see there is indeed something inside the keystore. And this is working just fine, it gives back symmetric_key.
Now, from the same folder is another class that is supposed to read from that keystore.
Note: there is no issue regarding the file (I've tested by moving its localization) so it can't be that.
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = "keystore_password".toCharArray();
ks.load(new FileInputStream("TestKeyStore.jks"), keyStorePassword);
System.out.println(ks.containsAlias("symmetric_key"));
This always gets me: false
If I try this: System.out.println(ks.aliases());, it's always null
If I try this:
if (!keystore.aliases().hasMoreElements()) {
System.out.println("nothing inside the keystore");
}
it gives me back nothing inside the keystore.
Even though it's not the case in the generator.
Any clue?
Thank you
The problem is that you are setting the entries after writing the keystore.
If you move:
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.jks")) {
ks.store(fos, keyStorePassword);
}
Till after this line:
ks.setCertificateEntry("test_certif", certificate);
Everything should work fine.
Bellow you have an working example with some code removed for clarity:
public static void main(String[] args) throws Exception{
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
final KeyPair keyPair = keyGen.genKeyPair();
RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate();
//generate a keystore
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = PASSWORD;
ks.load(null, keyStorePassword);
X509Certificate[] chain = {generateCertificate("cn=Unknown", keyPair, 365, "SHA256withRSA")};
// saving one keypair in keystore object
ks.setKeyEntry("asymmetric key", keyPair.getPrivate(), keyStorePassword, chain);
//Symmetric key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry((secretKey));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(keyStorePassword);
// saving symmetric key in keystore object
ks.setEntry("symmetric_key", secretKeyEntry, protectionParameter);
// Saving our keystore object into the filesystem
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.p12")) {
ks.store(fos, keyStorePassword);
}
// The rest of the code
}
The reason why it works in the method that saves the keystore is because the changed keystore is still in memory, but not on the filesystem. Also, since you are creating a PKCS12 keystore, I would avoid the .jks extension and go for something like .pkcs12.

getKey(alias, keyPassword) returns null value for secret key when calling from separate method

I am trying to retrieve a stored key in java key store. I have written the following code.
public class clientEncryptionUtility
{
public static void generateKeyAndStoreOnKeyStore(String _keyStorePassword, String _keyStorePath, String _keyPassword, String keyAlias) throws Exception // take the keystore path, alias, password
{
KeyStore keyStore = KeyStore.getInstance("JCEKS");
char[] keyStorePassword = _keyStorePassword.toCharArray();
String path = _keyStorePath;
FileInputStream fis = new FileInputStream(path);
//load keystore
keyStore.load(fis, keyStorePassword);
//Loading the KeyStore object
KeyStore.ProtectionParameter protectionParam = new KeyStore.PasswordProtection(keyStorePassword);
//Generate the symmetric key for encryption
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
int keyBitSize = 128;
keyGenerator.init(keyBitSize, secureRandom);
SecretKey secretKey = keyGenerator.generateKey(); //Secret encryption key is genereated
//setting the password for the key stored in keystore
System.out.println("Algorithm used to generate key : "+secretKey.getAlgorithm());
char[] keyPassword = _keyPassword.toCharArray();
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyPassword);
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
keyStore.setEntry(keyAlias, secretKeyEntry, entryPassword);
SecretKey newSecretKey = (SecretKey) keyStore.getKey(keyAlias, keyPassword);
String stringKey = newSecretKey.toString();
System.out.println("The encryption key at the alias is: " + stringKey);
}
public static void getKeyFromKeyStore(String _keyStorePassword, String _keyStorePath, String keyAlias, String _keyPassword) throws Exception
{
KeyStore keyStore = KeyStore.getInstance("JCEKS");
char[] keyStorePassword = _keyStorePassword.toCharArray();
String path = _keyStorePath;
FileInputStream fis = new FileInputStream(path);
//load keystore
keyStore.load(fis, keyStorePassword);
char[] keyPassword = _keyPassword.toCharArray();
SecretKey secretKey = (SecretKey) keyStore.getKey(keyAlias, keyPassword);
// Key key = keyStore.getKey(keyAlias, keyPassword);
String stringKey = secretKey.toString();
System.out.println("The encryption key at the alias is: " + stringKey);
}
}
-If I call the generateKeyAndStoreOnKeyStore() method, and store the key and retrieve the key in the same function, the key is retrieved.
-However if I do the same from another method getKeyFromKeyStore() wherein I am just trying to retrieve the key at the alias from the keystone, I get a nullPointerException.
-Where am I going wrong?
Unfortunately it's not clear from the javadocs that you must call the KeyStore.store(...) method to persist changes to the keystore. Once generateKeyAndStoreOnKeyStore() exits, the KeyStore instance created there goes out of scope and any unsaved changes made to the keystore disappear.
Call the KeyStore.store(...) method after making changes.

Read Secretkey from JCEKS keystore | Java

I am trying to read secret key (passphrase) from JCEKS keystore using Java code , code is working for JKS keystore but not for JCEKS keystore. i am using ibm_sdk71 as runtime (due to project requirements).
public static String getPasswordFromKeystore(String entry, String keystoreLocation, String keyStorePassword,String keyPP1) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeySpecException, java.security.UnrecoverableEntryException{
char[] password = "new".toCharArray() ;
FileInputStream fIn = new FileInputStream(keystoreLocation);
KeyStore.PasswordProtection keyStorePP = new KeyStore.PasswordProtection(keyStorePassword.toCharArray());
KeyStore.PasswordProtection keyPP = new KeyStore.PasswordProtection(keyPP1.toCharArray());
KeyStore ks= KeyStore.getInstance("JCEKS");
ks.load(fIn, keyStorePassword.toCharArray());
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
KeyStore.SecretKeyEntry ske;
ske = (KeyStore.SecretKeyEntry)ks.getEntry(entry, keyPP);
PBEKeySpec keySpec;
keySpec = (PBEKeySpec)factory.getKeySpec(ske.getSecretKey(),PBEKeySpec.class);
password = keySpec.getPassword();
return new String(password);
}
Appreciate any guidance.
Regards

RSA Encryption Javascript and Decrypt Java

Spent almost 2 days with different combinations.I am generating a asymmetric key pair (public and private) in java using RSA algorithm and trying to use the public key in javascript to encrypt some text and decrypt back in java on server side. I am getting "javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes" exception while trying to decrypt back the string encrypted in javascript. Would appreciate some help...
Using thi Javascript library to encrypt.
https://github.com/wwwtyro/cryptico
var publicKeyString = ""// base64encoded public key string generated in java
Here is my javascript code
var EncryptionResult = cryptico.encrypt("somestring", publicKeyString);
console.log("Encrypted status-"+EncryptionResult.status);
console.log("Encrypted String-"+EncryptionResult.cipher);
It is successfully encrypting the string.
Java Key Generation and Decryption
Cipher cipher = Cipher.getInstance("RSA");
KeyFactory fact = KeyFactory.getInstance("RSA");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024); // 1024 used for normal
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
FileOutputStream fos = null;
ObjectOutputStream oos = null;
Code to store the private key in file which is used to decrypt in decrypt method.
RSAPrivateKeySpec rsaPrivKeySpec = fact.getKeySpec(privateKey,
RSAPrivateKeySpec.class);
System.out.println("Writing private key...");
fos = new FileOutputStream(PRIVATE_KEY_FILE);
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos.writeObject(rsaPrivKeySpec.getModulus());
oos.writeObject(rsaPrivKeySpec.getPrivateExponent());
oos.close();
Decrypt method
public String decrypt(String ciphertext)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException
{
if (ciphertext.length() == 0) return null;
byte[] dec = org.apache.commons.codec.binary.Base64.decodeBase64(ciphertext);
try {
System.out.println("Private Key file name----"+PRIVATE_KEY_FILE);
privateKey = readPrivateKeyFromFile(PRIVATE_KEY_FILE);
} catch (IOException e) {
e.printStackTrace();
}
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(dec);
return new String(decrypted, PLAIN_TEXT_ENCODING);
}
//reading private key from file
public PrivateKey readPrivateKeyFromFile(String fileName)
throws IOException {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(new File(fileName));
ois = new ObjectInputStream(fis);
System.out.println("Private Key file-"+fileName);
BigInteger modulus = (BigInteger) ois.readObject();
BigInteger exponent = (BigInteger) ois.readObject();
// Get Private Key
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privateKey = fact.generatePrivate(rsaPrivateKeySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ois != null) {
ois.close();
if (fis != null) {
fis.close();
}
}
}
return null;
}
From the Cryptico documentation it seems that it is not a simple RSA encryption, but a complex operation that generates AES key, encrypts it with RSA, encrypts the data with AES and outputs a concatenation of encrypted AES key and encrypted data. If you want to decrypt that in Java you will have to check the Cryptico source code and reimplement the same in Java.
As for your current attempt and javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes error:
When you do not specify the full transformation the default JCE transformation for RSA is RSA/ECB/PKCS1Padding.
In this mode the RSA encrypts or decrypts a single block of data which the length is not greater than the size of the key (more specifically, if the input sequence of bytes is interpreted as a big integer, its value should be less that the modulus used by the RSA). You can find additional information in this and this questions.
With the key size of 1024 bits the maximum data size is 128 bytes, and that is exactly what the exception says because the output of Cryptico is obviously not a single RSA block and its length is greater that expected by "plain" RSA. Trying to use some other cipher mode or padding mode in Java will not help in that situation either.
Thanks Oleg for the detailed information. I will definitely take a look into it.
For now I switched to jsencrypt and it seems to work fine.
https://github.com/travist/jsencrypt
EDIT
How you get the encoded public key for the js encrypt?
Here is the solution for data Encryption from JS and Decrypt on Java(server side). I have used Cryptico js library for encryption(http://wwwtyro.github.io/cryptico/).
First of all we have to generate the java Keystore file from your local system. Don't use other Keystore files such as online Keystore. For creating the java Keystore(JKS) you can use KeyStore Explorer tool.
Below is the config I have used, using KeyStore Explorer tool
Keystore type - JKS
RSA algorithm - Keysize 1024
Version - version 3
Signature algorithm - SHA256 with RSA
Validity period - 99 years(based on your requirement)
Name filed - Fill all the mandatory fields - remember the "alias" and "password" what you entered here.
Finally, save the file as .jks on your local system.
Step-1
we have to use this Keystore file on the java side and we send the public key to the frontend.
I have created the service class which is responsible to load Keystore from the keystore file path(string), Keypair and Decrypt. You have to provide the alias, password, keystore type.
public KeyPair getExistingKeyStoreKeyPair(String keystorePath){
KeyPair generateKeyPair = null
try {
File file = new File(keystorePath)
KeyStore keyStore = loadKeyStore(file, "password", "JKS")
generateKeyPair = getKeyPair(keyStore, "fin360", "password")
} catch (Exception ex){
println(ex)
}
return generateKeyPair
}
public KeyStore loadKeyStore(final File keystoreFile, final String password, final String keyStoreType) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null")
}
final URI keystoreUri = keystoreFile.toURI()
final URL keystoreUrl = keystoreUri.toURL()
final KeyStore keystore = KeyStore.getInstance(keyStoreType)
InputStream is = null
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray())
} finally {
if (null != is) {
is.close()
}
}
return keystore;
}
public KeyPair getKeyPair(final KeyStore keystore, final String alias, final String password) {
PublicKey publicKey
PrivateKey privateKey
Key key
KeyPair keyPair
try {
key = (PrivateKey) keystore.getKey(alias, password.toCharArray())
final Certificate cert = keystore.getCertificate(alias)
publicKey = cert.getPublicKey()
privateKey = key
keyPair = new KeyPair(publicKey, privateKey)
} catch (Exception ex){
println(ex)
}
return keyPair;
}
public decryptData(String data, String keystorePath) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException{
try {
byte[] dectyptedText = new byte[1]
byte[] byteArray = new byte[256]
BigInteger passwordInt = new BigInteger(data, 16)
if (passwordInt.toByteArray().length > 256) {
for (int i=1; i<257; i++) {
byteArray[i-1] = passwordInt.toByteArray()[i]
}
} else {
byteArray = passwordInt.toByteArray();
}
KeyPair generateKeyPair = getExistingKeyStoreKeyPair(keystorePath)
PrivateKey privateKey = generateKeyPair.getPrivate()
Cipher cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
dectyptedText = cipher.doFinal(byteArray)
String txt2 = new String(dectyptedText)
return txt2
}
catch (Exception ex){
println(ex)
return null
}
}
decryptData() method will playing the main role here. When you send the value data.getBytes() directly to the dycrypt method cipher.doFinal(byteArray) you get the exception - IllegalBlockSizeException size should not more than 128 bytes. So we have get rid of the issue I get the workaroud here - [Getting 1 byte extra in the modulus RSA Key and sometimes for exponents also
Basically it adds the zero when we converting data from BigInteger to byteArray. So I removed the zero from the array.
Let's start use the service class to get the key values.
String publicKey= null
String keystorePath = your file path
KeyPair generateKeyPair = encryptDecryptService.getExistingKeyStoreKeyPair(keystorePath)
PublicKey publicKey1 = generateKeyPair.getPublic()
KeyFactory keyFactory;
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(BigInteger.ZERO, BigInteger.ZERO)
try {
keyFactory = KeyFactory.getInstance("RSA")
rsaPublicKeySpec = keyFactory.getKeySpec(publicKey1, RSAPublicKeySpec.class)
} catch(NoSuchAlgorithmException e1) {
println(e1)
} catch(InvalidKeySpecException e) {
println(e)
}
String testPublicKey = rsaPublicKeySpec.getModulus().toString(16)
publicKey = testPublicKey
Send you publicKey to JS.
In your HTML or servlet import all the required js and jar files(you will get it from the cryptico js library).
try{
var rsa = new RSAKey();
rsa.setPublic(pub, "10001");
password = rsa.encrypt(password);
formdata = "password="+password+"&dataEncrypt=true";
}
catch (error){
console.log(error);
}
above I have directly used new RSA() instance(in cryptico library it will be different. Internaly library is using the same) and set the publickey to the instance. We have to use hex string value is '10001'. Form the query string with encrypted data which we send to server. Form data holds the encrypted data well as well as 'dataEncrypt' key value. I used to check whether data is encrypted or not.
Finally on the server side you will get the request params and below is the code for decrypt.
Boolean isDataEncrypted = false
String decryptedPassword = null
isDataEncrypted = params.containsKey("dataEncrypt")
if(params.containsKey("password")){
if(isDataEncrypted) {
String keystorePath = helperService.fetchKeystoreFilePath()
decryptedPassword = encryptDecryptService.decryptData(params.password, keystorePath)
// update decrypted data into request params
params.password = decryptedPassword
}
}
println("Data decrypted => " + decryptedPassword)

Categories