I'm getting this error when I try to create a JKS file, write it to disk, then run keytool to convert it to a P12. The reason I'm going this route is because I cannot get a P12 that works for iOS in code (not a crypto person). There was enough code out there to create a JKS. To create my end credential, I'm doing this:
public X509Certificate buildEndEntityCert(PublicKey entityKey, PrivateKey caKey, X509Certificate caCert, String clientName)
throws Exception {
String name = "CN=" + clientName;
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
caCert.getSubjectX500Principal(),
BigInteger.ONE,
new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis() + VALIDITY_PERIOD),
new X500Principal(name),
entityKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert))
.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(entityKey))
.addExtension(Extension.basicConstraints, false, new BasicConstraints(false))
.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.nonRepudiation))
.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth));
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(caKey);
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));
}
I call that method and create the JKS like this:
KeyPair endPair = generateRSAKeyPair(2048);
X509Certificate endCert = buildEndEntityCert(endPair.getPublic(), intermediateCredential.getPrivateKey(), intermediateCredential.getCertificate(), clientName); // intermediateCredential and rootCredential are properties of this class that get loaded when the app starts up
X500PrivateCredential endCredential = new X500PrivateCredential(endCert, endPair.getPrivate(), clientName);
KeyStore store = KeyStore.getInstance("JKS");
store.load(null, null);
store.setKeyEntry(clientName, endCredential.getPrivateKey(), "secret".toCharArray(),
new Certificate[]{
endCredential.getCertificate(),
intermediateCredential.getCertificate(),
rootCredential.getCertificate()
});
store.store(new FileOutputStream(clientName + ".jks"), "secret".toCharArray());
Then when I run keytool from ProcessBuilder:
"C:\\Program Files\\Java\\jdk1.7.0_80\\bin\\keytool",
"-importkeystore",
"-srckeystore",
clientName + ".jks",
"-destkeystore",
clientName + ".p12",
"-srcstoretype", "JKS",
"-deststoretype", "PKCS12",
"-deststorepass",
clientName,
"-srcalias",
clientName,
"-destalias",
clientName
I get:
Problem importing entry for alias CLIENT_NAME: java.security.KeyStoreException: Key protection algorithm not found: java.security.KeyStoreException: Certificate chain is not validate.
I tried searching for this but did not find much info. What does this mean or am I am doing something wrong?
I have the same exception, trying to programatically fill a key store.
I tracked the problem down to sun.security.pkcs12.PKCS12KeyStore
In method setKeyEntry
if (chain != null) {
// validate cert-chain
if ((chain.length > 1) && (!validateChain(chain)))
throw new KeyStoreException("Certificate chain is " +
"not valid");
Method validateChain
private boolean validateChain(Certificate[] certChain)
{
for (int i = 0; i < certChain.length-1; i++) {
X500Principal issuerDN =
((X509Certificate)certChain[i]).getIssuerX500Principal();
X500Principal subjectDN =
((X509Certificate)certChain[i+1]).getSubjectX500Principal();
if (!(issuerDN.equals(subjectDN)))
return false;
}
// Check for loops in the chain. If there are repeated certs,
// the Set of certs in the chain will contain fewer certs than
// the chain
Set<Certificate> set = new HashSet<>(Arrays.asList(certChain));
return set.size() == certChain.length;
}
So check for intermediateCredential and rootCredential.
I would like you to the following steps (suppose the name of the jks file is abc.jks)
Paste the abc.jks file in the bin folder of java where keytool.exe file is present (e.g. C:\Program Files (x86)\Java\jre1.8.0_121\bin)
Run this command keytool -keystore abc.jks -list -v and enter the password
Now you will need to check the following content in it
A. Entry type (It should be private, if it is not a then you don't have the key)
B. Certificate chain (It should be 3, if it is one then you need to import the CA certificates)
you can confirm the same in the image
You can use the this command to convert the jks to pfx keytool -importkeystore -srckeystore abc.jks -srcstoretype jks -destkeystore xyz.pfx -deststoretype pkcs12 (I will recommend to paste the abc.jks in the bin folder of java) at the end you need to change the extension to P12
I am developing a client GUI that accepts self signed server certificates and adds them to the trust store just like any browser would do.
The problem is that my client application asks for the certificate every time it is started, in other words it does not remember that the certificate is already in the trust store. How do I implement this?
This is how I am writing my trust store files:
public void WriteTrustStore(String alias, X509Certificate c){
char[] password = "changeit".toCharArray();
char SEP = File.separatorChar;
keystoreFile = new File(System.getProperty("java.home")
+ SEP + "lib" + SEP + "security" + SEP + "cacerts");
try {
setTrustStore(trustStore);
FileInputStream in = new FileInputStream(keystoreFile);
trustStore.load(in, password);
in.close();
trustStore.setCertificateEntry(alias, c);
FileOutputStream out = new FileOutputStream(keystoreFile);
trustStore.store(out, password);
out.close();
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
e.printStackTrace();
}
}
Then I have another method where I am initializing my SSL Context and also creating dynamic alias names by doing something like:
string alias = getHostname() + "-" + getPortname();
In the end I have an alias name like:
"myhost-5001"
And then I call the WriteTrustStore(alias,certificate) method.
But in the next execution run of the program if I try to find the certificate with this alias name, I always end up getting a Null Pointer Exception.
I know that a truststore file has a property like:
trustStore.containsAlias(alias)
I tried,
if(trustStore.containsAlias(alias) == false){
WriteTrustStore(alias, (X509Certificate) cert)
}
else {
System.out.Println("Certificate already in trust store!");
}
But still I get a Null-Pointer exception. And also I know that the certificate with alias name myhost-5001 is in the Java trust store, I crossed checked using keytool and portecle.
Thanks for your help!
I figured it out, there are two ways this can be done.
First Method
I found this here: Check for trusted certificates, where you enumerate over aliases like this:
Enumeration en = keystore.aliases();
String ALIAS = "" ;
X509Certificate signingcert = null;
while (en.hasMoreElements())
{
X509Certificate storecert = null;
String ali = (String)en.nextElement() ;
if(keystore.isCertificateEntry(ali))
{
storecert = (X509Certificate)keystore.getCertificate(ali);
if( (storecert.getIssuerDN().getName()).equals(issuerdn))
{
try{
System.out.println("Found matching issuer DN cert in keystore:\r\nChecking signature on cert ...") ;
cert.verify(storecert.getPublicKey()) ;
System.out.println("Signature verified on certificate") ;
signingcert = storecert;
break;
}
catch(Exception exc){
System.out.println("Failed to verify signature on certificate with matching cert DN");
}
}
}
else
if(keystore.isKeyEntry(ali))
System.out.println(ali + " **** key entry ****");
}
Second Method
Just create a duplicate certificate that looks in the trust store for a certificate with the alias name you are passing.
X509Certificate DuplicateCert = (X509Certificate) trustStore.getCertificate(alias);
First method is safer because you are also looking at the Issuer DN but takes longer, second method is simple and shorter.
Second method works like a charm for me, you can find the complete GUI code review here and see how I am using it: JAX-WS client SSL code review
So my problem is as follows,
Basically I want to create a certificate chain using bouncy castle (jdk16 version 1.46). I am rather new to bouncy castle and java.security in general so if my approach might be completely wrong, but anyway this is what I did:
So far I am able to create a self signed certificate which I use as the root certificate. This is done using the following code:
//-----create CA certificate with key
KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null);
This basically creates the keypair, the two null options are for a provider and a secure random, if needed.
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true)));
//------this creates the self signed certificate
X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap);
This will create the a certificate with the provided attributes.
serial = simply the current time in milliseconds
start = same as serial basically (may have 1 or 2 milliseconds difference)
end = start + 2 days
The map simply adds the basic contraint to set the certificate to be a CA. I use a map here since I want to be able to add additional X509Extensions if need be.
//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
This will store the certificate and private key in a pem file using the bouncy caste pem writer.
After that the file is generated and I can install the file as well (I use IE and then install it via the Internet Options as a trusted CA. The certificate is also shown to be valid).
After that I create the intermediate certificate, using the following code (note the above code is in the same scope so those variables are available as well)
KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert)));
intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic())));
X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap);
//-----save intermediate certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter);
The procedure is bascially the same, however I add additional X509Extensions:
X509Extensions.AuthorityKeyIdentifier = sets the CA certificate as the intermediates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
furthermore the CA is used as the issuer and the CA private key is used to create the intermediate certificate.
This also works and I can install the intermediate certificate (using IE again), it is also shown that the parent certififcate is the generated CA certificate and that the certificate is valid.
Now comes the tricky part where I am making a mistake I guess. I now create a new certificate using the intermediate certificate, using the following code.
KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null);
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert)));
endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic())));
X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap);
X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter);
Essentially it is the same as creating the intermediate certificate. However I now use the following X509Extension settings:
X509Extensions.AuthorityKeyIdentifier = sets the intermediate certificate as the certificates parent
X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate
Also the intermediate certificate is used as the issuer and its private key is used to create the certificate.
I can also install the new certificate but when I examine if (again IE), it shows that the certificate is however invalid because "This CA is either not entitled to issue certificates or the certificate can not be used as an end-entity."
So I somehow need to enable the intermediate certificate to be able to create new certificates as well, by adding some KeyUsages/ExtendedKeyUsage I assume.
Does someone know how I enable the intermediate certificate to do what I need it to do or if I do something wrong in general ?
EDIT 1:
So okay I forgot to provide the code for the method which created the certificate and the one that saved it in PEM format (I renamed it to savePemX509Certificate since the old one was misguiding).
Code for the certificate generation:
public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException
{
if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null)
{
//-----GENERATE THE X509 CERTIFICATE
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X509Principal dnSubject = new X509Principal(subject);
X509Principal dnIssuer = new X509Principal(issuer);
certGen.setSerialNumber(serialnumber);
certGen.setSubjectDN(dnSubject);
certGen.setIssuerDN(dnIssuer);
certGen.setNotBefore(start);
certGen.setNotAfter(end);
certGen.setPublicKey(publicKey);
certGen.setSignatureAlgorithm(signAlgorithm);
//-----insert extension if needed
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return certGen.generate(privateKey, provider);
}
return null;
}
Code for the saveing of the certificate and key:
public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException
{
if(cert!=null && key!=null && writer!=null)
{
PEMWriter pemWriter = new PEMWriter(writer);
pemWriter.writeObject(cert);
pemWriter.flush();
if(key!=null)
{
pemWriter.writeObject(key);
pemWriter.flush();
}
pemWriter.close();
return true;
}
return false;
}
As you can see I basically put the certificate and the key in the file, thats all. The result is the following and seems good to me.
-----BEGIN CERTIFICATE-----
MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew
HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC
AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC
Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP
9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U
NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ
BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ
q3CmnvC+cfT2VQ==
-----END CERTIFICATE-----
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM
ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH
xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt
NWB4LWYc4y4QvJ/l46ap
-----END DSA PRIVATE KEY-----
So after gtrig provided me with the correct way to create the certificate, I ended up using this method to create either a normal or self signed (if the private key is from the same keyPair as the public key that is) certificate
public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException
{
if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null)
{
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey);
X509v3CertificateBuilder certBldr = null;
if(parentCert==null)
certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey);
else
certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey);
if(map!=null)
for(ASN1ObjectIdentifier extension : map.keySet())
certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));
}
return null;
}
Something looks wrong with the way you're creating the PEM files. You're using a method called, generateSelfSignedPemX509Certificate, but you don't really want a self-signed certificate, you want an end certificate signed by the intermediate private key, and you want an intermediate certificate signed by the CA private key.
Also, you need basic constraints and key usage extensions on your certificates.
For creating certificates signed by other entities (non-self-signed), I use these methods from Bouncy Castle to create an "end" certificate.
ASN1Sequence seq=
(ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject();
SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq);
ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey);
X509v3CertificateBuilder certBldr =
new JcaX509v3CertificateBuilder(
parentCert,
serialNum,
startDate,
endDate,
distName,
pubKey)
.addExtension(
new ASN1ObjectIdentifier("2.5.29.35"),
false,
new AuthorityKeyIdentifier(parentPubKeyInfo))
.addExtension(
new ASN1ObjectIdentifier("2.5.29.19"),
false,
new BasicConstraints(false)) // true if it is allowed to sign other certs
.addExtension(
new ASN1ObjectIdentifier("2.5.29.15"),
true,
new X509KeyUsage(
X509KeyUsage.digitalSignature |
X509KeyUsage.nonRepudiation |
X509KeyUsage.keyEncipherment |
X509KeyUsage.dataEncipherment));
// Build/sign the certificate.
X509CertificateHolder certHolder = certBldr.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
.getCertificate(certHolder);
For a CA or intermediate certificate, you'll need to add a SubjectKeyIdentifier extension. Also, BasicConstraints should be true, and KeyUsage should be:
new X509KeyUsage(
X509KeyUsage.keyCertSign|
X509KeyUsage.cRLSign));
I am inserting a client certificate into my servertruststore using following code
FileInputStream fileInputStream = new FileInputStream( "c:/server.jks" );
keyStore.load( fileInputStream, "keystore".toCharArray() );
fileInputStream.close();
keyStore.setCertificateEntry( alias, new X509Certificate( trustedCertificate ) );
FileOutputStream fileOutputStream = new FileOutputStream("c:/server.jks" );
keyStore.store( fileOutputStream, "keystore".toCharArray() );
fileOutputStream.close();
Now i see that certificate is entered into my truststore but the CA's certificate which signed client's certificate is not present in my truststore. So I want to know is there any way we can check whether the certificate of CA is available or not before entering a certificate into keystore?
I guess what you have to do is to verify if the certificate has been issued by a root authority or it has been self-signed. I presume you are using the default java keystore which is cacerts.
I haven't tested the code but I think this may be a solution to your problem:
Code taken and modified from the following link:
How can I get a list of trusted root certificates in Java?
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
Set<X509Certificate> additionalCerts = new HashSet<X509Certificate>();
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
// This class retrieves the most-trusted CAs from the keystore
PKIXParameters params = new PKIXParameters(keystore);
// Get the set of trust anchors, which contain the most-trusted CA certificates
Iterator it = params.getTrustAnchors().iterator();
while( it.hasNext() ) {
TrustAnchor ta = (TrustAnchor)it.next();
// Get certificate
X509Certificate cert = ta.getTrustedCert();
additionalCerts.add(cert);
}
Then you may use the following code to pass the client certificate and the Set containing all the root CAs to the verifyCertificate(X509Certificate cert, Set additionalCerts) method of the following code:
http://www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/
Ok, I exported all the certs in my browser by doing this: Tools, Options..., Advanced, Encryption, View Certificates, Your Certificates, Backup All... (this is in Firefox).
There are 4 certs in the list of Certificates, two are under one Name and have distinct serial numbers, and the other two are under a different name and have two other distinct serial numbers. So, in summary, there are four certs, two pair have the same name but distinct serial numbers.
If I were to import this p12 file into another browser on another machine I get all four certs (as expected).
-- BUT --
When I open the p12 file with the java.security.* package and look at the size(), it shows only two certs in the p12 file. When I loop through the aliases I see only two certs. Is there something in the KeyStore object that allows me access to all four certs? It's tough because the aliases are the same for the two pairs, only the serial numbers are different. Thanks in advance for any help you can provide.
Ok, to answer my own ancient question... I learned that Java is not that good at reading p12 files. It creates a hashmap using the alias of each certificate as the key so if there are two certs with the same alias, Java will clobber the first cert with the second cert with the same alias (key), rsulting in only one cert per alias.
When importing the certs into a browser, the browser takes all the entries in the p12 file (not caring about the aliases).
The way I worked around this was to use Java runtime exec functionality to call openssl and pipe the output of each cert into a String and using that string to create an X509Certificate. Here's some sample code (I cannot copy and paste as my dev box is not internet connected):
private ArrayList<X509Certificate> parseCerts( String fileName, String pwd ) {
ArrayList certsFromP12File = new ArrayList();
String cmdLine = "/usr/bin/openssl pkcs12 -info -in " + fileName + " -clcerts -nokeys -passin pass:" + pwd;
String line;
Process p = Runtime.getRuntime().exec( cmdLine );
BufferedReader input = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
boolean readingCert = false;
boolean gotCertToProcess = false;
String certString;
while ((line=input.readLine()) != null ) {
if ( line.contains("-----BEGIN CERTIFICATE-----") ) {
readingCert = true;
}
if ( readingCert ) {
certString += line + System.getProperty("line.separator");
}
if ( line.contains("-----END CERTIFICATE-----") ) {
readingCert = false;
getCertToProcess = true;
}
if ( gotCertToProcess ) {
X509Certificate cert = null;
byte[] cert_bytes = certString.getBytes();
ByteArrayInputStream certInputStream = new ByteArrayInputStream(cert_bytes);
cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate( certInputStream );
certsFromP12File.add( cert );
gotCertToProcess = false;
certString = "";
}
}
input.close();
return certsfromP12File;
}
Hope that helps others. :)