A secure connection between a server and the client is make. The server is authenticated via it's certificate. The certificate is verified by the client against a Certificate Authority CA. Equally the client authenticates itself to the server by means of a client certificate. This certificate is also signed by a Certificate Authority and can be verified. Often it is not possible to inconvenient to obtain a signed certificate from a public certificate authority. In this case the server and client certificates can be signed by our own CA.
Creating your own Certificate Authority
For our convenience some clever people have provided us with a perl script assisting us in the creation of our own CA, CA.pl. This script is typically distributed with most unix flavors.
Make a new directory and copy this script into it
Wolf:~ alex% mkdir CA
Wolf:~ alex% cd CA/
Wolf:~ alex% cp /System/Library/OpenSSL/misc/CA.pl
To create a new certificate authority call the script with the -newca parameter as follows.
Wolf:~/CA alex$ ./CA.pl -newca
CA certificate filename (or enter to create)
[ENTER]
Making CA certificate ...
Generating a 1024 bit RSA private key
.......................................++++++
............................++++++
writing new private key to './demoCA/private/cakey.pem'
Enter PEM pass phrase:[password]
Verifying - Enter PEM pass phrase:[password]
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:London
Locality Name (eg, city) []:London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:myorg.co.uk
Organizational Unit Name (eg, section) []:WorkShop
Common Name (eg, YOUR name) []:myorganisation
Email Address []:admin@myorg.co.uk
Wolf:~/CA alex$
In the example the PEM password is set to password. The result of this command is a new directory named demoCA. The file cacerts.pem contains a self-signed certificate (including public key). The private key resides in the private/cakey.pem file.
CA directory structure
To create a certificate with openssl the following command can be used.
Wolf:~/CA alex$ ./CA.pl -newreq
Generating a 1024 bit RSA private key
.....++++++
......................++++++
writing new private key to 'newreq.pem'
Enter PEM pass phrase:[password]
Verifying - Enter PEM pass phrase:[password]
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:London
Locality Name (eg, city) []:London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MyOtherOrg
Organizational Unit Name (eg, section) []:Sales Dept.
Common Name (eg, YOUR name) []:SellALot
Email Address []:admin@sellalot.co.uk
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:password
Request (and private key) is in newreq.pem
Wolf:~/CA alex$
This command will generate a file newreq.pem containing a certificate and a private key. To sign the generated certificate signing request CSR the following command is used :
Wolf:~/CA alex$ ./CA.pl -sign
Using configuration from /System/Library/OpenSSL/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:[password]
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: May 1 14:08:54 2005 GMT
Not After : May 1 14:08:54 2006 GMT
Subject:
countryName = UK
stateOrProvinceName = London
localityName = London
organizationName = MyOtherOrg
organizationalUnitName = Sales Dept.
commonName = SellALot
emailAddress = admin@sellalot.co.uk
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
CA:21:99:58:B1:75:48:B9:2E:6E:07:08:37:0C:AE:36:62:DE:01:55
X509v3 Authority Key Identifier:
keyid:A1:AA:1F:C5:8C:17:60:E7:B3:7D:FF:5A:92:2F:E1:E9:5F:AE:57:87
DirName:/C=UK/ST=London/L=London/O=myorg.co.uk/OU=WorkShop
/CN=myorganisation/emailAddress=admin@myorg.co.uk
serial:00
Certificate is to be certified until May 1 14:08:54 2006 GMT (365 days)
Sign the certificate? [y/n]:y
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:London
Locality Name (eg, city) []:London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MyOtherOrg
Organizational Unit Name (eg, section) []:Sales Dept.
Common Name (eg, YOUR name) []:SellALot
Email Address []:admin@sellalot.co.uk
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:password
An optional company name []:password
Request (and private key) is in newreq.pem
Wolf:~/CA alex$
This will create newcert.pem which contains a signed certificate, signed by your own certificae authoritiy.
To extract the private key from the request use the following.
Wolf:~/CA alex$ openssl rsa < newreq.pem > servler_key.pem
Enter pass phrase:[password]
writing RSA key
Wolf:~/CA alex$
Often is is helpful to change the create file names.
newcert.pem -> server_cert.pem [signed certificate]
newreq.pem -> server_req.pe [CSR]
server_key.pem [private key]
Installing the new Certificate on Apache
Add the following to the httpd.conf file.
SSLEngine on
SSLProtocol all
SSLCipherSuite HIGH:MEDIUM
SSLCertificateFile server_cert.pem
SSLCertificateKeyFile server_key.pem
SSLVerifyClient require
SSLVerifyDepth 1
SSLCACertificationFile demoCA/cacert.crt
Now that we have created a CA and configured Apache to use SSL and provide secure contence on port 443 a connection can be attempted. If we have switched client authentication on SSLVerifyClient require then we still need to create and sign a client certificate. The process to do this is the same a we used to create the servers certificate.
CA.pl -newreq
...
CA.pl -sign
...
mv newreq.pem client_req.pem
mv newcert.pem client_cert.pem
openssl rsa < client_req.pem > client_key.pem
openssl pkcs12 -export -in client_cert.pem -inkey client_key.pem -out client.p12
The resulting client.p12 file must be imported into the browser. In many cases it might be required to add the CA's certificate cacerts.pem to the browsers certificate store as well. This might also prevent popups appearing
To connect using a java program follow the steps. Details on the commands issued are the the Tools section
•Create a keystore and a keypair
•Export a CSR from the keystore
•Sign the request with your own CA's cacert
•Re-Import the signed cert back into the keystore. Remember to use the same alias
To establish a connection using the standard JSSE API it is be necessary to specify the proper key and trustore (default truststore is $JAVA_HOME/jre/lib/security/cacerts). Keystores are used to contain keys and trusted certs and must contain a entry of type keyEntry consisting of the client's private key and the signed certificate. Truststores are used to validated trusted entities. The truststore must contain the CA's public certificate. To specify the key and truststores at runtime the following paramters used -Djavax.net.ssl.keyStore and -Djavax.net.ssl.keyStorePassword and respectively -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStorePassword
Generate a X509 certificate from a signed certificate
openssl x509 -in server_cert.pem -out server.x509
Generate a PKCS12 certificate form a signed certificate
openssl pkcs12 -export -in server_cert.pem -inkey server_key.pem
-out server.p12
Generate a public / private key using keytool
alex% keytool -genkey -keystore clientstore -alias meAsClient
-storepass changeit
What is your first and last name?
[Unknown]: meAsClient.org.za
What is the name of your organizational unit?
[Unknown]: clientUnit
What is the name of your organization?
[Unknown]: clientOrg
What is the name of your City or Locality?
[Unknown]: Johannesburg
What is the name of your State or Province?
[Unknown]: Gauteng
What is the two-letter country code for this unit?
[Unknown]: ZA
Is CN=meAsClient.org.za, OU=clientUnit, O=clientOrg, L=Johannesburg,
ST=Gauteng, C=ZA correct?
[no]: y
Enter key password for <meAsClient>
(RETURN if same as keystore password):
alex%
This will create a new keystore with password changeit persisted in the clientstore file. The following entries in this keystore are also created.
alex% keytool -list -keystore clientstore
Enter keystore password: changeit
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
measclient, Aug 3, 2003, keyEntry,
Certificate fingerprint (MD5): 1F:64:B5:95:10:D2:7B:43:76:CD:D6:01:39:C8:7E:E8
alex%
Note the entry type : keyEntry. A keystore can maintain 2 different types of entries, one being a keyEntry containing a private key and certificate and the other being a trustedCertEntry which only contains a certificate.
Generate a CSR using keytool
alex% keytool -keystore clientstore -certreq -alias measclient
-file client.csr -storepass changeit
Sign the CSR with OPENSSL
openssl x509 -req -CA cacert.pem -CAkey cakey.pem -extensions v3_ca
-in client.csr -inform DER -out client_cert.x509
-CAcreateserial
eg :
alex% openssl x509 -req -CA demoCA/cacert.pem -CAkey demoCA/private/cakey.pem
-extensions v3_ca -in client.csr -inform DER
-out client_cert.x509 -CAcreateserial
Signature ok
subject=/C=ZA/ST=Gauteng/L=Johannesburg/O=clientOrg
/OU=clientUnit/CN=meAsClient.org.za
Getting CA Private Key
Enter PEM pass phrase: [password[
alex%
Import the CA's self signed certificate into the truststore
alex# keytool -import -alias butterflyCA
-keystore /Syst.. ..urity/cacerts -file /CA/demoCA/cacert.pem
Import the signed certificate response into the keystore. This requires the CA to be added to the truststore. Note the alias is the same as was used in the creation of the CSR.
alex% keytool -import -alias measclient -keystore clientstore -trustcacerts
-file client_cert.x509
Enter keystore password: changeit
Certificate reply was installed in keystore
/*
* SSLIX.java
* The following is only required if the certificate on the server is self-
* signed. In this case the certificate has to be imported into a keystore * and given trusted permissions. It is also possible to add the
* Certificate. Authorities Certificate to the keystore to address this
* problem.
* The Certificate and Key were generated with
* cd /etc/httpd/cont/
* make SOMETHING.crt
*
* This generates a keypair as well as a certificate
*
* To import this certificate into the default keystore used the following
* command :
*
* keytool -import -keystore "<JAVA_HOME>\jre\lib\security\cacerts"
* -alias teddy
* -storepass changeit [This is the default]
* -file ssl.crt\SOMETHING.crt [The certificate file]
*/
import java.net.*;
import javax.net.ssl.*;
import java.io.*;
import java.util.*;
import java.security.*;
/**
*
* @author Alex
*/
public class SSLIX
{
/** Creates a new instance of SSLIX */
public SSLIX()
{
}
public void connect() throws Exception
{
URL url = new URL("https://teddy.earth");
System.out.println("Connecting to Host : ["+url.getHost()+"]");
System.setProperty("javax.net.ssl.keyStore","butterflyKS");
System.setProperty("javax.net.ssl.keyStorePassword","changeit");
System.out.println("KeyStore : "+System.getProperty("javax.net.ssl.keyStore"));
System.out.println("TrustStore : "+System.getProperty("javax.net.ssl.trustStore"));
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setDoOutput(false); // true for POST, false for GET
connection.setDoInput(true);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
connection.setAllowUserInteraction(true);
String aLine=null;
InputStreamReader inReader = new InputStreamReader(connection.getInputStrea());
BufferedReader aReader = new BufferedReader(inReader);
while ((aLine = aReader.readLine()) != null)
System.err.println(aLine);
aReader.close();
connection.disconnect();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
try
{
SSLIX ix = new SSLIX();
ix.connect();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}