Java's APIs are great for developing networked or fully distributed applications. The java.net package makes it a cinch to develop custom socket-based client-server applications. The java.rmi packages make it possible to develop distributed object systems with minimal programming. Other packages, such as Apache's SOAP, provide the capability to develop distributed applications that communicate with non-Java-based objects.
Networked applications, by their very nature, require close attention to security. The Secure Sockets Layer (SSL) protocol was developed by Netscape in 1994 as a common solution to client-server communication security issues. SSL supports a flexible client-server authentication scheme and provides for algorithm-independent encrypted client-server communication. SSL runs as a layer between the Transport Control Protocol (TCP) and application layer protocols, such as HTTP and SMTP.
The current version of SSL is 3.0. It has been standardized by the Internet Engineering Task Force (IETF) as Transport Layer Security (TLS) version 1.0. There are few changes between TLS 1.0 and SSL 3.0, but it is good idea to continue to use SSL 3.0 until older, non-TLS-capable browsers fade away.
Since SSL and TLS provide a standard, flexible, secure, and easy-to-use solution for basic client-server communication issues, the question naturally arises, "How can I use SSL within my Java programs?" Fortunately, Sun has answered that question with a complete SSL/TLS API, the Java Secure Socket Extension (JSSE). The current version of JSSE is 1.02, freely available from Sun's Web site.
In this column, I'll show you how to install JSSE and use it to implement HTTPS (i.e., HTTP over SSL). I'll provide you with an example of a mini-HTTPS server and Java clients that support SSL. I'll then show you how to setup a bi-directional SSL scheme where clients authenticate servers and servers authenticate clients.
Since JSSE does not come with the Java 2 SDK, you must download it from Sun's site and integrate it with your current JDK installation.
jsse-1_0_2-do.zip. Unzip this file to produce a folder named
jsse1.0.2.
jsse1.0.2 folder you'll find a lib directory and
within the lib directory, you'll find the files jsse.jar,
jcert.jar, and jnet.jar. Copy these files to the
lib/ext subdirectory of your Java home directory. Use the program
shown in Listing 1 to find your Java home directory. (It may not be where you
think it is.) You should also copy these JAR files to the
jre/lib/ext directory off of where the Java 2 SDK is installed.
Listing 1. The ShowJavaHome program.
public class ShowJavaHome {
public static void main(String[] args) {
System.out.println(System.getProperty("java.home"));
}
}
Listing 2. The JSSETest program.
import java.security.*;
public class JSSETest {
public static void main(String[] args) {
try {
Class.forName("com.sun.net.ssl.internal.ssl.Provider");
}catch(Exception e) {
System.out.println("JSSE is NOT installed correctly!");
System.exit(1);
}
System.out.println("JSSE is installed correctly!");
}
}
Since SSL uses certificates for authentication, we'll need to create certificates for our clients and servers. JSSE can use certificates created by the java keytool, so creating these certificates will be straightforward.
However, before we begin, there is one issue that you should be aware of. JSSE differentiates between regular keystores and truststores. Keystores (from JSSE's perspective) are databases of key pairs and certificates that are used to set up SSL authentication. Truststores are keystores that are used to verify the identities of other clients and servers. When a client or server is setting up an SSL session, it will retrieve its certificates and keys from its keystore. When it verifies the identities of other clients or servers, it will retrieve trusted certification authority (CA) certificates from its truststores.
JSSE looks for truststores using the following algorithm.
javax.net.ssl.trustStore system property is defined,
then the value of this property is used as the truststore's location.
lib/security/jssecacerts file is defined off of
the java.home directory, then the jssecacerts file is used as the truststore.
lib/security/cacerts file is defined off of the
java.home directory, then the cacerts file is used as the
truststore.You can find the value of the javax.net.ssl.trustStore property
using the ShowTrustStore program provided in Listing 3.
Listing 3. The ShowTrustStore program.
public class ShowTrustStore {
public static void main(String[]
args) {
String trustStore =
System.getProperty("javax.net.ssl.trustStore");
if(trustStore ==
null)
System.out.println("javax.net.ssl.trustStore is not
defined");
else System.out.println("javax.net.ssl.trustStore = "
+ trustStore);
}
}
Server certificates can be generated with a single keytool command. I used
the following command to create an RSA certificate, referenced by the alias of
jamie, and stored in a keystore named certs.
keytool -genkey -keystore certs -keyalg rsa -alias jamie -storepass
serverkspw -keypass serverpw
The keytool then prompted me for information to put into the certificate. My
answers are shown in bold.
What is your first and last name?
[Unknown]:
enpower
What is the name of your organizational
unit?
[Unknown]: Software Development
What is the name
of your organization?
[Unknown]: Toolery.com
What is
the name of your City or Locality?
[Unknown]: Chula
Vista
What is the name of your State or
Province?
[Unknown]: CA
What is the two-letter country
code for this unit?
[Unknown]: US
Is <CN=enpower,
OU=Software Development, O=Toolery.com, L=Chula Vista, ST=CA, C=US>
correct?
[no]: y
Note that I used a keystore password of serverkspw and a key password of serverpw. Go ahead and use these same values for the time being. You can use a different alias if you like. Also, enter your own information for the certificate. I used my machine name (enpower) for the first and last name of the certificate. You should do the same. If your machine does not have a name, use it's IP address. The enpower name is the name of my laptop's manufacturer.
Now that we have a server certificate, all we need is a Java web server to take advantage of the certificate. Listing 4 provides an HTTP server that I've used in a few of my Java books. It is a fairly primitive server. I don't recommend using it for production systems. But it is small and works for simple HTTP-related examples.
Listing 5 provides a class named SecureServer that extends HTTPServer to provide support for SSL. As you can see, it is only about 50 lines. By following this example, you'll be able to see how easy it is to add SSL support to an existing HTTP application.
Compile HTTPServer from within your working directory. (The same
one that contains cacerts.) Then compile SecureServer. Next, create
an HTML file named index.htm to be served in your directory. You
can use the one shown in Listing 6 if you want.
Now start the server by entering java SecureServer from a
console window. You may have to wait about a minute or two for the server to
begin taking requests. The seeding of the secure random number generator slows
things down. If you have a server currently running on port 443 (the HTTPS
port), you'll have to disable it in order to get SecureServer to
work.
When you run SecureServer, it will generate the following
output.
SecureServer version 1.0
SecureServer is listening on port 443.
Now use a browser to establish an SSL connection to SecureServer. Since my
machine name is enpower, I'll enter https://enpower/ in the
Internet Explorer 5.5 address bar. Internet Explorer contacts SecureServer and
tries to set up an SSL connection. SecureServer then sends IE its certificate.
Because the certificate is not signed by a valid certificate authority, Internet
Explorer displays the following popup.

When I click on the View Certificate button, this dialog box appears.

The above dialog explains why Internet Explorer balked at the certificate. If you click the Details tab, you can view the information that is contained in the certificate.

If you click the Certification Path tab, you'll see that the certificate is self-signed.

After clicking the OK button and accepting the certificate, Internet Explorer displays the following content.

That's all there is to setting up server-side SSL. Next I'll discuss how
SecureServer works and then show you how to set up SSL on the
client side.
The first thing that you should notice about SecureServer is
that it imports the following packages:
import javax.net.*;
import javax.net.ssl.*;
import com.sun.net.ssl.*;
These are the basic packages that are part of the JSSE API. The
javax.net package provides the SocketFactory and
ServerSocketFactory classes, which are used to replace normal TCP
sockets with SSL sockets. The javax.net.ssl package provides
classes and interfaces for establishing and managing an SSL session. The
com.sun.net.ssl package provides the underlying key management
classes and interfaces.
There is another JSSE package named javax.security.cert that
provides additional public key certificate support. However, we don't need this
package for SecureServer.
SecureServer defines the following field variables:
String KEYSTORE = "certs";
char[] KEYSTOREPW = "serverkspw".toCharArray();
char[] KEYPW = "serverpw".toCharArray();
boolean requireClientAuthentication;
These variables contain the names of the keystore, the keystore password, and
key password. Note that you shouldn't hardwire your passwords into your code.
Also, never use String objects to store passwords because they are
immutable and cannot by overwritten. Use a char array instead. The
requireClientAuthentication field should be set to
true if you want the server to authenticate the client's
certificate (more on this later).
The main() method simply creates a SecureServer
object and invokes its run() method. The SecureServer constructor
passes the server's name, version, and port values to the superclass constructor
(HTTPServer).
The getServerSocket() method is where all of the SSL action
takes place. It overrides the getServerSocket() method of
HTTPServer to substitute a SSLServerSocket for an ordinary TCP
ServerSocket.
The getServerSocket() method begins by registering JSSE as a
cryptographic provider.
Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
It then accesses the cacerts keystore. JKS stands for Java keystore, which is the type of keystore created by the keytool.
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new
FileInputStream(KEYSTORE), KEYSTOREPW);
The Security and KeyStore classes are defined in
the java.security package, which is part of the standard Java 2
SDK.
A KeyManagerFactory is used to create a X.509 key manager for
the keystore. KeyManagerFactory is defined in
com.sun.net.ssl.
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, KEYPW);
Now that we have key management out of the way, we need to establish an
SSLContext. An SSLContext is an environment for
implementing JSSE. It is used to create a ServerSocketFactory,
which is used to create an SSLServerSocket.
SSLContext sslc = SSLContext.getInstance("SSLv3");
The SSLContext is set to use SSL 3.0 instead of TLS 1.0. I tend
to use SSL 3.0 because of its support among older browser. The
SSLContext is initialized to work with our key manager.
sslc.init(kmf.getKeyManagers(), null, null);
Next, we create a ServerSocketFactory from the
SSLContext.
ServerSocketFactory ssf = sslc.getServerSocketFactory();
And finally, we create the SSLServerSocket.
SSLServerSocket serverSocket = (SSLServerSocket)
ssf.createServerSocket(serverPort);
At this point, we are not using client authentication.
serverSocket.setNeedClientAuth(requireClientAuthentication);
Listing 4. A simple HTTP server.
import java.net.*;
import java.io.*;
import
java.util.*;
// Small, simple HTTP server
public class HTTPServer
{
String NAME;
String VERSION;
int
serverPort;
// No command line parameters are
required
public static void main(String
args[]){
HTTPServer server = new HTTPServer("HTTPServer", "1.0",
80);
server.run();
}
// Create an HTTPServer
for a particular TCP port
public HTTPServer(String name, String
version, int port) {
this.NAME =
name;
this.VERSION = version;
this.serverPort =
port;
}
// Display name and version number
public
void displayVersionInfo(){
System.out.println(NAME+" version
"+VERSION);
}
// Run until interrupted
public void
run() {
displayVersionInfo();
try
{
// Get a server socket
ServerSocket
server = getServerSocket();
int localPort =
server.getLocalPort();
// Let us know that you're
listening
System.out.println(NAME+" is listening on port
"+localPort+".");
do {
// Accept
a connection
Socket client =
server.accept();
// Handle the connection with a
separate thread
(new
HTTPServerThread(client)).start();
}
while(true);
} catch(Exception ex)
{
System.out.println("Unable to listen on
"+serverPort+".");
ex.printStackTrace();
System.exit(1);
}
}
//
Get a server socket on the hard-wired server port
ServerSocket
getServerSocket() throws Exception {
return new
ServerSocket(serverPort);
}
}
// Handle a single server
connection
class HTTPServerThread extends Thread {
Socket
client;
// Keep track of the client socket
public
HTTPServerThread(Socket client) {
this.client =
client;
}
// Thread entry point
public void run()
{
try {
// Display info about the
connection
describeConnection(client);
//
Create a stream to send data to the
client
BufferedOutputStream outStream = new
BufferedOutputStream(client.getOutputStream());
HTTPInputStream
inStream = new HTTPInputStream(client.getInputStream());
//
Get the client's request
HTTPRequest request =
inStream.getRequest();
// Display info about
it
request.log();
// Sorry, we only
handle
gets
if(request.isGetRequest())
processGetRequest(request,outStream);
System.out.println("Request
completed. Closing connection.");
}catch(IOException ex)
{
System.out.println("IOException occurred when processing
request.");
}
try
{
client.close();
}catch(IOException ex)
{
System.out.println("IOException occurred when closing
socket.");
}
}
// Display info about the
connection
void describeConnection(Socket client)
{
String destName =
client.getInetAddress().getHostName();
String destAddr =
client.getInetAddress().getHostAddress();
int destPort =
client.getPort();
System.out.println("Accepted connection to
"+destName+" ("
+destAddr+")"+" on port
"+destPort+".");
}
// Process an HTTP GET
void
processGetRequest(HTTPRequest request,BufferedOutputStream
outStream)
throws IOException {
/* If you
want to use this in a secure environment then you should place some
restrictions on the requested file name */
String
fileName = request.getFileName();
File file = new
File(fileName);
// Give them the requested
file
if(file.exists())
sendFile(outStream,file);
else System.out.println("File
"+file.getCanonicalPath()+" does not exist.");
}
// A simple
HTTP 1.0 response
void sendFile(BufferedOutputStream out,File file)
{
try {
DataInputStream in = new
DataInputStream(new FileInputStream(file));
int len = (int)
file.length();
byte buffer[] = new
byte[len];
in.readFully(buffer);
in.close();
out.write("HTTP/1.0
200 OK\r\n".getBytes());
out.write(("Content-Length: " +
buffer.length +
"\r\n").getBytes());
out.write("Content-Type:
text/html\r\n\r\n".getBytes());
out.write(buffer);
out.flush();
out.close();
System.out.println("File
sent:
"+file.getCanonicalPath());
System.out.println("Number of
bytes: "+len);
}catch(Exception ex){
try
{
out.write(("HTTP/1.0 400 " + "No can do" +
"\r\n").getBytes());
out.write("Content-Type:
text/html\r\n\r\n".getBytes());
}catch(IOException ioe)
{
}
System.out.println("Error
retrieving "+file);
}
}
}
// Convenience class
for reading client requests
class HTTPInputStream extends FilterInputStream
{
public HTTPInputStream(InputStream in)
{
super(in);
}
// Get a line
public
String readLine() throws IOException {
StringBuffer result=new
StringBuffer();
boolean finished = false;
boolean
cr = false;
do {
int ch =
-1;
ch = read();
if(ch==-1) return
result.toString();
result.append((char)
ch);
if(cr &&
ch==10){
result.setLength(result.length()-2);
return
result.toString();
}
if(ch==13) cr =
true;
else cr=false;
} while
(!finished);
return result.toString();
}
// Get
the whole request
public HTTPRequest getRequest() throws IOException
{
HTTPRequest request = new HTTPRequest();
String
line;
do {
line =
readLine();
if(line.length()>0)
request.addLine(line);
else
break;
}while(true);
return
request;
}
}
// Used to process GET requests
class
HTTPRequest {
Vector lines = new Vector();
public
HTTPRequest() {
}
public void addLine(String line)
{
lines.addElement(line);
}
// Is this a GET or
isn't it?
boolean isGetRequest() {
if(lines.size() >
0) {
String firstLine = (String)
lines.elementAt(0);
if(firstLine.length() >
0)
if(firstLine.substring(0,3).equalsIgnoreCase("GET"))
return
true;
}
return false;
}
// What
do they want to get?
String getFileName()
{
if(lines.size()>0) {
String firstLine =
(String) lines.elementAt(0);
String fileName =
firstLine.substring(firstLine.indexOf(" ")+1);
int n =
fileName.indexOf(" ");
if(n!=-1) fileName =
fileName.substring(0,n);
try {
if(fileName.charAt(0) == '/') fileName =
fileName.substring(1);
}
catch(StringIndexOutOfBoundsException ex)
{}
if(fileName.equals("")) fileName =
"index.htm";
if(fileName.charAt(fileName.length()-1)=='/')
fileName+="index.htm";
return
fileName;
}else return "";
}
// Display some
info so we know what's going on
void log()
{
System.out.println("Received the following
request:");
for(int
i=0;i<lines.size();++i)
System.out.println((String)
lines.elementAt(i));
}
}
Listing 5. Extending the HTTP sever with SSL support.
import java.net.*;
import java.io.*;
import
java.util.*;
import java.security.*;
import javax.net.*;
import
javax.net.ssl.*;
import com.sun.net.ssl.*;
public class SecureServer
extends HTTPServer {
String KEYSTORE = "certs";
char[]
KEYSTOREPW = "serverkspw".toCharArray();
char[] KEYPW =
"serverpw".toCharArray();
boolean
requireClientAuthentication;
public static void main(String
args[]){
SecureServer server = new
SecureServer();
server.run();
}
public
SecureServer(String name, String version, int port,
boolean
requireClientAuthentication) {
super(name, version,
port);
this.requireClientAuthentication =
requireClientAuthentication;
}
public SecureServer()
{
this("SecureServer", "1.0", 443,
false);
}
ServerSocket getServerSocket() throws Exception
{
// Make sure that JSSE is
available
Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
// A keystore is where
keys and certificates are kept
// Both the keystore and
individual private keys should be password protected
KeyStore
keystore = KeyStore.getInstance("JKS");
keystore.load(new
FileInputStream(KEYSTORE), KEYSTOREPW);
// A KeyManagerFactory is
used to create key managers
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("SunX509");
// Initialize the
KeyManagerFactory to work with our keystore
kmf.init(keystore,
KEYPW);
// An SSLContext is an environment for implementing
JSSE
// It is used to create a
ServerSocketFactory
SSLContext sslc =
SSLContext.getInstance("SSLv3");
// Initialize the SSLContext to
work with our key managers
sslc.init(kmf.getKeyManagers(), null,
null);
// Create a ServerSocketFactory from the
SSLContext
ServerSocketFactory ssf =
sslc.getServerSocketFactory();
// Socket to
me
SSLServerSocket serverSocket =
(SSLServerSocket) ssf.createServerSocket(serverPort);
//
Authenticate the
client?
serverSocket.setNeedClientAuth(requireClientAuthentication);
//
Return a ServerSocket on the desired port (443)
return
serverSocket;
}
}
Listing 6. A sample HTML file (index.htm).
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta
http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
/>
<title>Welcome to Java Security using
JSSE</title>
</head>
<body>
<h1>Welcome to
Java Security using JSSE</h1>
<p>This page was securely sent
using SSL version 3.0.</p>
</body>
</html>
Your Java applications may also require clients to support SSL. Client-side
SSL is even easier to support than server-side SSL. Listing 7 presents a basic
text-based Java browser. You can use it for browsing text-based Web pages. For
example, if you enter java Browser http://onjava.com, you'll get a
ton of HTML markup.
Listing 8 presents SecureBrowser, which extends Browser to
provide SSL support. You can run SecureBrowser against a site that
implements SSL, such as Sun's secure web site. For example, java
SecureBrowser https://www.sun.com will establish a secure connection to
Sun's web page and download a lot of markup securely.
The trick to implementing SSL on the client is to register JSSE and set the
java.protocol.handler.pkgs system property so that JSSE
automatically is used to handle https URLs.
Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl.internal.www.protocol");
You may wonder what happens when you run SecureBrowser against
SecureServer. It doesn't work. That's because
SecureBrowser won't accept SecureServer's phony
certificate. However, we can trick SecureBrowser into accepting
SecureServer's certificate. Here's how:
Use
keytoolto export the server certificate from the certs
keystore.
keytool -export -keystore certs -alias jamie -file server.cer
Enter keystore password: serverkspw
Certificate stored in file <server.cer>
Use keytool to create a new keystore named jssecacerts (which will be used as a truststore by SecureBrowser). Import server.cer into jssecacerts.
keytool -import -keystore jssecacerts -alias jamie -file
server.cer
Enter keystore password: 12345678
Owner:
CN=enpower, OU=Software Development, O=Toolery.com, L=Chula Vista, ST=CA,
C=US
Issuer: CN=enpower, OU=Software Development, O=Toolery.com, L=Chula
Vista, ST=CA, C=US
Serial number: 3ae5d0fc
Valid from: Tue Apr 24
12:16:12 PDT 2001 until: Mon Jul 23 12:16:12 PDT 2001
Certificate
fingerprints:
MD5:
A9:00:67:FF:7A:1B:D4:4A:D5:33:72:97:C5:88:0B:6D
SHA1:
16:40:79:8A:11:BC:F8:AE:96:0D:FF:30:46:B5:62:0F:E2:18:56:7F
Trust this
certificate? [no]: y
Certificate was added to keystore
Finally, copy jssecacerts to the lib/security
subdirectory of your java.home directory. (On your client
machine.)
Now SecureBrowser will use jssecacerts as a
truststore to authenticate SecureServer.
When I run SecureBrowser against SecureServer, I
get the following output:
java SecureBrowser https://enpower/
THE
HEADERS
-----------
KEY: Content-Length
VALUE: 487
KEY:
Content-Type
VALUE: text/html
THE CONTENT
-----------
<?xml
version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta
http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
/>
<title>Welcome to Java Security using
JSSE</title>
</head>
<body>
<h1>Welcome to
Java Security using JSSE</h1>
<p>This page was securely sent
using SSL version
3.0.</p>
</body>
</html>
Listing 7. A Basic Java Browser.
import java.io.*;
import java.net.*;
import
java.security.*;
// A simple text-based browser
public class Browser
{
String urlString;
// You must supply the URL
to be browsed
public static void main(String[] args) throws
Exception {
if(args.length != 1)
{
System.out.println("Usage: java Browser
url");
System.exit(1);
}
Browser browser = new
Browser(args[0]);
browser.run();
}
// Construct a browser object
public
Browser(String urlString) {
this.urlString =
urlString;
}
// Get the
URL
public void run() throws Exception
{
URL url = new
URL(urlString);
HttpURLConnection urlc =
(HttpURLConnection) url.openConnection();
System.out.println("THE HEADERS");
System.out.println("-----------");
for(int i=1;;++i)
{
String
key;
String
value;
if((key =
urlc.getHeaderFieldKey(i)) == null)
break;
if((value =
urlc.getHeaderField(i)) == null) break;
System.out.println("KEY: " + key);
System.out.println("VALUE: " + value);
}
BufferedReader reader = new
BufferedReader(
new
InputStreamReader(urlc.getInputStream()));
String
line;
System.out.println("THE
CONTENT");
System.out.println("-----------");
while((line =
reader.readLine()) != null)
System.out.println(line);
}
}
Listing 8. A Browser that Supports Basic SSL
import java.io.*;
import java.net.*;
import
java.security.*;
// Extend Browser to use SSL
public class
SecureBrowser extends Browser {
// Must supply URL in command
line
public static void main(String[] args) throws Exception
{
if(args.length != 1)
{
System.out.println("Usage: java
SecureBrowser
url");
System.exit(1);
}
SecureBrowser browser = new
SecureBrowser(args[0]);
browser.run();
}
// Construct a
SecureBrowser
public SecureBrowser(String urlString)
{
super(urlString);
//
Register JSSE
Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
// Here's
the trick!
// Simply set the protocol handler
property to use SSL.
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");
}
}
In some applications, client authentication is of paramount importance.
So far, we have SecureBrowser and SecureServer
running against each other. SecureServer provides its certificate
to SecureBrowser and SecureBrowser authenticates
SecureServer. Then they set up a secure communication channel. This
scenario is still a little one-sided. How does SecureServer know
that it is talking to SecureBrowser? That's where mutual
authentication comes in.
The first thing that we need to do is to create a certificate for our client. This is accomplished using the keytool.
keytool -genkey -keyalg rsa -alias jaworski
Enter keystore password: 12345678
What is your first and last name?
[Unknown]: libretto70ct
What is the name of your organizational unit?
[Unknown]: Software Development
What is the name of your organization?
[Unknown]: Toolery.com
What is the name of your City or Locality?
[Unknown]: Chula Vista
What is the name of your State or Province?
[Unknown]: CA
What is the two-letter country code for this unit?
[Unknown]: US
Is <CN=libretto70ct, OU=Software Development, O=Toolery.com,
L=Chula Vista, ST=CA, C=US> correct?
[no]: y
Enter key password for <jaworski>
(RETURN if same as keystore password):
I created the above certificate on another computer named Libretto70CT.
The next thing that we need to do is to export the certificate. Again, we'll use the keytool.
keytool -export -alias jaworski -file jj.cer
Enter keystore password: 12345678
Certificate stored in file <jj.cer>
Now, we import the certificate (jj.cer) into a keystore on the
server. This keystore will function as the server's truststore.
keytool -import -alias jaworski -file jj.cer
Enter keystore
password: 12345678
Owner: CN=libretto70ct, OU=Software Development,
O=Toolery.com, L=Chula Vista, ST=CA, C=US
Issuer: CN=libretto70ct,
OU=Software Development, O=Toolery.com, L=Chula Vista, ST=CA, C=US
Serial
number: 3ae65817
Valid from: Tue Apr 24 21:52:39 PDT 2001 until: Mon Jul 23
21:52:39 PDT 2001
Certificate fingerprints:
MD5:
BA:28:EC:44:B9:01:AA:6A:AF:3B:87:CB:73:26:6A:78
SHA1:
71:B5:C1:F3:88:78:1E:33:2F:0B:66:60:8D:20:71:E5:6D:89:71:00
Trust this
certificate? [no]: y
Certificate was added to keystore
The keystore that is created from the above command is the default keystore
named .keystore. Rename it to jssecacerts and put it
in the lib/security subdirectory of your java.home
directory. This will make your server aware of the client's key.
We want SecureBrowser to present its certificate to
SecureServer so that SecureServer can also
authenticate SecureBrowser. The change required on
SecureServer's part is minimal. Simply change the following line in
SecureServer's constructor from
this("SecureServer", "1.0", 443, false);
to
this("SecureServer", "1.0", 443, true);
This turns on mutual authentication in SecureServer.
On the client side, we must tell the underlying SSL implementation which
keystore to use and which password to use to access the keystore. This amounts
to setting the javax.net.ssl.keyStore and
javax.net.ssl.keyStorePassword properties. On my Libretto, the
default keystore that was created is named .keystore and is located
in the C:\Windows directory. Simply add the following two lines to
the bottom of the SecureBrowser constructor.
System.setProperty("javax.net.ssl.keyStore","c:\\windows\\.keystore");
System.setProperty("javax.net.ssl.keyStorePassword","12345678");
All that's left is to restart SecureServer and access it with
SecureBrowser. Make sure that you give SecureServer a
minute or two to start up. Then startup SecureBrowser on your
client machine. (I recommend using separate machines because it simplifies the
process of managing keystores and truststores.
java SecureBrowser https://enpower/
THE
HEADERS
-----------
KEY: Content-Length
VALUE: 487
KEY:
Content-Type
VALUE: text/html
THE CONTENT
-----------
<?xml
version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta
http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
/>
<title>Welcome to Java Security using
JSSE</title>
</head>
<body>
<h1>Welcome to
Java Security using JSSE</h1>
<p>This page was securely sent
using SSL version 3.0.</p>
</body>
</html>
SecureServer reports the transaction as follows:
java SecureServer
SecureServer version 1.0
SecureServer is
listening on port 443.
Accepted connection to LIBRETTO70CT (192.168.1.70) on
port 1098.
Received the following request:
GET / HTTP/1.1
User-Agent:
Java1.3.0_01
Host: enpower
Accept: text/html, image/gif, image/jpeg, *;
q=.2, */*; q=.2
Connection: keep-alive
File sent:
C:\WINDOWS\Desktop\JSSE\index.htm
Number of bytes: 487
Request completed.
Closing connection.
When you are finished with these examples, go back and delete all of the keystores that you've created to keep any trusted certificates from laying around on your system.
Jamie Jaworski is a Java security expert and author, focused on cryptography and such.
Return to ONJava.com.
oreillynet.com Copyright © 2000 O'Reilly & Associates, Inc.