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
Related
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.
I have an SSL socket server running with a 2-side handshake required (for which I'm using self signed certificates). After the handshake was successful, I would like to check the client certificate's cn on the server side. Unfortunately this field is set to Unknown.
Here is the code I used to determine the cn field value:
((SSLSocket) socket).addHandshakeCompletedListener(new HandshakeCompletedListener() {
#Override
public void handshakeCompleted(HandshakeCompletedEvent hce) {
X509Certificate cert = (X509Certificate)hce.getLocalCertificates()[0];
String certName = cert.getSubjectX500Principal().getName().substring(3,cert.getSubjectX500Principal().getName().indexOf(","));
System.out.println(certName);
}
});
Which prints Unknown
Aditionally, I checked the client's keyStore using this command:
keytool -list -v -keystore clientStore.jks
Which prints
Keystore-type: JKS
Keystore-provider: SUN
Keystore contains 1 entry
Aliasname: test
creation date: 23.04.2018
entry type: PrivateKeyEntry
certificate length: 1
certificate[1]:
owner: CN=test, OU="Org Unit", O=Org, L=City, ST=State, C=DE
...
As you can see, the client store's certificate's cn is set. However it is inexplicable to me why it then seems not to be transmitted to the server.
I would be glad for every kind of help.
Best regards,
Galveston01
After the handshake was successful, I would like to check the client
certificate's cn on the server side.
To check what certificates you have received you need to call getPeerCertificates instead of getLocalCertificates, which is for the certificates you sent.
And you should read carefully the doc :
public X500Principal getSubjectX500Principal()
Returns the subject (subject distinguished name) value from the
certificate as an X500Principal. If the subject value is empty, then
the getName() method of the returned X500Principal object returns an
empty string ("").
For this reason it's not recommended to call indexOf() substring() without checking first the input.
I have a problem when do business with AppleWallet.
They offer me three certificates:
Leaf Certificate in String format;
Sub Certificate in String format;
Apple Root CA - G3 Cert file, ends with ".cer"
My problem: how do I verify and encode the data using RSA algorithm?
PS:The document makes clear that a PublicKey is provided by the Leaf Certificate. And the three certificate is in chain. Leaf Certificate is signed by Sub Certificate, and Sub Certificate is signed by AppleRootCA-G3.cer.
I need to do two things:
Verify the three certificates.
Extract the RSA PublicKey from the Leaf Certificate.
I have no idea how to do this.
What you basically have to do is construct a certificate chain if you didn't get it as a chain. A certificate chain basically consists of the end entity certificate (also, leaf certificate, the most important certificate in the chain) at the zeroth position and followed by the lesser important certificates. The CA Certificate being the least important.
So this is how the usual X.509 certificate chain looks like:
3. 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)
When you are constructing a certificate chain yourself from each certificate, you have, you have to see which certificate is signed by whom and then construct the chain in the above manner (the numbers in the tree indicate the index in the java's certificate array).
You can find which certificate is signed by whom by looking at the SubjectDN and IssuerDN. The Subject distinguished name is the end entity, and the Issuer distinguished name is the name of the entity who signed your certificate.
If you need to verify if a certificate is signed by another certificate programmatically, you could do this:
userCert.verify(caCert.getPublicKey());
You can extract a public key from a certificate using this code snippet:
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// Provided the certificate doesn't have certificate headers (---begin cert--- and ---end cert---)
Certificate cert = certificateFactory.generateCertificate(new FileInputStream(new File("CERTIFICATE_PATH")));
System.out.println(cert.getPublicKey());
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);