Reputation: 61
I am trying to create a PKCS12 keystore file using Java API. However as soon as I try to import the certificate I get the exception
java.security.KeyStoreException: TrustedCertEntry not supported
my code is:
Provider p = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
...
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, keystorePass);
keyStore.setCertificateEntry("certificate", certificate);
keyStore.setKeyEntry("key",privateKey, keypass, certChain);
The same approach works for creating JKS files but failed for PKCS12 files.
Note: The certificate given to this program as input is created by the server using the CSR generated with the same private key used here. I.e. the public modulus for the given certificate, CSR used to generate it and the given private key are the same.
The server cert is stored in variable certChain.
Note: I have tried OpenSSL to create the pkcs12 and I was successful, however I need to do the same using Java API.
Note: I am using JDK 7
Upvotes: 4
Views: 13671
Reputation: 754
As Dave Thompson reported, we cannot use Java 7 (and earlier) as it does not allow a trustedCert entry in a PKCS12.
If you are still stuck on using Java 7 (or earlier), then the following will help (note: I used BouncyCastle to get TLS1.2 support):
X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(cert.getBytes()));
KeyStore ts = KeyStore.getInstance("JKS");
KeyStore ks = KeyStore.getInstance("PKCS12");
ts.load(null, null);
ks.load(null, null);
ts.setCertificateEntry("certAlias", ca);
PrivateKey privateKey = null;
PemReader pemReader = new PemReader(
new InputStreamReader(new ByteArrayInputStream(key.getBytes())));
try {
PemObject pemObject = pemReader.readPemObject();
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
byte[] content = pemObject.getContent();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
privateKey = factory.generatePrivate(privKeySpec);
} finally {
pemReader.close();
}
ks.setKeyEntry("keyAlias", privateKey, keyPassword, new X509Certificate[] { ca });
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keyPassword);
SSLContext sslContext = SSLContext.getInstance("TLS", BouncyCastleJsseProvider.PROVIDER_NAME);
sslContext.init(kmf.getKeyManagers(), trustManagers,
SecureRandom.getInstance("DEFAULT", BouncyCastleProvider.PROVIDER_NAME));
Note: in the aforementioned code: Variable cert is the String contents of the certificate which begins with: -----BEGIN CERTIFICATE-----\n
Variable key is the String contents of the pem-based private key which begins with: -----BEGIN PRIVATE KEY-----\n
Variable keyPassword is the String contents of the password for the keystore.
Here are some of the java imports of the aforementioned code to provide clarity:
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
Upvotes: 0
Reputation: 38771
Java 7 (and earlier) does not allow a trustedCert
entry in a PKCS12 keystore although 8 does, perhaps because PKCS12 was designed and is usually used only for privatekey(s) and the related cert(s) and which Java puts together in the privateKey
entry. You say this cert is the cert for/matching the privatekey, so it must be first in the certChain in the "key" entry, and you do not need a "cert" entry for it.
Upvotes: 9