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)
Related
long story short - I need to validate XML signature, and one of the steps is to validate that PUBLIC certificate which was used to sign XML is issued by one of the knows roots.
This question is exactly the same as mine: How to check if public X509Certificate is trusted - but the problem with the answer is that I do not want to "hardcode" any trust store files and passwords like they do:
final File file = new File("C:\\TMP\\jssecacerts");
InputStream inStream = new FileInputStream(file);
KeyStore keystore = KeyStore.getInstance("JKS");
//String password = "";
//keystore.load(inStream, password.toCharArray());
keystore.load(inStream, null);
I was hoping I can reuse the default, built-in jre/lib/security/cacerts file which is shipped with every JAVA installation. So I tried
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
but it comes up empty.
The questions are as follows:
Please confirm JAVA loads jre/lib/security/cacerts file into memory every time I run a JAVA program. I find it hard to believe it does not - otherwise how all HTTPS communications would work?
How do I use this file to validate certificates? Or to simplify - how can I get access to trusted certificates KeyStore so I can apply validation logic?
Note
I don't want to import a certificate into a keystore with keytool. What I want, is to add a java.security.cert.Certificate object into a java.security.KeyStore object.
Scenario
My apps works with FireFox user certificates and those in a smartcard, with NSS + JSS. The APP first loads all user certificates via NSS and stores them into a KeyStore object, and stores their SN, DN, etc. Then, with org.mozilla.jss.CryptoManager I load all external modules, then every token of the modules, at last, with this line, I get a CryptoStore with all certificates of the token:
CryptoStore store = null;
store = token.getCryptoStore();
org.mozilla.jss.crypto.X509Certificate[] certs = store.getCertificates();
Then, I read these certificates and store their SN, DN, etc., along with those data of user certificates, for UI part to show.
When doing an authentication, the user is prompted a dialog with all certificates to choose. This window has a table with rows containing alias of each certificate, users' + card. Then I pick the certificate from the keyStore with the selected alias.
Error
Here comes the problem: the keyStore only has entries of user certificates from FireFox, not those in the card. If I pick one from the card, and do this:
certificate = keyStore.getCertificate(alias);
It returns null. It makes sense because those from the card are stored in CryptoStore. Now, if I do this:
keyStore.setCertificateEntry(alias, cert); //cert is a certificate from card
no exception occurs. Then, when I do this again:
certificate = keyStore.getCertificate(alias);
A NullPointerException from sun.security.pkcs11.p11keyStore.P11KeyStore.getID() happens.
Question
Adding a certificate from a smartcard to a java.security.KeyStore is possible with my approach here?
Why the exception?
I want to retrieve certificate with password from personal my store by java programming.
I found some code of retrieving certificate but it shows all certificates. These certificates shown data didn't need to open with these related password.
I do not want to these style of showing certificate. I want to write the code format type is- choose certificate I want and I add password of this certificate on the browser and then show of this certificate information.
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);
X509Certificate Cert = null;
for (Certificate cert: chain) {
System.out.println(cert);
}
}
}
How to repair this code? And I found some C# code for accessing certificate. I wanna also use just like this by java program. How to convert the following C# code to java code?
Access certificate by C#
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindBySerialNumber, "{serial number no space}", true);
//service is the webservice that need to //be authenticated using X509 certificate
TestWebService service = new TestWebService();
//Note, we should find the certificate from the the
//root certificate store on local machine if the
//certificate is imported correctly and the serial
//number is correct
if (col.Count == 1)
{
//all we need to do is to add the certificate
//after that we can use the webservice as usual
service.ClientCertificates.Add(col[0]);
service.Test();
}
The password is not certificate specific. The password is for the keyestore. Its similar to the database where in the password is for a schema and not individual tables.
To answer other question of retrieving on a single certificate, for that you would need to know the alias beforehand and use that alias to retrieve the certificate.
in your code it would be ks.getCertifcate("alias")
I am working on an older IBM iSeries (IBM-i, i5OS, AS/400, etc), with a Java 5 JVM (Classic, not ITJ J9) on O/S version V5R3M0.
Here is the scenario in a nutshell:
I created a key-store of type JKS using Portecle 1.7 (Note: I did try converting my key-store to JCEKS but that was rejected as an unsupported format, so it appears that JKS is the only option with the iSeries machine (at least the version I am on).
I then created a key-pair and CSR and sent the CSR to Thawte to be signed.
I imported the signed certificate from Thawte successfully using the PKCS#7 format to import the entire certificate chain, which included my certificate, the Thawte intermediary and the Thawte server root.
This all worked as expected.
However, when I ran up the JVM, configured properly to point to the store and supply it's password (which I have done in the past with self-signed certificates created in Portecle for testing), and try to start my web server on 443, I get the following security exception:
java.security.KeyStoreException: Cannot store non-PrivateKeys
Can anyone tell me where I went wrong, or what I should check next?
The "Cannot store non-PrivateKeys" error message usually indicates you are trying to use secret symmetric keys with a JKS keystore type. The JKS keystore type only supports asymmetric (public/private) keys. You would have to create a new keystore of type JCEKS to support secret keys.
As it turns out, this was a subtle problem, and it's worth giving the answer here in case someone else has something similar.
The TLDR answer is that I did not check that my key and certificate were not null and as a result attempted to add a null key and certificate to a key-store. The longer answer follows.
The way we have our web server set up to use SSL, specifically to support our user's typical configuration where the IP address is used to configure the web site listen address rather than a DNS name, is that it locates the certificate in the master key-store using the alias, and creates an ephemeral key-store containing just the certificate for that web site, using that key-store to configure an SSL context and an SSL socket factory, like so:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}
Notice that it uses a blank password for extracting the private key and certificates from the master key-store. That was my problem - I had, out of habit from using keytool, created the private key-pair with a password (the same password as the key-store).
Because I had a password on the certificate, the key and certificate were not extracted, and null was passed to sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn); However, setKeyEntry checks the passed Key using instanceof and concludes (correctly) that null is not an instanceof PrivateKey, resulting in the misleading error I was seeing.
The corrected code checks that a key and certificate are found and sends appropriate errors:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
if(ctfkey==null) {
throw new IOException("Cannot create server socket factory: No key found for alias '"+svrctfals+"'");
}
if(ctfchn==null || ctfchn.length==0) {
throw new IOException("Cannot create server socket factory: No certificate found for alias '"+svrctfals+"'");
}
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}
Instead of using an ephemeral keystore, you could handle everything within a single SSLContext.
You would need to initialise your SSLContext using an custom X509KeyManager instead of using the one given by the default KeyManagerFactory. In this X509KeyManager,chooseServerAlias(String keyType, Principal[] issuers, Socket socket) should return a different alias depending on the local address obtained from the socket.
This way, you wouldn't have to worry about copying the private key from one keystore to another, and this would even work for keystore types from which you can't extract (and thus copy) but only use the private key, e.g. PKCS#11.
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);