Keystores and the Java Keytool Utility
Because the WS-Security specification depends on the use of encryption keys and certificates, it's useful to discuss a mechanism to generate and maintain them.
You can use the Java keytool utility, which ships with the JDK, to generate public/private key-pairs and certificates and maintain them in a password-protected keystore so that your Java programs can use them. A keystore is a standard, password-protected repository, also known as PKCS#12, which you can use to store and transport keys and certificates securely.
Creating a Keystore and Key-Pair
The keytool utility can generate a key pair. Typically, you must generate two key-pairs to use one as a certificate/public-key for the other; therefore, execute the keytool with the -genkey option twice, and store each distinct key-pair into a separate keystore.
Here's how to use the keytool utility to generate a key-pair as a private key.
| Author's Note: Enter the command lines shown below on a single line. |
%JAVA_HOME%\bin\keytool -genkey -alias privkey
-keystore privkeystore -dname "cn=privkey"
-keypass foobar -storepass foobar
To generate a key-pair to use as a certificate/public-key, use this code (again, enter the entire command on a single line). %JAVA_HOME%\bin\keytool -genkey -alias pubcert
-keystore pubcertkeystore -dname "cn=pubcert"
-keypass foobar -storepass foobar
The preceding commands %JAVA_HOME%\bin\keytool -list -keystore privkeystore
Enter keystore password: foobar
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
privkey, Jul 25, 2005, keyEntry,
Certificate fingerprint (MD5):
A1:FA:99:E2:A7:E8:1A:FB:D8:B7:87:91:D1:0E:9C:F8
Now, look at the pubcert certificate keystore: %JAVA_HOME%\bin\keytool -list -keystore pubcertkeystore
Enter keystore password: foobar
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
pubcert, Jul 25, 2005, keyEntry,
Certificate fingerprint (MD5):
99:8F:14:C5:BB:21:86:77:D2:CF:56:DE:98:DD:74:62
To examine a key in detail, you can use the keytool utility to display it to the console in RFC 1421 format using the -rfc option, as follows: %JAVA_HOME%\bin\keytool -export -keystore privkeystore
-alias privkey -storepass foobar --rfc
You'll see output on the console similar to the following: -----BEGIN CERTIFICATE-----
MIIBlTCB/wIEQuWjhTANBgkqhkiG9w0BAQQFADASMRAwDgYDVQQDEwd0ZXN
0a2V5MB4XDTA1MDcyNjAyNDQyMVoXDTA1MTAyNDAyNDQyMVowEjEQMA4GA1
UEAxMHdGVzdGtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAz/HFY
xicr+vonubY3rgnJFdl6OsvbinR2L54U7WKHNz2w7w3cOvTMGqop/xQtePx
k3hXIJFs27OBC28Y8jRKYdgGDYMVU5/V0ddlGQUgfU7Xy9jdIPm61ayu3QH
9LcXYSzVfHNeL3HHRcJV3jSwRs1K/vIVZKLNnBRufe2kORK0CAwEAATANBg
kqhkiG9w0BAQQFAAOBgQBWAoAzG5B54dNUt7t3iU98Dre0EI9JkEn8HYiix
oJxs1SmI/vESDbuAJY9EbjlPnvhHrgZL3rtb8twwzHwbLhnxVeV/LRk2C2e
ghkPPEklp3w+UVv5U3dsvoR6LO4z3fTjnc+YbMG0Iss5gkwxJqYy/6qeyYY
3EGoxl8Ehyu/hOw==
-----END CERTIFICATE-----
Self-Signing Certificates
Keys are unusable unless they are signed, but you can use the keytool to self-sign them (for testing purposes only), as follows:
%JAVA_HOME%\bin\keytool -selfcert -alias privkey
-keystore privkeystore -keypass foobar -storepass foobar
Now, the certificate can be self-signed, as follows: %JAVA_HOME%\bin\keytool -selfcert -alias pubcert
-keystore pubcertkeystore -keypass foobar
-storepass foobar
Exporting Certificates with the Keytool Utility %JAVA_HOME%\bin\keytool -export -keystore pubcertkeystore
-alias pubcert -storepass foobar -file pubcert
You should see a response that says: Certificate stored in file <pubcert>
You can also use the keytool utility to display the contents of the certificate file using the -printcert option, as follows: %JAVA_HOME%\bin\keytool -printcert -file pubcert
The output will look like: Owner: CN=pubcert
Issuer: CN=pubcert
Serial number: 42e5b3c4
Valid from: Mon Jul 25 21:53:40 MDT 2005 until:
Sun Oct 23 21:53:40 MDT 2005
Certificate fingerprints:
MD5:
99:8F:14:C5:BB:21:86:77:D2:CF:56:DE:98:DD:74:62
SHA1: EC:59:92:E9:1F:8A:A6:0A:85:54:EC:76:47:DB:5F:3F:D2:15:78:77
The exported certificate contains the public key and distinguished name given to the certificate (in this case, pubcert). %JAVA_HOME%\bin\keytool -import -alias pubcert
-file pubcert -keystore privkeystore -storepass foobar
The output looks like: Owner: CN=pubcert
Issuer: CN=pubcert
Serial number: 42e5b3c4
Valid from: Mon Jul 25 21:53:40 MDT 2005 until: Sun Oct 23 21:53:40 MDT 2005
Certificate fingerprints:
MD5:
99:8F:14:C5:BB:21:86:77:D2:CF:56:DE:98:DD:74:62
SHA1: EC:59:92:E9:1F:8A:A6:0A:85:54:EC:76:47:DB:5F:3F:D2:15:78:77
Answer the following question:
Trust this certificate? [no]: yes
Certificate was added to keystore
Now that the certificate has been imported into the private key's keystore, you can reexamine the contents of the keystore using the keytool utility with the -list option, as follows: %JAVA_HOME%\bin\keytool -list -keystore privkeystore
Enter keystore password: foobar
After entering your password you'll see the following output:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 2 entries
privkey, Jul 25, 2005, keyEntry,
Certificate fingerprint (MD5):
E7:4A:D9:D7:67:A6:6D:E7:A5:C4:28:22:3D:C5:C4:30
pubcert, Jul 25, 2005, trustedCertEntry,
Certificate fingerprint (MD5):
99:8F:14:C5:BB:21:86:77:D2:CF:56:DE:98:DD:74:62
As the preceding examples illustrated, there are now two entries in the private-key's keystore. The first, with the alias testkey, is identified as a key entry. The second entry is the certificate from the certificate file.
Using the WSS4J Framework
Apache's WSS4J is a Java implementation of the OASIS Web Services Security (WS-Security) specification. WSS4J is a framework that you can use to sign and verify SOAP messages with WS-Security information. WSS4J uses the Apache Axis and Apache XML-Security projects and is interoperable with JAX-RPC server/clients and .NET server/clients. WSS4J implements Username Token profile V1.0 and X.509 Token Profile V1.0. WSS4J can generate and process the following SOAP Bindings:
:pserver:anoncvs@cvs.apache.org:/home/cvspublic.
The tools in the next sections demonstrate how to use WSS4J to generate XML that conforms to the latest WS-Security specification. private static final WSSecurityEngine secEngine =
new WSSecurityEngine();
Next, create a crypto provider. The default factory getInstance()method creates a provider according to the class name specified by the system property org.apache.ws.security.crypto.provider. If the provider property is not set, the getInstance() method creates a default class instance, org.apache.ws.security.components.crypto.BouncyCastle. private static final Crypto crypto =
CryptoFactory.getInstance();
You use the AxisClient as the context engine for messaging operations. private AxisClient engine = null;
private MessageContext msgContext = null;
public WSSecuritySample()
{
engine = new AxisClient(new NullProvider());
msgContext = new MessageContext(engine);
}
Creating the Target SOAP Envelope private Message getAxisMessage(String unsignedEnvelope)
{
InputStream inStream =
new ByteArrayInputStream(
unsignedEnvelope.getBytes());
Message axisMessage = new Message(inStream);
axisMessage.setMessageContext(msgContext);
return axisMessage;
}
The SOAP envelope used in this article and passed to the getAxisMessage method shown above is illustrated as follows: <SOAP-ENV:Envelope
xmlns:SOAP-ENV=http://www.w3.org/2003/05/soap-envelope
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<sayHello xmlns=
"http://jeffhanson.com/services/helloworld">
<value xmlns="">
Hello world!
</value>
</sayHello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Signing a SOAP Message public Message signSOAPEnvelope(SOAPEnvelope
unsignedEnvelope) throws Exception
{
WSSignEnvelope signer = new WSSignEnvelope();
String alias = "16c73ab6-b892-458f-abf5-2f875f74882e";
String password = "security";
signer.setUserInfo(alias, password);
Document doc = unsignedEnvelope.getAsDocument();
The "build" method creates the signed SOAP envelope. It takes a SOAP Envelope as a W3C Document and adds a WSS Signature header to it. The signed elements depend on the signature parts specified by the WSBaseMessage.setParts(java.util.Vector parts) method. By default, it signs the SOAP Body element. Document signedDoc = signer.build(doc, crypto);
// Convert the signed document into a SOAP message.
Message signedSOAPMsg =
(org.apache.axis.Message)AxisUtil.toSOAPMessage(signedDoc);
return signedSOAPMsg;
}
Listing 1 shows a signed SOAP envelope as returned from the preceding method. public Message encryptSOAPEnvelope(
SOAPEnvelope unsignedEnvelope, Message axisMessage)
throws Exception
{
WSEncryptBody encrypt = new WSEncryptBody();
encrypt.setUserInfo(
"16c73ab6-b892-458f-abf5-2f875f74882e");
// build the encrypted SOAP part
Document doc = unsignedEnvelope.getAsDocument();
Document encryptedDoc = encrypt.build(doc, crypto);
// Convert the document into a SOAP message
Message encryptedMsg =
(Message)AxisUtil.toSOAPMessage(encryptedDoc);
// Retrieve the desired SOAP part
String soapPart = encryptedMsg.getSOAPPartAsString();
((SOAPPart)axisMessage.getSOAPPart()).
setCurrentMessage(soapPart, SOAPPart.FORM_STRING);
encryptedDoc =
axisMessage.getSOAPEnvelope().getAsDocument();
// Convert the document into a SOAP message
Message encryptedSOAPMsg =
(Message)AxisUtil.toSOAPMessage(encryptedDoc);
return encryptedSOAPMsg;
}
The SOAP envelope contained within the Axis message returned from the preceding method will look like Listing 4.
public static void main(String[] args)
{
try
{
WSSecuritySample app = new WSSecuritySample();
Message axisMessage = app.getAxisMessage(soapMsg);
SOAPEnvelope unsignedEnvelope =
axisMessage.getSOAPEnvelope();
System.out.println(
"<<< Unsigned and Unencrypted >>>");
XMLUtils.PrettyElementToWriter(
unsignedEnvelope.getAsDOM(),
new PrintWriter(System.out));
Message samlMsg =
app.addUserTokens(unsignedEnvelope);
System.out.println("\n<<< User Tokens >>>");
XMLUtils.PrettyElementToWriter(
samlMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
Message encryptedMsg = app.encryptSOAPEnvelope(
unsignedEnvelope, axisMessage);
System.out.println("\n<<< Encrypted >>>");
XMLUtils.PrettyElementToWriter(
encryptedMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
Message signedMsg =
app.signSOAPEnvelope(unsignedEnvelope);
System.out.println("\n<<< Signed >>>");
XMLUtils.PrettyElementToWriter(
signedMsg.getSOAPEnvelope().getAsDOM(),
new PrintWriter(System.out));
}
catch (Exception e)
{
e.printStackTrace();
}
}
Although the process may initially seem complex, a method such as the main method shown above simplifies the process considerably, breaking it down neatly into just a few steps: creating a SOAP envelope, and then signing, encrypting, and adding username tokens to it. I urge you to download the sample code for this article and experiment with the process. The WSS4J framework provides the core methods you need to meet the WS-Security specifications.
| DevX is a division of Jupitermedia Corporation © Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices |