Creating a .p12 file with a KeyStore - java

I have an external service that creates certificate for me, out of which I recieve a buffer (String). I attempt to load this buffer into a KeyStore in Java and then use the "store" function in order to create a .p12 file. However, the store function throws an exception - "Given final block not properly padded".
No matter what I try, I cannot get this to work or find the cause of the issue.
My code is :
public void createP12Certificate(String userName, String comment) throws KeyStoreException, AdminCertificateException, CertificateException, NoSuchAlgorithmException, IOException
{
KeyStore store = KeyStore.getInstance("PKCS12");
/* Some Code that gets 'buff' etc. */
byte[] byteBuff = hexStringToByteArray(buff);
Arrays.reverse(byteBuff);
InputStream inputStream = new ByteArrayInputStream(byteBuff);
store.load(inputStream, password.toCharArray());
OutputStream outputStream = new FileOutputStream(userName+".p12");
store.store(outputStream,anotherPassword); //Throws Exception
}
Thank you very much!

The issue is at those lines
/* Some Code that gets 'buff' etc. */
byte[] byteBuff = hexStringToByteArray(buff);
Because the other posted code would work without an exception.
char[] passwordChars = "password".toCharArray();
String fileOne = "/tmp/output_1.p12";
String fileTwo = "/tmp/output_2.p12";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.store(new FileOutputStream(fileOne), passwordChars);
keyStore = KeyStore.getInstance("PKCS12");
byte[] byteBuff = Files.readAllBytes(Paths.get(fileOne));
InputStream inputStream = new ByteArrayInputStream(byteBuff);
keyStore.load(inputStream, passwordChars);
keyStore.store(new FileOutputStream(fileTwo), passwordChars);

Related

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

How to add, delete or display keys from a keystore without using keytool for AES encryption?

How can I manage keystore in java without using keytool command ?
I know how to load the Key Store from the java code, but this is not what I just want, I want to create a Keystore, Display keys from a keystore or delete a Key entry from a keystore.
Is it possible to do with the java code ?
This is how i am loading the keystore,
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
} finally {
if (fis != null) {
fis.close();
}
}
The instructions are given here to generate a new keystore,
https://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html
but it just generate an empty keystore, not a keystore with the key inside it.
First of all, you have to create an empty keystore before adding a key inside it, and your code wouldn't work because,
To create an empty keystore using the above load method, pass null as the InputStream argument.
See the following example to see how to pass null as argument.
Creating a Keystore,
public static void createStore(String path, String keyStoreName,
String storePassword) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException {
KeyStore store = KeyStore.getInstance("BKS");
char[] password = storePassword.toCharArray();
store.load(null, password);
FileOutputStream fos = new FileOutputStream(path + keyStoreName);
store.store(fos, password);
System.out.println("New Store Created !");
fos.close();
}
The above code is copied from my repo, aes-256-java-bks
According to its description, it has all the features which you need,
This simple code allows you to encrypt/decrypt any kind of file using
AES-256 standard. It uses Bouncy Castle Keystore for Key Management.
Beside Encryption, the code allows you to manage your keystore, like
Creating a new Keystore, Loading an existing keystore, adding key to
an existing keystore, generating new Key with user Password, deleting
key from a keystore or displaying keys from given keystore, all these
features could be accessed at runtime, all you need to do is execute
the program.
The following codes are from the same repository as mentioned above,
Loading store,
static KeyStore loadStore() throws KeyStoreException,
FileNotFoundException, IOException, NoSuchAlgorithmException,
CertificateException {
KeyStore store = KeyStore.getInstance("BKS");
InputStream keystoreStream = new FileInputStream(keyStoreLocation);
store.load(keystoreStream, storePassword.toCharArray());
System.out.println("Key Store loaded!\n");
return store;
}
For security reasons, you cannot display actual keys from the keystore, but you sure could get the list of all the aliases of keys from the keystore,
Check this code,
private static void getAliases() throws KeyStoreException,
FileNotFoundException, NoSuchAlgorithmException,
CertificateException, IOException {
if (store.size() == 0)
System.out.println("Store is Empty!");
Enumeration<String> enumeration = store.aliases();
while (enumeration.hasMoreElements()) {
String alias = (String) enumeration.nextElement();
System.out.println("Key Alias: " + alias);
}
}
Deleting a Key from keystore,
public static void deleteAlias(String alias) throws KeyStoreException {
store.deleteEntry(alias);
}
Your FileInputStreamis not reading "keystorename" cause it does not exists or it does, but in another location.
According to documentation:
To create an empty keystore using the above load method, pass null as the InputStream argument.

Storing AES Secret key using keystore in 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); .

java.security.UnrecoverableKeyException: Cannot recover key when executing KeyManagerFactory.init()

I have the following code that tries to initialize KeyManagerFactory using SHA-256 hash as password.
public static KeyManager[] getKeystoreManagers()
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException {
KeyStore keystore = getKeyStore();
if (keystore == null) {
return null;
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, getMachinePassword(SHA_256).toCharArray());
return kmf.getKeyManagers();
}
getKeyStore() returns my application keystore. getMachinePassword() returns a password using SHA-256, 64 digits password length. The problem is that I get the exception when init() is called:
java.security.UnrecoverableKeyException: Cannot recover key
If I am passing smaller password length lets say, 50 digits the init succeeded. What seems to be the problem here?
I've solved my problem. The keystore was created using setEntry with specific alias. Therefore, in my conversion function I had to get the entry using the old password and set the same entry with the same alias again with the new password. Now, with this updated keystore the KeyManagerFactory.init() runs successfully. See the following code below:
static void convertPasswordAlgorithm(String keystorePath, String fromAlgorithm, String toAlgorithm) throws Exceptionc {
FileInputStream fileInStream = null;
String keystoreFullPath = keystorePath + ISiteConstants.c_FILE_SEPERATOR + KEYSTORE_FILE_NAME;
KeyStore keyStore;
try {
String alias = getCertificateAlias();
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
fileInStream = new FileInputStream(keystoreFullPath);
// Try to load the keystore with fromAlgorithm password hash.
char[] machineOldAlgPassword = getMachinePassword(fromAlgorithm).toCharArray();
keyStore.load(fileInStream, machineOldAlgPassword);
// Save the entry to update
KeyStore.Entry entry = keyStore.getEntry(alias, new KeyStore.PasswordProtection(machineOldAlgPassword));
HandleFiles.close(fileInStream);
// If succeeded, recalculate password using toAlgorithm hash and save.
String machineNewAlgPassword = getMachinePassword(toAlgorithm);
keyStore.setEntry(alias, entry, new KeyStore.PasswordProtection(machineNewAlgPassword.toCharArray()));
FileOutputStream fileOutputStream = new FileOutputStream(keystoreFullPath);
keyStore.store(fileOutputStream, machineNewAlgPassword.toCharArray());
HandleFiles.close(fileOutputStream);
} finally {
HandleFiles.close(fileInStream);
}
}

Programmatically Obtain KeyStore from PEM

How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server in an HTTPS connection. I have confirmed that the client certificate works if I use openssl and keytool to obtain a jks file, which I load dynamically. I can even get it to work by dynamically reading in a p12 (PKCS12) file.
I'm looking into using the PEMReader class from BouncyCastle, but I can't get past some errors. I'm running the Java client with the -Djavax.net.debug=all option and Apache web server with the debug LogLevel. I'm not sure what to look for though. The Apache error log indicates:
...
OpenSSL: Write: SSLv3 read client certificate B
OpenSSL: Exit: error in SSLv3 read client certificate B
Re-negotiation handshake failed: Not accepted by client!?
The Java client program indicates:
...
main, WRITE: TLSv1 Handshake, length = 48
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
...
The client code :
public void testClientCertPEM() throws Exception {
String requestURL = "https://mydomain/authtest";
String pemPath = "C:/Users/myusername/Desktop/client.pem";
HttpsURLConnection con;
URL url = new URL(requestURL);
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getSocketFactoryFromPEM(pemPath));
con.setRequestMethod("GET");
con.setDoInput(true);
con.setDoOutput(false);
con.connect();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
con.disconnect();
}
public SSLSocketFactory getSocketFactoryFromPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
PEMReader reader = new PEMReader(new FileReader(pemPath));
X509Certificate cert = (X509Certificate) reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("alias", cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, null);
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
I noticed the server is outputing SSLv3 in the log while the client is TLSv1. If I add the system property -Dhttps.protocols=SSLv3 then the client will use SSLv3 as well, but I get the same error message. I've also tried adding -Dsun.security.ssl.allowUnsafeRenegotiation=true with no change in outcome.
I've googled around and the usual answer for this question is to just use openssl and keytool first. In my case I need to read the PEM directly on the fly. I'm actually porting a C++ program that already does this, and frankly, I'm very surprised how difficult it is to do this in Java. The C++ code:
curlpp::Easy request;
...
request.setOpt(new Options::Url(myurl));
request.setOpt(new Options::SslVerifyPeer(false));
request.setOpt(new Options::SslCertType("PEM"));
request.setOpt(new Options::SslCert(cert));
request.perform();
I figured it out. The problem is that the X509Certificate by itself isn't sufficient. I needed to put the private key into the dynamically generated keystore as well. It doesn't seem that BouncyCastle PEMReader can handle a PEM file with both cert and private key all in one go, but it can handle each piece separately. I can read the PEM into memory myself and break it into two separate streams and then feed each one to a separate PEMReader. Since I know that the PEM files I'm dealing with will have the cert first and the private key second I can simplify the code at the cost of robustness. I also know that the END CERTIFICATE delimiter will always be surrounded with five hyphens. The implementation that works for me is:
protected static SSLSocketFactory getSocketFactoryPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
byte[] certAndKey = fileToBytes(new File(pemPath));
String delimiter = "-----END CERTIFICATE-----";
String[] tokens = new String(certAndKey).split(delimiter);
byte[] certBytes = tokens[0].concat(delimiter).getBytes();
byte[] keyBytes = tokens[1].getBytes();
PEMReader reader;
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certBytes)));
X509Certificate cert = (X509Certificate)reader.readObject();
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
PrivateKey key = (PrivateKey)reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] {cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, "changeit".toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
Update: It seems this can be done without BouncyCastle:
byte[] certAndKey = fileToBytes(new File(pemPath));
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
...
protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
protected static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(spec);
}
protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
Although the answer of Ryan works well I want to provide an alternative for other developers as I faced a similar challenge in the past where I also needed to handle encrypted private keys in pem format. I have created a library to simplify loading pem files and creating SSLSocketFactory or SSLContext out of it, see here: GitHub - SSLContext Kickstart I hope you like it :)
The pem files can be loaded with the following snippet:
var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");
var sslFactory = SSLFactory.builder()
.withIdentityMaterial(keyManager)
.withTrustMaterial(trustManager)
.build();
var sslContext = sslFactory.getSslContext();
var sslSocketFactory = sslFactory.getSslSocketFactory();
Coming back to your main question, with the above snippet it is not needed to create a keystore object from the pem files. It will take care of that under the covers and it will map it to a KeyManager instance.

Categories