Java access to intermediate CAs from Windows keystores? - java

I need to build a certificate chain on Windows, from an X.509 smart card cert through one or more intermediate CAs to a root CA. That's easy when the CA certs are in a JKS keystore, but I need to use the Windows keystores as well.
I can get the root CA cert from "Windows-ROOT", but I can't get to the "Intermediate Certification Authorities" keystore.
Has anyone done this?
Thanks!

The SunMSCAPI Cryptographic provider does only support two keystores: Windows-MY (personal certificate store) and Windows-ROOT (trusted authorities certificate store), thus I don't thinks it is possible to directly access to other windows certificate stores. However it may not be necessart since it seems that the Windows-MY keystore is able to build certificate chains with the certificates from other stores.
Here is a code snippet I use to test it:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null) ;
Enumeration en = ks.aliases() ;
while (en.hasMoreElements()) {
String aliasKey = (String)en.nextElement() ;
Certificate c = ks.getCertificate(aliasKey) ;
System.out.println("---> alias : " + aliasKey) ;
if (ks.isKeyEntry(aliasKey)) {
Certificate[] chain = ks.getCertificateChain(aliasKey);
System.out.println("---> chain length: " + chain.length);
for (Certificate cert: chain) {
System.out.println(cert);
}
}
If I add a single certificate with private key in the personal certificate store the chain length is 1. After adding the CA in the intermediate CA certificate store the I launch the program a second time and the chain length is now 2.
UPDATE (April, 2nd)
It is possible to programmatically add certificates in the Windows-MY and Windows-ROOT keystore with some limitations:
when adding a certificate in the Windows-ROOT the user is prompted for confirmation
all certificate added in the Windows-MY keystore is a TrustedCertificateEntry (from the keystore point of view, not the Windows point of view). The keystore seems to build the longest possible chain with all available certificates.
the certifcates with no associated private key are not visible in the Windows certificate store browser but it is possible to programmatically delete them.
Adding a certificate in a keystore is straightforward:
Certificate c = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream("C:/Users/me/Downloads/myca.crt"));
KeyStore.TrustedCertificateEntry entry = new KeyStore.TrustedCertificateEntry(c);
ks.setEntry("CA1", entry , null);

Jcs had the answer, but I want to show some pseudocode so:
// load the Windows keystore
KeyStore winKeystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
winKeystore.load(null, null);
// add the user's smart card cert to the keystore
winKeystore.setCertificateEntry(myAlias, userCertificate);
// build the cert chain! this will include intermediate CAs
Certificate[] chain = winKeystore.getCertificateChain(myAlias);
Windows cert chains aren't validated as they're built, but now you can do the usual thing of creating a CertPath and PKIXParameters and using them to validate the chain.
CertPathValidator certPathValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType());
certPathValidator.validate(certPath, params);

Related

Construct Java netty SslContext and apache SSLContexts to trust certificates from dynamic .pem files without a preloaded truststore

Question on the possibility to construct Java io.netty.handler.ssl.SslContext and org.apache.http.ssl.SSLContexts to trust certificates from multiple dynamic generated .pem files without a preloaded truststore.
In my case, I have 3 dynamically generated certificates, a root CA, an intermediate CA, another root CA, all in .pem format. (All starting with -----BEGIN CERTIFICATE----- )
I would like to create both the io.netty.handler.ssl.SslContext and org.apache.http.ssl.SSLContexts with the above generated certificates.
If possible, I would like to achieve this without creating a preloaded truststore.p12.
Probably something like keytool -import -trustcacerts -file root_ca.pem -alias root-ca -keystore truststore.p12 And have a final truststore.p12 with everything, and have the app read the final truststore.p12 might work, but this is something I would like to avoid.
Is there a way to achieve the construction of both io.netty.handler.ssl.SslContext and org.apache.http.ssl.SSLContexts without a preloaded trust store, but directly from the .pem files?
Thank you
Apache (for sure) uses the JSSE-provided TrustManager with minor tweaks. This TrustManager is actually initalized from a KeyStore object in memory that contains the certs; this KeyStore is often read in from a truststore file (or at least pseudo-file like a classloader resource), but need not be.
// prepare in standard JCA
KeyStore ks = KeyStore.getInstance("PKCS12"); // type doesn't matter as long as it's filebased
ks.load(null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int counter = 0;
for( String f : new String[]{"pem1","pem2","pem3"} ){ // or other source(s) of data
try( InputStream is = new FileInputStream(f) ){
ks.setCertificateEntry("alias"+(++counter), cf.generateCertificate(is) );
// or other aliases as long as they're unique, maybe even filenames
}
}
// now use in Apache
SSLContext ctx = SSLContexts.custom().loadTrustMaterial(ks).build();
HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)....build();
I expect more or less the same is true with netty, but I'm not familiar with it.
JCA CertificateFactory actually reads either PEM or DER, if you needed that.

What does the Certificate[] chain in the Keystore.setKeyEntry() mean and how to obtain that info from a JKS or PKCS12?

I know what a certificate chain is. In java when working with KeyStore objects we can add certificates and private keys to a keystore object.
for that we do:
KeyStore sourceKeystore = KeyStore.getInstance("jks");
try (InputStream stream = new BufferedInputStream(Files.newInputStream(sourceKeystorePath))) {
sourceKeystore.load(stream, sourceKeystorePassword);
}
KeyStore destKeystore = KeyStore.getInstance("jks");
destKeystore.load(null, destKeystorePassword);
Enumeration<String> aliasList = sourceKeystore.aliases();
while (aliasList.hasMoreElements()) {
String alias = aliasList.nextElement();
destKeystore.setCertificateEntry(alias, sourceKeystore.getCertificate(alias));
if(sourceKeystore.isKeyEntry(alias)) {
System.out.println(alias + " : is private key");
Key key = sourceKeystore.getKey(alias, "secret".toCharArray());
Certificate[] chain = new Certificate[1];
chain[0] = sourceKeystore.getCertificate(alias);
destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
}
}
try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(destKeystorePath))) {
destKeystore.store(stream, destKeystorePassword);
}
What I want to undertstand is destKeystore.setKeyEntry(). When I give a cert chain as a parameter in this can I give an array of certs like this?
[rootCert, interCert, mainCert]
[mainCert, interCert, rootCert]
[mainCert]
First Question: what does these various ways of setting the chain mean?
Second question: Also if I have a JKS file. How do I find this exact value of certificate chain and in which order the certificate chain was set for a private key in this KeyStore? basically what I mean is I want to find out what was the Certificate[] parameter passed to KeyStore.setKeyEntry() in that JKS file
First, the basics of how the certificate chain is formed.
When you initially create a key pair by any means (keytool, openssl, etc,.), it basically consists of a private key associated with its self-signed certificate, where the self-signed certificate contains the public key. And then a PKCS#10 (certificate signing request) is created out of the key-pair, which is basically some identity information about the owner of the private key + public key, put together and signed by the private key. This CSR will be sent to a Certificate Authority to get signed certificate back. The CA signs it and responds with a certificate chain. This received certificate chain is then updated to the initially created private key, replacing the old self-signed certificate. Now, we call this key pair a signed key pair, it is not self-signed any more.
Now understanding what the CA sent. Basically a certificate chain sent by the CA looks like this:
CA Certificate (self-signed)
|
|__ 2. Sub CA Certificate (signed by the above CA)
|
|__ 1. Sub-sub CA Certificate (if any) (signed by the above Sub CA)
|
|__ 0. End Entity Certificate (your certificate, signed by the above cert)
If you look at the indexes of the certificates, they tell the following:
The most important certificate is the first certificate (aka, the user/peer certificate)
The least important certificate is the last certificate (aka, the CA certificate)
In coding terminology, the first (zeroth) element of the certificate array is the user certificate, and the last element of the certificate array is the CA certificate. Which means, the matching public key that belongs to your private key can be found in the first certificate.
99% of the time you don't have to deal with the order of the certificate chain yourself. When the CA responds with a certificate chain, it is usually in the correct order. All you have to do, is to update the certificate chain to your private key.
Now the answers to your questions:
Since you are in the java world, the first order of certificates is considered backwards (incorrect). The second option is correct. The third option is correct as well, but it is advisable to always include the entire certificate chain if you have it.
In you code where you are doing:
Certificate[] chain = new Certificate[1];
chain[0] = sourceKeystore.getCertificate(alias);
destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
there is also a method available to return you the entire certificate chain that is associated with the private key getCertificateChain(). Where you can simple do:
Certificate[] chain = sourceKeystore.getCertificateChain(alias);
destKeystore.setKeyEntry(alias, key, "secret".toCharArray(), chain);
The order in which the getCertificateChain() returns the array is the way it was set in the first place.

SunMSCAPI returns no certificates

We're trying to use SunMSCAPI to retrieve a certificate from the Windows certificate store. I've created a very simple example that loads the keystore and lists the available aliases. However, the code doesn't list anything, even though I see two personal certificates in the keystore.
On my own system it works fine by the way, but on the actual application server we will be using it doesn't list anything.
Below is the code I'm using
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
Enumeration<String> aliases = ks.aliases();
System.out.println("Listing aliases " + ks.size());
while (aliases.hasMoreElements())
{
String ka = aliases.nextElement();
System.out.println(ka + " " + ks.isKeyEntry(ka));
}
And a screenshot of the certificate store on the application server. As you can see, I expected two aliases to be listed (but perhaps I'm looking in the wrong location?):
You are seeing the computer certificates, not user certificates. Windows-MY keystore only can use the personal user certificates.
You can explore the personal certificates using Manage user certificates (certmgr )from control panel instead of Manage computer certificates (certIm)

Export server trust certificate chain to Truststore under one alias

We got a server certificate chain in .p7b format from our client and we need to export to our client trust store using Java/Scala API
Their cert file contains three certificates: root, intermediate,actual server...
How can we export three of them into our trust-store under same alias ?
Is it actually required to export them under one alias ?
This is what we did so far...
//load default cacerts first in order to export the server cert
val keystore = KeyStore.getInstance(KeyStore.getDefaultType)
keystore.load(new FileInputStream(cacertsPath), decryptedPass.toCharArray)
val cf = CertificateFactory.getInstance("X.509")
//this is the server cert we are trying to export
val bais = fullStream(customTrustFile)
val certs = cf.generateCertificates(bais) --> this returns a chain of 3 certs
certs.toArray[Certificate](new Array[Certificate](certs.size())).zipWithIndex.foreach {
case (cert, i) => keystore.setCertificateEntry("api.*.*.site-" + i, cert)
// Save the new keystore contents
keystore.store(new FileOutputStream(cacertsPath),decryptedPass.toCharArray)
If you see, the way we are inserting certs, it uses three aliases with suffix -1 , -2, -3, so we end up inserting three entries into the truststore, not sure if this is the right of inserting cert chain..
Is there a way to insert the cert chain under a single alias ?
How does the client finds the matching server trust ? is it using alias ? Also does the client requires only server root cert ? or it needs all three ?
Thanks
1.Is there a way to insert the cert chain under a single alias ?
No, each trusted certificate has one alias
An alias identifies a unique trusted certificate entry, a private key entry or a secret key entry. A private key entry can also be accompanied by a certificate chain of the corresponding public key.
2 How does the client finds the matching server trust ? is it using alias ? Also does the client requires only server root cert ? or it needs all three ?
You only need to import the root certificate into the truststore. The alias is not needed
The client during a connection will receive the server certificate and the certification chain (without the root). It will try to match the last certificate of the chain, from leaf to upper, with some of the truststore's certificates. This is done verifying that the signature of the certificate corresponds with the public key of the root certificate

Verify non-SSL certificate against default system keystore in Java

I have a X509 certificate (java.security.cert.X509Certificate) and I need to verify it. The trusted anchor is already in system default certstore ( jre\lib\security\cacerts ) but I could populate it into something else if needed. The certificate is not a SSL certificate.
My first idea was to use Java's trust manager as I am already using HTTPs with server certificates issued by the same CA. But it seems that this can be used only for SSL certifictaes as it provides only checkServerTrusted and checkClientTrusted. For sake of completness, this is my attempt:
X509TrustManager x509TrustManager=null;
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore)null);
for (TrustManager trustManager : trustManagerFactory.getTrustManagers())
{
if (trustManager instanceof X509TrustManager)
{
x509TrustManager = (X509TrustManager)trustManager;
}
}
java.security.cert.X509Certificate x509 = .... get certificate ...
x509TrustManager.checkServerTrusted(new java.security.cert.X509Certificate[]{x509}, "RSA");
This does all I need but in addition it checks whether the certificate is valid for SSL server (or client if I use checkClientTrusted) and that obviously fails.
Is there any simple way how to specify the cert and/or key usage for the trust manager or at least a way how to disable the check (I can do it myself).
In the future I would like to use OCSP or CRL list to check the actual state of the certificate.

Categories

Resources