neutral_sphere
neutral_sphere

Reputation: 61

Creating pkcs12 using Java API failes due to error: java.security.KeyStoreException: TrustedCertEntry not supported

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

Answers (2)

dulon
dulon

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

dave_thompson_085
dave_thompson_085

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

Related Questions