tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Carl Olivier" <carl.oliv...@unysen.co.uk>
Subject RE: SSL InvalidKeystore Format?
Date Sun, 04 Dec 2005 18:10:11 GMT
Greetings.

Not sure if this will help, but I spent a lot of time fighting with
certificates for use with Tomcat - from CSRs to issued certificates from the
CAs - as well as using PFX files as exported from other webservers etc..

I have however got it working great, and thought to give you my steps,
commands, and pointers - hopefully they will assist!

Scenario 1:  CSR with issued Cert from CA

1.  Generate the CSR.

Important - generating the CSR requires a private key (which resides in the
keystore used for the generation) to be present when importing the actual
certificate from the CA (in response to the CSR).  Thus I have a number of
different keystores I use.  

Generate the Private Key I will be using to generate the CSR (using my
'privatestore' keystore):

"keytool -genkey -keyalg RSA -dname cn=www.site.com ou=My Deptartment o=My
Company l=My City s=My province c=GBR -alias certrequest -keypass mypasswd
-keystore /ssl/privatestore -storepass mystorepwd -validity 365"

This generates the private key, and generates the CSR ready for extraction
to be submitted to the CA.

I use the following code snippet to extract the private key - bearing in
mind that I will be wanting to use the private key later when I wish to
import the public key (cert) as returned by the CA.  Useful to note that I
create a keystore per connector (per site) for use in Tomcat.

=== code snippet ===
		
		//this first snippet extracts the private key - as I wish to
persist it in Base64 encoding
		//for use when I import the certificate later
		//I resuse this keystore for private key/CSR generation	
	
		//load the privatestore keystore used for privatekey/CSR
generation
		File workingkeystore = new File( "/ssl/privatestore" );
		KeyStore workingStore = KeyStore.getInstance( "JKS" );
		workingStore.load( new FileInputStream( workingkeystore ),
"mystorepwd".toCharArray() );
		
		//extract the keypair (private key is what we want)
		KeyPair kp = getPrivateKey( workingStore, "certrequest",
"mypasswd".toCharArray() );
		PrivateKey pkey = kp.getPrivate();
		//get the private key binary
		byte[] binary = pkey.getEncoded();
		//base64 encoder - in package sun.misc
		BASE64Encoder myB64 = new BASE64Encoder();
		//encode the private key binary as a Base64 string
		String b64 = myB64.encode( binary );
		
		//persist the private key for later use
		PrintWriter out = new PrintWriter( new FileWriter( new
File("/ssl/privatekeys/www.mysite.com.pkey") ) );

		try
		{
			out.println( "-----BEGIN PRIVATE KEY-----" );
			out.println( b64 );
			out.println( "-----END PRIVATE KEY-----" );
		}
		finally
		{
			out.close();
		}
		
=== end snippet ===

Now I use the keytool to generate the CSR with the private key generated
above (note I use the same alias I used when generating the private key to
ensure correct processing).  Note that this will generate the Base64
representation of the CSR to the specified location using the -file switch.

"keytool -certreq -file /ssl/csrs/www.site.com.csr -alias certrequest
-keypass mypasswd -keystore /ssl/privatestore -storepass mystorepwd"

The Base64 string which is the contents of the CSR above can then be
submitted to the CA.  Be sure to keep the private key generated for use when
importing the CA issued certificate.  I will now go over how I go about
importing/setting up the actual certificate keystore for use with Tomcat:

Using a Base64 String representation of the Certificate returned by the CA:

Before I run the code below however I run the Private Key we generated to
create the CSR through the openssl to get it into the correct format -
namely pkcs8 (DER) format using the openssl command as follows:

"openssl pkcs8 -topk8 -nocrypt -in /ssl/privatekeys/www.mysite.com.pkey -out
/ssl/temp/www.mysite.com.tempkey -outform der"

=== code snippet ===
		
		//first create the keystore we will be using for the cert
		//the actual keystore file path - ensure this does not exist
		String keyFilePath =
"/ssl/keystores/www.mysite.com.keystore";
		KeyStore ks;
		try {
			ks = KeyStore.getInstance("JKS", "SUN");
			ks.load( null, "mypasswd".toCharArray() );
			ks.store(new FileOutputStream ( new
File(keyFilePath)  ), "mypasswd".toCharArray());
		} catch (Exception e) {
			//handle as you require
			throw new RuntimeException("Failed to create new
Keystore (" + keyFilename + ").", e);
		}
		
		//The File containing the Base64 String representation of
the Certificate
		//this was returned by the CA - most of them will give you a
Base64 String representation
		//if not there are ways to get it this way - if I have time
tonight will detail...
		File inputSource = new
File("/ssl/certs/www.mysite.com.cert");

		//read and create an instance of the certificate(s) given in
the supplied Base64 string
		X509Certificate[] certsFound = null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(inputSource);

			BufferedInputStream bis = new
BufferedInputStream(fis);

			CertificateFactory cf =
CertificateFactory.getInstance("X.509");
			ArrayList temp = new ArrayList();
			while (bis.available() > 0) {
				X509Certificate cert =
(X509Certificate)cf.generateCertificate(bis);
				temp.add(cert);
			}
			certsFound = new X509Certificate[temp.size()];
			temp.toArray(certsFound);

		} catch (CertificateException ce) {
			//handle as required
			throw new RuntimeException("Failed to read
Certificates from Base64 String in file: " + inputsource, ce);
		} finally {
			fis.close();
		}

		//now we read this in and set up the actual keystore
containing both the private key (used to generate the CSR) and
		//the certificate (public key) returned by the CA.
		//I use the openssl executable in order to ensure that all
my certification files and keys are in the correct format
		//this allows me to control the formats I use for use with
Tomcat from multiple sources...
		try {
			FileInputStream inKeyFile = new FileInputStream(new
Fiile("/ssl/keystores/www.mysite.com.keystore"));
			KeyStore ks = KeyStore.getInstance("JKS");
			ks.load(inKeyFile, "mypasswd".toCharArray());
			
			Key storeKey;

			// loading Key
			InputStream fl =null;
			byte[] key;
			KeyFactory kf;
			try
			{
				fl = fullStream
("/ssl/privatekeys/www.mysite.com.pkey");
				key = new byte[fl.available()];
				kf = KeyFactory.getInstance("RSA");
				fl.read ( key, 0, fl.available() );
			}
			finally
			{
				if (fl!=null)
					fl.close();
			}
			PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec
( key );
			storeKey = kf.generatePrivate (keysp);
			
			// storing keystore - with the private key and
certificates
			ks.setKeyEntry("www.mysite.com", storeKey,
"mystorepwd".toCharArray(), certsFound );
			
			//store the keystore properly with the DER private
key and the X509 certificate(s)
			FileOutputStream outKeyfile = new FileOutputStream (
keyfile );
          		ks.store(outKeyfile, "mystorepwd".toCharArray());

			//final step - import the certificate to the trusted
store as in use by your Tomcat's JVM
			KeyStore ts;
			FileInputStream trustedKeystoreFile = new
FileInputStream(new Fiile("/jsdk/jre/lib/security/cacerts"));
			KeyStore ks = KeyStore.getInstance("JKS");
			ts.load(trustedKeystoreFile ,
"mytrustedpasswd".toCharArray());
			
			ts.setCertificateEntry("www.mysite.com",
certData.getCertificateChain()[0]);

		} catch (Exception e) {
			//handle as required
			throw new RuntimeException("Failed to import
Certificate provided into keystore at " + keyfile.getName(), e);
		} finally {
			if (inKeyFile != null) {
				try {
					inKeyFile.close();
				} catch (Exception e) {
					//could not close??
				}
			}

			if (outKeyfile != null) {
				try {
					outKeyfile.close();
				} catch (Exception e) {
					//could not close??
				}
			}

			if (tmpKey != null)
				if (tmpKey.exists())
					if (!tmpKey.delete())
						tmpKey.deleteOnExit();
		}

=== end code snippet ===

Note about the import to the trusted store - importing the cert from the CA
into the Tomcat JVM trusted store you are using by default uses the cacerts
file found in:  [JREHOME]/lib/security/cacerts file - although I override
this and use a different one.

I also have other code snippets for importing PFX files (PKCS#12) which
contain both the private and public keys (certificate) - but this one seemed
to be the best option for your needs.

Hope it helps!

Regards,

Carl


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message