I'm currently learning encryption/decryption techniques in Java and one major problem I have come across is storing the key in a .jks file and being able to load it in during different launches. In my calling class it calls the constructor and this is the code for it:
public Encrypt_Decrypt() throws NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher cipher = Cipher.getInstance("AES");
SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
ivSpec = ivParams;
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
byte[] keyBytes = secretKey.getEncoded();
SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");
key = sks;
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key);
KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection("password".toCharArray());
try
{
File f = new File("keystore.jks");
KeyStore keyStore = KeyStore.getInstance("JKS");
java.io.FileInputStream fis = null;
try
{
fis = new java.io.FileInputStream("keystore");
}
finally
{
if (fis != null)
{
fis.close();
}
}
keyStore.load(fis, "password".toCharArray());
keyStore.setEntry("key", entry, protParam);
try (FileOutputStream fout = new FileOutputStream(f))
{
keyStore.store(fout, "password".toCharArray());
;
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
After launching this code in the calling class, this is the error it returns:
keystore (The system cannot find the file specified)
In my code I create the file so why is it having an issue? I looked in my project folder and it is not being saved there so what do I have to do to be able to create, store and use this file without issues?
The keystore (The system cannot find the file specified) message relates to this line:
new java.io.FileInputStream("keystore");
It looks like that should have been using the File f? Something similar to how the FileOutputStream is handled just below works well:
try (FileInputStream fis = new FileInputStream(f)) {
keyStore.load(fis, "password".toCharArray());
}
For reference, there's another error waiting there. Trying to store the AES symmetric key in the JKS keystore results in this error:
java.security.KeyStoreException: Cannot store non-PrivateKeys
at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:258)
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 KeystoreTest.main(KeystoreTest.java:44)
This is because the JKS storetype only supports public/private keys - also here.
With a new JCEKS keystore instead, your example code then worked fine:
File f = new File("keystore.jceks");
KeyStore keyStore = KeyStore.getInstance("JCEKS");
Related
I am trying to read a cert with file IO , every time I try accessing my cert under
testApp/src/main/resources -> cert
it always reads
X:\workspace1\testApp\target\classes\cert\test.p12
And here is my code that I am using, It always exceptions out with null pointer.
// String fileName = "config/sample.txt";
ClassLoader classLoader = App2.class.getClassLoader();
File file = new File(classLoader.getResource("cert/test.p12").getFile());
FileInputStream fm = new FileInputStream(file);
KeyStore ks = KeyStore.getInstance("PKCS12");
try {
ks.load(fm, "test".toCharArray());
}
catch(Exception e) {
e.printStackTrace();
}
Key key = ks.getKey("test", "test".toCharArray());
Certificate cert = ks.getCertificate("test");
PublicKey publicKey = cert.getPublicKey();
System.out.println("Public key");
System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
fm.close();
I want to read this cert to extract public or private key.
this worked for me
File file = new File(classLoader.getResource("./cert/test.p12").getFile());
I'm using this code to store a key into a KeyStore in an Android App:
SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
DESKeySpec keySpec = new DESKeySpec(key); // byte[] key
SecretKey skey = kf.generateSecret(keySpec);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, "ksPassword".toCharArray());
PasswordProtection pass = new PasswordProtection(
"entryPassword".toCharArray());
KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(skey);
ks.setEntry("keyAlias", skEntry, pass);
FileOutputStream fos = ctx.getApplicationContext().openFileOutput("bs.keystore",
Context.MODE_PRIVATE);
ks.store(fos, ksPassword);
fos.close();
Then, in another method, I use this code to retrieve the key I stored,
FileInputStream fis = ctx.getApplicationContext().openFileInput("bs.keystore");
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(fis, "ksPassword".toCharArray());
Key k = (SecretKey) ks.getKey(keyAlias, "entryPassword".toCharArray());
fis.close();
but the instruction ks.getKey("keyAlias", "entryPassword".toCharArray()) returns null.
Where am I wrong?
Ok, I finally understood the problem...
I used the method to store more than a key in the keystore. Using the code ks.load(null, "ksPassword".toCharArray()); the previous key was erased each time (because loading an empty keystore) and only the last one was stored on the keystore.
So the correct code is:
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try {
FileInputStream fis = ctx.getApplicationContext().openFileInput("bs.keystore");
ks.load(fis, ksPassword);
} catch(FileNotFoundException e) {
ks.load(null, ksPassword);
}
The first time that the method is executed the file bs.keystore does not exist, so the code in the catch block is executed. Instead in the next calls the file exists and the new key is added to the keystore.
I know this is possible with openssl.
But I wonder if there are PKCS converting possibilities(pkcs8 to 12) in Java using any library.
First you read PKCS#8 encoded key as a file and create PrivateKey object
public PrivateKey loadPrivateKey(String keyFile)
throws Exception {
File f = new File(keyFile);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
Then this key is being saved into PKCS#12 key store
public void createKeyStore(String keyStorePwd, String keyStoreFile,
PrivateKey privateKey, X509Certificate certificate)
throws Exception {
char[] pwd = keyStorePwd.toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null, pwd);
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(pwd);
Certificate[] certChain =
new Certificate[]{ certificate };
KeyStore.PrivateKeyEntry pkEntry =
new KeyStore.PrivateKeyEntry(privateKey, certChain);
ks.setEntry("keypair", pkEntry, protParam);
FileOutputStream fos = new FileOutputStream(keyStoreFile);
ks.store(fos, pwd);
fos.close();
}
First of all, this is not a duplicate question. I am facing a very strange issue.
Following is what I do.
Case 1:
Generate Key Pair
Encrypt Using Private Key
Decrypt Using Public Key
Everything works fine.
Case 2:
Load Certificate from Mozila Firefox Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
Everything works fine.
Case 3:
Load Certificate from Internet Explorer Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
At Decrypt time, I get error of BadPadding exception
Following is snippet of each of my codes.
Generating Key Pair
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Load Mozilla KeyStore
String strCfg = System.getProperty("user.home")+ File.separator + "jdk6-nss-mozilla.cfg";
Provider p1 = new sun.security.pkcs11.SunPKCS11(strCfg);
Security.addProvider(p1);
keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, "password".toCharArray());
Content of Config file
name=NSS
slot=2
library=C:/Program Files/Mozilla Firefox/softokn3.dll
nssArgs="configDir='C:/Documents and Settings/pratik.vohera.DIGI-CORP/Application Data/Mozilla/Firefox/Profiles/t48xsipj.default' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"
Load IE KeyStore
keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
Get Public and Private key from KeyStore
if (keyStore != null) {
Enumeration<String> enumaration = null;
try {
enumaration = keyStore.aliases();
} catch (KeyStoreException e1) {
e1.printStackTrace();
}
ArrayList<String> certiList;
while (enumaration.hasMoreElements()) {
String aliases = enumaration.nextElement();
certiList = new ArrayList<String>();
certiList.add(aliases);
try {
selectedCert = keyStore.getCertificate(aliases);
selectedpublickey = (RSAPublicKey) selectedCert.getPublicKey();
selectedAlias = aliases;
selectedprivateKey = (PrivateKey) keyStore.getKey(selectedAlias, null);}
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
Encryption
private static String publicEncrypt(String text, Key pubKey) throws Exception {
BASE64Encoder bASE64Encoder = new BASE64Encoder();
byte[] plainText = text.getBytes();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String encryptedText = bASE64Encoder.encode(cipher.doFinal(plainText));
return encryptedText;
}
Decryption
private static String privateDecrypt(String text, Key priKey)throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] encryptText = base64Decoder.decodeBuffer(text);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String decryptedString = new String(cipher.doFinal(encryptText));
return decryptedString;
}
Exception Stacktrace
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at test.testclass.privateDecrypt(testclass.java:198)
at test.testclass.test(testclass.java:137)
at test.testclass.main(testclass.java:120)
I have been working on this for a long time. This is very important. Do let me know if any further information is required.
In the third case, the problem is: You try to encrypt using the private key and decrypt using the public key which is wrong. You should always decrypt using the private key.
I have been able to use the algorithm to encrypt and decrypt files , but when I go to try and send a file from Android to a WAS server, it fails. Here is the encrypt side
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom();
keygen.init(random);
SecretKey key = keygen.generateKey();
// wrap with RSA public key
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream (getFileLocation(PUBLIC_KEY, localTest)));
Key publicKey = (Key) keyIn.readObject();
keyIn.close();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedKey = cipher.wrap(key);
DataOutputStream out = new DataOutputStream(new FileOutputStream(getFileLocation(SIGN_FILE, localTest)));
out.writeInt(wrappedKey.length);
out.write(wrappedKey);
InputStream in = new ByteArrayInputStream(message.getBytes());
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
crypt(in, out, cipher);
in.close();
out.close();
FileInputStream fis = new FileInputStream(getFileLocation(SIGN_FILE, localTest));
byte[] buffer = new byte[fis.available()];
int i =0;
while (i< buffer.length ){
buffer[i]= (byte)fis.read();
i++;
}
String ss = encodeMsg(buffer);
return ss;
Here is the decrypt side
Security.addProvider(new BouncyCastleProvider());
byte[] arr = decodeMsg(encrypted);
DataInputStream in = new DataInputStream(new ByteArrayInputStream(arr));
int length = in.readInt();
byte[] wrappedKey = new byte[length];
in.read(wrappedKey, 0, length);
// unwrap with RSA private key
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream (getFileLocation(PRIVATE_KEY, localTest)));
Key privateKey = (Key) keyIn.readObject();
keyIn.close();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.UNWRAP_MODE, privateKey);
Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
OutputStream out = new FileOutputStream(getFileLocation(DECRYPTED, localTest));
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
crypt(in, out, cipher);
in.close();
out.close();
FileInputStream fis = new FileInputStream(getFileLocation(DECRYPTED, localTest));
byte[] buffer = new byte[fis.available()];
int i =0;
while (i< buffer.length ){//!= 0) {
buffer[i]= (byte)fis.read();
i++;
}
String ss = new String(buffer);
return ss;
Again, on my workstation, this works. When doing the mobile request to the WAS web server, it fails. At first, it argued with the object class and so I recreated the keys using Java 1.6. I have recompiled the war into Java 1.6 as well. It errors as below.
--cipher unwrap
java.security.InvalidKeyException com.ibm.crypto.provider.RSA.engineUnwrap(Unknown Source)
javax.crypto.Cipher.unwrap(Unknown Source)
com.webapp.web.security.RSAEncrypt.decrypt(RSAEncrypt.java:161)
com.webapp.web.MobileRequest.doPost(MobileRequest.java:81)
javax.servlet.http.HttpServlet.service(HttpServlet.java:738)
javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
...
Does the WAS environment have to be updated to handle this? ideas?
UPDATE the keysize is set to 2048
This could be due to key policy settings, do you have the Unlimited Strength Juristiction Policies installed on both machines? They can be found at the bottom of this page: http://www.oracle.com/technetwork/java/javase/downloads/index.html
Else, how are you sending the data to the server?
The Unlimited Jurisdiciton policy might work, but I attempted to use the IBMJCE without success as well. Then, I switched to use the SunJCE provider (version Java 1.6) and now I am able to do the encryption and decryption in both Android and Websphere. I am having the administrators look into the policy files to see if BouncyCastle could be enabled, but I am ok with using the Sun provider files.