 | Level: Intermediate Todd Sundsted (todd-p2p@etcee.com), Chief Architect, PointFire
01 Oct 2001 A core requirement of any non-trivial P2P application is secure communication between peers. While the details of the security depend on how the application will be used and on what it will protect, it's often possible to implement strong, general-purpose security using off-the-shelf technology such as SSL. This month, Todd Sundsted demonstrates how to use SSL (via JSSE) in P2P security.
Last month we looked at the role of trust in P2P applications.
Level of trust measures how confident we are that
we are communicating with whom we think we are and are accessing the
resources we think we are. We also examined the three building blocks
used to establish trust in all distributed applications, including P2P
applications: authentication, authorization, and encryption. Now we'll put last month's lesson into practice by modifying
our simple P2P application. Specifically, we'll extend the application
to support P2P authentication and encryption using X.509 certificates.
We'll tackle authorization in a future article. Secure authentication
An application's security requirements depend heavily on how the
application will be used and on what it will protect. Nevertheless,
it's often possible to implement strong, general purpose security
using off-the-shelf technology. Authentication is an excellent
example. When a customer attempts to purchase a product from a Web site,
both the customer and the Web site perform authentication. The
customer typically authenticates himself by supplying a name and a
password. The Web site, on the other hand, authenticates itself by
exchanging a block of signed data and a valid X.509 certificate as
part of an SSL handshake. The customer's browser verifies the
certificate and uses the enclosed public key to verify the signed
data. Once both sides are authenticated, commerce can begin. SSL can handle both server authentication (as in the example
above) and client authentication using the same mechanism. Web sites
typically don't rely on SSL for client authentication -- it's easier to
require the user to supply a password. However, SSL client and server
authentication is perfect for the transparent authentication that must
happen between peers -- like those in P2P applications.
Secure Sockets Layer (SSL)
SSL is a security protocol that provides privacy for
communications over networks like the Internet. SSL allows
applications to communicate without fear of eavesdropping or
tampering. SSL is actually two protocols that work together: the SSL Record
Protocol and the SSL Handshake Protocol. The SSL Record Protocol, the
lower level of the two protocols, encrypts and decrypts variable
length records of data for higher level protocols, such as the SSL
Handshake Protocol. The SSL Handshake Protocol handles the exchange
and verification of application credentials. When an application (the client) wants to communicate with another
application (the server) the client opens a socket connection with the
server. The client and the server then negotiate a secure connection.
As part of this negotiation, the server authenticates itself to the
client. The client can optionally authenticate itself to the server.
Once authentication is complete and the secure connection established,
the two applications can communicate securely. See Resources for additional information on SSL. By convention, I'll consider the peer who initiated the
communication to be the client and the other the server, regardless of
the role they assume after the connection.
How to get SSL in Java applications
SSL for Java applications is provided by Java Secure
Socket Extension (JSSE). JSSE is a standard part of the recently released
JDK 1.4 Beta, but is available as an extension to earlier versions of
the Java platform. JSSE uses SSL as the underlying mechanism for its secure sockets.
JSSE secure sockets work like regular sockets except they support
transparent authentication and encryption. Because they also look
like ordinary sockets (they are subclasses of class
java.net.Socket and class java.net.ServerSocket)
most of the code that uses JSSE is oblivious to the change. The code that's
affected the most is the code that handles the creation and initialization
of the secure socket factories. If you intend to use JSSE with a Java platform prior to version 1.4,
you'll have to download and install the JSSE extension yourself (see
Resources). The installation instructions are
quite straightforward, so I won't repeat them here.
The model
Figure 1 illustrates the role SSL will play in the communication
between peers. Figure 1. SSL in action

Two peers, known as A and B, desire to communicate securely.
In the context of our simple P2P application, peer A wants to query a
resource on peer B. Each peer has a database (known as a keystore) containing its
private key and a certificate containing its public key. A password
protects the contents of the database. The database also contains
one or more self-signed certificates from trusted peers. Peer A initiates the transaction, each peer authenticates the
other, both peers negotiate the cipher to use and its strength
and establish a secure channel. After these operations are complete,
each peer knows whom they are talking to and that the channel is
secure.
Initialization
Because the introduction of JSSE and SSL most significantly
affects the initialization code, let's take a look at the code
in peer A that's responsible for initialization. Listing 1. Security initialization code
// Each peer has an identity that must be locally (but not globally)
// unique. This identity and its associated public and private keys
// are stored in a keystore and protected by a password. Each
// peer also has a name that must be globally unique.
String stringIdentity = null;
String stringPassword = null;
String stringName = null;
// The code that prompts the user for his/her identity
// and password goes here. the user's name is
// generated (if necessary) later.
// Create home directory. This is a very portable way
// to create a home directory, but it has its problems --
// the various flavors of Microsoft Windows put the directory
// in widely different locations in the directory hierarchy.
String stringHome = System.getProperty("user.home") + File.separator + "p2p";
File fileHome = new File(stringHome);
if (fileHome.exists() == false)
fileHome.mkdirs();
// Create keystore. We must run an external process to create the
// keystore, because the security APIs don't expose enough
// functionality to do this inline. I haven't tested this widely enough
// to know how portable this code is, but it works on everything I
// tried it on.
String stringKeyStore = stringHome + File.separator + "keystore";
File fileKeyStore = new File(stringKeyStore);
if (fileKeyStore.exists() == false)
{
System.out.println("Creating keystore...");
byte [] arb = new byte [16];
SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
securerandom.nextBytes(arb);
stringName = new String(Base64.encode(arb));
String [] arstringCommand = new String []
{
System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool",
"-genkey",
"-alias", stringIdentity,
"-keyalg", "RSA",
"-keysize", "1024",
"-dname", "CN=" + stringName,
"-keystore", stringHome + File.separator + "keystore",
"-keypass", stringPassword,
"-storetype", "JCEKS",
"-storepass", stringPassword
};
Process process = Runtime.getRuntime().exec(arstringCommand);
process.waitFor();
InputStream inputstream2 = process.getInputStream();
IOUtils.copy(inputstream2, System.out);
InputStream inputstream3 = process.getErrorStream();
IOUtils.copy(inputstream3, System.out);
if (process.exitValue() != 0)
System.exit(-1);
}
// Once the application has created/located the keystore, it
// opens it and creates a KeyStore instance from the data
// in it.
char [] archPassword = stringPassword.toCharArray();
FileInputStream fileinputstream = new FileInputStream(stringHome + File.separator +
"keystore");
KeyStore keystore = KeyStore.getInstance("JCEKS");
try
{
keystore.load(fileinputstream, archPassword);
}
catch (IOException ioexception)
{
System.out.println("Cannot load keystore. Password may be wrong.");
System.exit(-3);
}
if (keystore.containsAlias(stringIdentity) == false)
{
System.out.println("Cannot locate identity.");
System.exit(-2);
}
// Create key manager. The key manager holds this peer's
// private key.
KeyManagerFactory keymanagerfactory = KeyManagerFactory.getInstance("SunX509");
keymanagerfactory.init(keystore, archPassword);
KeyManager [] arkeymanager = keymanagerfactory.getKeyManagers();
// Create trust manager. The trust manager hold other peers'
// certificates.
TrustManagerFactory trustmanagerfactory = TrustManagerFactory.getInstance("SunX509");
trustmanagerfactory.init(keystore);
TrustManager [] artrustmanager = trustmanagerfactory.getTrustManagers();
// Create SSL context.
SSLContext sslcontext = SSLContext.getInstance("SSL");
SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
sslcontext.init(arkeymanager, artrustmanager, securerandom);
// Create factories.
m_socketfactory = sslcontext.getSocketFactory();
m_serversocketfactory = sslcontext.getServerSocketFactory();
m_keystore = keystore;
|
When a user first launches the application, the application
prompts the user for an identity (a nickname) and a password. The
identity is only used to identify peers locally -- it has no global
significance. The application generates a random 128-bit (16 byte)
string that it uses to globally identify peers and converts it to an
alphanumeric string. It uses the identity, the password, and the name
to create a keystore and a public/private key pair, which it stores in
the keystore. The password protects the information in the
keystore. I'm sure you noticed the approach I took to create the initial
keystore. The application launches keytool as an external process and
keytool creates the keystore. I had to do it this way because the
public Java security APIs don't provide tools for creating
certificates -- that functionality is hidden within JSSE and its APIs
aren't published. The biggest drawback associated with launching the
keytool to do the work is the risk that the user-supplied password,
which is passed in as part of the parameter list, may be exposed. After the application creates the keystore, it opens the keystore
and loads it into memory (if we had been able to create the keystore
directly, we could have avoided this step). It creates a key
manager and a trust manager from the keystore. Key
managers manage the keys that are used to authenticate the application
to its peer across a secure socket. Trust managers manage the
certificates that are used to authenticate the peer sitting on the
other side of the secure socket. Finally, the application creates an SSLContext
instance, which acts as a factory for secure socket factories. It
creates secure versions of the SocketFactory and
ServerSocketFactory classes and uses them for later
communication.
User interface
Most of the rest of the code is the same as our previous release. (See "The P2P application framework" for details. Download the complete source below.) That's one of the advantages JSSE brings to the table --
secure sockets created with JSSE look and work just like regular
sockets. None of the supporting code (beyond creation and
initialization has to change). There are, however, changes related to
the user interface. Most of these changes were inspired by my desire
to make the application easier to use for those readers who simply
want to quickly set up the application so that they can try it
out. In response to reader feedback, I improved the command line
interface. Peers and their resources exist in a hierarchical
structure that users explore with the commands ls and
cd. The ls command lists the contents of a particular
point on the hierarchy (like the ls command does in UNIX or the
dir command does in DOS) and the cd command changes the
current location in the hierarchy (once again, like the commands of
the same name do in UNIX and DOS). cd .. backs down the
hierarchy. To make the application easier to use, I've created two
zip files, each containing a properly configured instance of the
application. One instance communicates on port 7776, the other on
port 7777. They have also been configured to authenticate each other
so that a secure channel can be established. The zip files are named
peerA.zip and peerB.zip (see Resources). More detailed usage instructions are provided in the README.
Conclusion
The addition of SSL to a P2P application is an excellent way to
provide simple yet strong security. Next month, we'll continue our P2P travels by looking at more sophisticated methods of peer discovery.
Download | Description | Name | Size | Download method |
|---|
| Sample code | j-p2pssl.zip | 120 KB | HTTP |
|---|
Resources - You can use JSSE as part of Java 1.4 beta or as a separate extension in previous versions.
- The IBM Research Division pursues myriad projects on the topic of security. Read more about IBM Research's efforts in Security Research in Java and Distributed Object Systems.
- Never used sockets before? Take this hands-on Java sockets tutorial (developerWorks, August 2001), which will teach you how to use sockets to handle typical scenarios that crop up in the real world.
- Joseph Sinclair wrote an interesting series for developerWorks on security in high-stakes systems. Part 1 (March 2001) examines how Java technology can be used to secure systems in which the consequences of mistaken identity can be particularly destructive. Part 2 (June 2001) discusses three familiar approaches for identifying users, highlighting their strengths and weaknesses.
- developerWorks hosts an entire topic area devoted to the practice of security.
- Find more Java resources on the developerWorks Java technology zone.
- Read all of the articles in Todd Sundsted's
The practice of peer-to-peer computing
column on developerWorks.
About the author  | |  |
Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed applications in C++, Todd moved on to the Java programming language when it became the obvious choice for that sort of thing. In addition to writing, Todd is co-founder and chief architect of PointFire, Inc. Contact Todd at todd-p2p@etcee.com.
|
Rate this page
|  |