I'm facing a problem right now and I can't understand why I can't read a KeyStore on Java 6. The piece code is like this:
KeyStore ks = KeyStore.getInstance("jks");
FileInputStream file = new FileInputStream(<path to file>);
ks.load(file, <password>);
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, ConstantsUtils.CERT_PASS.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
I'm using this certificate to sign a PDF from Java code, but variables key and chain stay null, so the keystore can't retreive my private key.
The Keystore was created by keytool importing a certificate with the next line (Windows 7):
C:\Program Files\Java\jdk1.6.0_37\bin>keytool -importcert -file "<path to cert>" -keystore <path to keystore -alias "<alias>" -keypass <password> -storepass <password>
Imported certificate is from a third party, and should work properly. I don't know if is something wrong importing the certificate or if I'm coding something in a bad way.
You're importing a certificate. Unless it corresponds to a private key already in the KeyStore, and you don't agree to the 'trust CA certs?' prompt, this will create a trusted certificate, and looking for it via a PrivateKey will fail.
And if you're importing a certificate from a third party, you won't have their private key, unless they are spectacularly incompetent.
In short what you're doing doesn't make sense.
Well, finally I found the ultimate solution.
My problem was that I had 2 files, one a certificate (we'll call it certificate.crt) and the other one was the private key (we'll call it private_key.pem). As EJP said previously the problem was around the private key import process. I had 2 different files so I was importing just certificate.crt file into a .jks file, and private_key.pem gave me an error when I tried to import (obviously). So my solution was to merge both files (certificate.crt and private_key.pem) into one PKCS#12 file with this command:
openssl pkcs12 -inkey private_key.pem -in certificate.cert -export -out certificate.pfx
Now, the new certificate.pfx is a keystore which contains public and private key, correctly formated so, with some changes in my code it was possible to obtain the private key and sign my document:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream file = new FileInputStream(<path to .pfx file>);
ks.load(file, <password>);
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, ConstantsUtils.CERT_PASS.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
So thats all. Thank you EJP, I was missunderstanding you, and you made me think in the right way. I was facing this four days so I'm pretty hyped up right now.
Related
I bought an SSL certificate in GoDaddy. I need to use it to start my Spark Java self-contained server through a secure connection. According to the documentation in http://sparkjava.com/documentation#examples-and-faq, I need to do the following:
String keyStoreLocation = "deploy/keystore.jks";
String keyStorePassword = "password";
secure(keyStoreLocation, keyStorePassword, null, null);
But when I download the certificate from GoDaddy I got the files:
11111.pem
11111.crt
bundle-g2-g1.crt
What do I need to do to convert these files is something compatible to use as the first parameter of secure(keyStoreLocation, keyStorePassword, null, null);?
IF the 1111.pem file is your private key (check the first line is 5 hyphens, BEGIN, optionally a word like RSA EC or ENCRYPTED, PRIVATE KEY, and 5 hyphens) then start with
openssl pkcs12 -export -in 1111.crt -inkey 1111.pem -certfile bundle-g2-g1.crt -out my.p12
Nearly all java programs since 2018 can actually use a PKCS12 instead of JKS for a keystore, but if this code really does need a JKS then do
keytool -importkeystore -srckeystore my.p12 -destkeystore my.jks -deststoretype jks
# if using very old Java (below 8u40 or so) add -srcstoretype pkcs12
Mostly dupe (but somewhat updated from)
Combined .pem certificate to truststore/keystore.jsk
convert certificate from pem into jks
How do I generate X.509 certificate from key generated by openssl and more linked there
https://serverfault.com/questions/483465/import-of-pem-certificate-chain-and-key-to-java-keystore
I have a blah.p7b certificate type PKCS#7 which i want to import it to a java keystore using keytool in order to enable HTTPS on tomcat , i don't have the alias name and keystore when the certificate was generated i took it from the client whose want to enable https on our web-application server that they use, can this works without having the original alias name and keystore ?
when i tried to import the certificate i used this command
keytool -import -trustcacerts -file certificate.p7b -keystore keystore -storepass <mypasswd> -alias "myalias"
but it gives me this error
keytool error: java.lang.Exception: Certificate reply does not contain public key for <mydomain>
Please help...
If you haven't got the original KeyStore you are hosed. You have to generate a new KeyStore, a new keypair, a new CSR, get it signed, and then import the signed cert and its chain into the KeyStore using the same alias as the keypair.
I am installing a wild Card SSL certificate to my keystore which will be used for Apache Tomcat web server.
Description :
My Tomcat Server is installed on windows 2012 server.
And I have certificates provided from COMODO.
The wildcard cert I'm using has already been used previously on a few servers. so I am directly installing same on my apache tomcat server .
so what I've generated a public keystore using keytool providing the same information used while purchasing the certificate using following tool command.
keytool -keysize 2048 -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore
Then I have attached my certificates to the generated keystore using following commond
For "Comodo" certificates
i.keytool -import -trustcacerts -alias root -file AddTrustExternalCARoot.crt -keystoreselfservice.keystore
And I have used correct chain of installation of certificate like root , all intermediate, primary from above command.
And while installing each certificate i received the following message
"Certificate added to keystore"
Though I have not got any error .
And when i have opened my keystore there were no certificate chain , means there is individual entry of each certificate . but there is no chain hierarchy of certificates like Root then intermediate then primary.
And in my final PI or certifcate, i am getting provider as local first name instead of Comodo .
EXAMPLE :
CN=nims.ABC.com,OU=abcCommunications,O=abc Group LLC, L=Roseville,ST=Minnesota,C=US
Provider must be
CN=COMODO RSA Organization Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
So I would like to know which steps I have missed or used any extra steps .
Please provide a solution to install a wild card certificate .
Thanks in advance
You did everything correctly. The trust chain is important for another aspect. If you trust one 'certificate' of the chain, you trust the following 'certificates' of the chain too. So to trust all certs of a CA you just have to trust the root CA's cert.
What you realy need to make the wild card certificate work on you server is to import the private key part of it.
I assume you mean Tomcat using Java SSL (JSSE) not APR/Native (OpenSSL). If you want Tomcat-APR, change your question.
If the cert you want to use is already in use on other servers, and you "generated a public keystore using" the keytool command you showed on the NEW server, you generated a NEW KEY which is different from the key the other servers used and different from the key included in the certificate, thus the certificate DOES NOT MATCH that new key and cannot be used with that new key. You also implicitly generated (and have not replaced) a self-signed cert, with both subject and issuer (what you call provider) identifying you rather than a CA like Comodo. This certificate is not good for general use but can be useful for some testing, which is why keytool does it implicitly.
You need to get the certificate, the ALREADY EXISTING private key that MATCHES the certficate, and the needed chain cert(s) into your JKS as a privateKey entry. If an existing SSL server is Java (using JSSE), just copy its JKS. If you want or need to change the password(s) on the copy for your new server, see keytool -storepassword and keytool -keypasswd.
If an existing server is OpenSSL (including Apache httpd and nginx), convert the OpenSSL PEM format to PKCS#12 (preferably on the old server); depending on that server's file layout this is something like
openssl pkcs12 -export -in certfile -inkey keyfile -certfile chaincert -out xxx
and then use keytool to convert PKCS#12 to JKS (preferably on the new server)
keytool -importkeystore -srckeystore xxx -srcstoretype pkcs12 -destkeystore yyy
Note you must use a password on the PKCS#12. This does not need to be the same as the old server keyfile (if any) or the new server JKS, but it's usually more convenient if it is.
If an existing server is IIS, you should be able to export the cert WITH private key AS PFX/PKCS#12 from the Certificate snapin of mmc, and then convert the PKCS12 to JKS as just above.
If an existing server is something else, add it to the question.
I have created a public private key pair using the KeyPairGenerator class in Java. From this key pair I have generated the CSR request using the PKCS10 class which has been sent to a CA for verification. The question is how do I load this public private key pair into a keystore? I cant use KeyStore.SetKeyEntry as it requires a certificate parameter along with the private key.
I thought that I would have to wait for the CA to send back a certificate which should then be used for loading the key pair. But If I create the keystore using the keytool command -
keytool -genkey -keyalg RSA -keysize 2048 -sigalg sha1withRSA -alias aliasname -validity 365 -keystore keystorename
and then load this keystore into the Java keystore class, the keystore object contains a privatekeyentry and a CertificateEntry. How is this possible without obtaining a certificate back from the CA.
The keytool command that you used creates a self-signed certificate. When you receive the new certificate signed by the CA, you can import that certificate into the keystore, which will then replace the self-signed certificate.
About your Java question, you will either need to generate a self-signed certificate based on the public key that you created (and signed by the private key), or you will need to wait for the CA to return your signed certificate and use that with the private key in SetKeyEntry.
I'm writing an Android app that requires SSL client authentication. I know how to create a JKS keystore for a desktop Java application, but Android only supports the BKS format. Every way I've tried to create the keystore results in the following error:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
So it looks like the client is never sending a proper certificate chain, probably because I'm not creating the keystore properly. I'm unable to enable SSL debugging like I can on the desktop, so that's making this much more difficult than it should be.
For reference the following is the command that IS working to create a BKS truststore:
keytool -importcert -v -trustcacerts -file "cacert.pem" -alias ca -keystore "mySrvTruststore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest
Here is the command I've tried that is NOT working to create a BKS client keystore:
cat clientkey.pem clientcert.pem cacert.pem > client.pem
keytool -import -v -file <(openssl x509 -in client.pem) -alias client -keystore "clientkeystore" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest
Detailed Step by Step instructions I followed to achieve this
Download bouncycastle JAR from
http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar
or take it from the "doc" folder.
Configure BouncyCastle for PC using one of the below methods.
Adding the BC Provider Statically (Recommended)
Copy the bcprov-ext-jdk15on-1.46.jar to each
D:\tools\jdk1.5.0_09\jre\lib\ext (JDK (bundled JRE)
D:\tools\jre1.5.0_09\lib\ext (JRE)
C:\ (location to be used in env variable)
Modify the java.security file under
D:\tools\jdk1.5.0_09\jre\lib\security
D:\tools\jre1.5.0_09\lib\security
and add the following entry
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
Add the following environment variable in "User Variables" section
CLASSPATH=%CLASSPATH%;c:\bcprov-ext-jdk15on-1.46.jar
Add bcprov-ext-jdk15on-1.46.jar to CLASSPATH of your project and Add the following line in your code
Security.addProvider(new BouncyCastleProvider());
Generate the Keystore using Bouncy Castle
Run the following command
keytool -genkey -alias myproject -keystore C:/myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
This generates the file C:\myproject.keystore
Run the following command to check if it is properly generated or not
keytool -list -keystore C:\myproject.keystore -storetype BKS
Configure BouncyCastle for TOMCAT
Open D:\tools\apache-tomcat-6.0.35\conf\server.xml and add the following entry
<Connector
port="8443"
keystorePass="myproject"
alias="myproject"
keystore="c:/myproject.keystore"
keystoreType="BKS"
SSLEnabled="true"
clientAuth="false"
protocol="HTTP/1.1"
scheme="https"
secure="true"
sslProtocol="TLS"
sslImplementationName="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
Restart the server after these changes.
Configure BouncyCastle for Android Client
No need to configure since Android supports Bouncy Castle Version 1.46 internally in the provided "android.jar".
Just implement your version of HTTP Client (MyHttpClient.java can be found below) and set the following in code
SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
If you don't do this, it gives an exception as below
javax.net.ssl.SSLException: hostname in certificate didn't match: <192.168.104.66> !=
In production mode, change the above code to
SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
MyHttpClient.java
package com.arisglobal.aglite.network;
import java.io.InputStream;
import java.security.KeyStore;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import com.arisglobal.aglite.activity.R;
import android.content.Context;
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
#Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.aglite);
try {
// Initialize the keystore with the provided trusted certificates.
// Also provide the password of the keystore
trusted.load(in, "aglite".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
How to invoke the above code in your Activity class:
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);
I use Portecle, and it works like a charm.
I don't think your problem is with the BouncyCastle keystore; I think the problem is with a broken javax.net.ssl package in Android. The BouncyCastle keystore is a supreme annoyance because Android changed a default Java behavior without documenting it anywhere -- and removed the default provider -- but it does work.
Note that for SSL authentication you may require 2 keystores. The "TrustManager" keystore, which contains the CA certs, and the "KeyManager" keystore, which contains your client-site public/private keys. (The documentation is somewhat vague on what needs to be in the KeyManager keystore.) In theory, you shouldn't need the TrustManager keystore if all of your certficates are signed by "well-known" Certifcate Authorities, e.g., Verisign, Thawte, and so on. Let me know how that works for you. Your server will also require the CA for whatever was used to sign your client.
I could not create an SSL connection using javax.net.ssl at all. I disabled the client SSL authentication on the server side, and I still could not create the connection. Since my end goal was an HTTPS GET, I punted and tried using the Apache HTTP Client that's bundled with Android. That sort-of worked. I could make the HTTPS conection, but I still could not use SSL auth. If I enabled the client SSL authentication on my server, the connection would fail. I haven't checked the Apache HTTP Client code, but I suspect they are using their own SSL implementation, and don't use javax.net.ssl.
Not sure you resolved this issue or not, but this is how I do it and it works on Android:
Use openssl to merge client's cert(cert must be signed by a CA that accepted by server) and private key into a PCKS12 format key pair:
openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
You may need patch your JRE to umlimited strength encryption depends on your key strength: copy the jar files fromJCE 5.0 unlimited strength Jurisdiction Policy FIles and override those in your JRE (eg.C:\Program Files\Java\jre6\lib\security)
Use Portecle tool mentioned above and create a new keystore with BKS format
Import PCKS12 key pair generated in step 1 and save it as BKS keystore. This keystore works with Android client authentication.
If you need to do certificate chain, you can use this IBM tool:KeyMan to merge client's PCKS12 key pair with CA cert. But it only generate JKS keystore, so you again need Protecle to convert it to BKS format.
command line:
keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
Your command for creating the BKS keystore looks correct for me.
How do you initialize the keystore.
You need to craeate and pass your own SSLSocketFactory. Here is an example which uses Apache's org.apache.http.conn.ssl.SSLSocketFactory
But I think you can do pretty the same on the javax.net.ssl.SSLSocketFactory
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "testtest".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
Please let me know if it worked.
Use this manual http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/
This guide really helped me. It is important to observe a sequence of certificates in the store. For example: import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate.