WS-Security is a family of specifications (see Figure 6-9) designed to augment wire-level security (e.g., HTTPS) and container-managed security (e.g., Tomcat) by providing a unified, transport-neutral, container-neutral, end-to-end framework for higher levels of security such as message confidentiality and authentication/authorization.
The layered blocks above WS-Security in Figure 6-9 can be clarified briefly as follows. The first layer consists of WS-Policy, WS-Trust, and WS-Privacy. The second layer of WS-SecureConversation, WS-Federation, and WS-Authorization builds upon this first layer. The architecture is thus modular but also complicated. Here is a short description of each specification, starting with the first layer:
WS-Security is often associated with federated security in the broad sense, which has the goal of cleanly separating web service logic from the high-level security concerns, in particular authentication/authorization, that challenge web service deployment. This separation of concerns is meant to ease collaboration across computer systems and trust realms.
Recall that SOAP-based web services are meant to be transport-neutral. Accordingly, SOAP-based services cannot depend simply on the reliable transport that HTTP and HTTPS provide, although most SOAP messages are transported over HTTP. HTTP and HTTPS rest on TCP/IP (Transmission Control Protocol/Internet Protocol), which supports reliable messaging. What if TCP/IP infrastructure is not available? The WS-ReliableMessaging specification addresses precisely the issue of delivering SOAP-based services over unreliable infrastructure.
A SOAP-based service can rely on the authentication/authorization support that a web container such as Tomcat or an application server such as Oracle WebLogic, JBoss, GlassFish, or WebSphere may provide. In this case, the service outsources users/roles security to the service container. The WS-Security specifications are a guide to how security in general can be handled from within SOAP messaging. Accordingly, the WS-Security specifications address security issues as part of SOAP itself rather than as the part of the infrastructure that happens to be in place for a particular SOAP-based service. The goals of WS-Security are often summarized with the phrase end-to-end security, which means that security matters are not delegated to either the transport level (e.g., HTTPS) or a particular service container (e.g., Tomcat) but, rather, handled directly through an appropriate security API. A framework for end-to-end security needs to cover the situation in which a message is routed through intermediaries, each of which may have to process the message, before reaching the ultimate receiver; thus, end-to-end security focuses on message content rather than on the underlying transport or the service container. As a result, SOAP messaging becomes considerably more complicated.
In order to focus squarely on WS-Security, the sample web service (see Example 6-16) is deliberately bare bones.
Further, the Endpoint
publisher is used to host the service despite the fact that Endpoint
supports neither
wire-level security nor users/roles authentication and authorization. The very point of WS-Security is to provide security
within SOAP messaging. The Echo
service focuses on how WS-Security supports user authentication in particular.
The publisher (see Example 6-17) first sets an Echo
instance as the service endpoint (line 1)
and then gets the Binding
(line 2) in order to register a service-side handler (lines 3, 4, and 5).
The publisher finishes its work by publishing the service at the specified URL (line 6).
Example 6-16. The bare-bones Echo service
package
echoService
;
import
javax.jws.WebService
;
import
javax.jws.WebMethod
;
@WebService
public
class
Echo
{
@WebMethod
public
String
echo
(
String
msg
)
{
return
"Echoing: "
+
msg
;
}
}
The Echo
class gives no hint of WS-Security, which is
delegated to the handler level, in this case to the message handler ServiceHandler
.
This separation of concerns means that, at the application level, the Echo service looks like any other
@WebService
: the service is a collection of operations, in this case only the echo
method.
Example 6-17. The EchoPublisher
, which publishes the Echo service
package
echoService
;
import
javax.xml.ws.Endpoint
;
import
javax.xml.ws.Binding
;
import
java.util.List
;
import
java.util.LinkedList
;
import
javax.xml.ws.handler.Handler
;
public
class
EchoPublisher
{
public
static
void
main
(
String
[
]
args
)
{
Endpoint
endpoint
=
Endpoint
.
create
(
new
Echo
());
![]()
Binding
binding
=
endpoint
.
getBinding
();
![]()
List
<
Handler
>
hchain
=
new
LinkedList
<
Handler
>();
![]()
hchain
.
add
(
new
ServiceHandler
());
![]()
binding
.
setHandlerChain
(
hchain
);
![]()
endpoint
.
publish
(
"http://localhost:7777/echo"
);
![]()
System
.
out
.
println
(
"http://localhost:7777/echo"
);
}
}
At this point, a shift to the client side may be helpful because the client, too, has a handler; the service-side handler validates the information that the client-side handler puts into the SOAP request message. The client-side handler inserts a username and a password into the header of every SOAP request from the client. The service-side handler then verifies the identity of the user by using the password as the credential that vouches for the identity. The client’s request hits the Echo service only if the service-side handler is successful in its verification.
On the client side and on the service side, the labor is divided in similar ways. The client-side
message handler inserts the username and password into the outgoing SOAP message but relies upon the
Prompter
, which in turn is a CallbackHandler
, to prompt for and read in the username and password;
this CallbackHandler
obscures but, in this example, does not encrypt the password.
The client-side message handler also inserts other security information into the SOAP request message
(see Figure 6-10).
On the service side, the message handler delegates verification to a CallbackHandler
of
its own, the Verifier
(see Figure 6-11). The Verifier
, in turn, relies on other CallbackHandler
instances
to extract the authentication information and to verify the sent username/password against
service-side copies of these. The architecture on the service side thereby complements the architecture on the client side.
An examination of a familiar request/response exchange, starting from a client request through the service response,
should cast light on the implementation details.
The EchoClientWSS
client (see Example 6-18) relies on the usual wsimport-generated artifacts (lines 1 and 2)
to get a port
reference (line 3), which is cast to the data type BindingProvider
(line 4) so that the client-side
SOAPHandler
, an instance of the ClientHandler
class, can be linked dynamically with the client (line 5). With
this setup in place, the EchoClientWSS
then makes a call against the Echo service (line 6) and
prints the response for confirmation (line 7). All of the WS-Security code is relegated to the
ClientHandler
.
Example 6-18. The sample EchoClientWSS
against the Echo service
import
java.util.List
;
import
java.util.LinkedList
;
import
javax.xml.ws.handler.Handler
;
import
javax.xml.ws.BindingProvider
;
import
javax.xml.ws.Binding
;
import
echoClient.EchoService
;
![]()
import
echoClient.Echo
;
![]()
public
class
EchoClientWSS
{
public
static
void
main
(
String
[
]
args
)
{
try
{
List
<
Handler
>
hchain
=
new
LinkedList
<
Handler
>();
hchain
.
add
(
new
ClientHandler
());
EchoService
service
=
new
EchoService
();
Echo
port
=
service
.
getEchoPort
();
![]()
Binding
binding
=
((
BindingProvider
)
port
).
getBinding
();
![]()
binding
.
setHandlerChain
(
hchain
);
![]()
String
response
=
port
.
echo
(
"Goodbye, cruel world!"
);
![]()
System
.
out
.
println
(
"From Echo service: "
+
response
);
![]()
}
catch
(
Exception
e
)
{
throw
new
RuntimeExceptione
(
e
);
}
}
}
The ClientHandler
receives, from the underlying SOAP libraries on the client side, a SOAP message that
represents a call to the echo operation in the Echo service. This message is
passed to the ClientHandler
(see Example 6-19), which does the WS-Security
work. The result of this work impacts only the SOAP header, not the
SOAP body; hence, the ClientHandler
must be a SOAPHandler
in order to access the SOAP header.
Example 6-19. The client-side ClientHandler
, which uses the Prompter
import
java.util.Set
;
import
java.util.HashSet
;
import
javax.xml.namespace.QName
;
import
javax.xml.soap.SOAPMessage
;
import
javax.xml.ws.handler.MessageContext
;
import
javax.xml.ws.handler.soap.SOAPHandler
;
import
javax.xml.ws.handler.soap.SOAPMessageContext
;
import
java.io.FileInputStream
;
import
java.io.File
;
import
com.sun.xml.wss.ProcessingContext
;
import
com.sun.xml.wss.SubjectAccessor
;
import
com.sun.xml.wss.XWSSProcessorFactory
;
import
com.sun.xml.wss.XWSSProcessor
;
public
class
ClientHandler
implements
SOAPHandler
<
SOAPMessageContext
>
{
private
XWSSProcessor
xwssClient
;
private
boolean
trace
;
public
ClientHandler
()
{
XWSSProcessorFactory
fact
=
null
;
try
{
fact
=
XWSSProcessorFactory
.
newInstance
();
![]()
FileInputStream
config
=
new
FileInputStream
(
new
File
(
"client.xml"
));
![]()
xwssClient
=
![]()
fact
.
createProcessorForSecurityConfiguration
(
config
,
new
Prompter
());
config
.
close
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
trace
=
true
;
// set to true to enable message dumps
}
// Add a security header block
public
Set
<
QName
>
getHeaders
()
{
![]()
String
uri
=
"http://docs.oasis-open.org/wss/2004/01/"
+
"oasis-200401-wss-wssecurity-secext-1.0.xsd"
;
QName
securityHdr
=
new
QName
(
uri
,
"Security"
,
"wsse"
);
HashSet
<
QName
>
headers
=
new
HashSet
<
QName
>();
headers
.
add
(
securityHdr
);
return
headers
;
}
public
boolean
handleMessage
(
SOAPMessageContext
msgCtx
)
{
Boolean
outbound
=
(
Boolean
)
msgCtx
.
get
(
MessageContext
.
MESSAGE_OUTBOUND_PROPERTY
);
SOAPMessage
msg
=
msgCtx
.
getMessage
();
if
(
outbound
.
booleanValue
())
{
ProcessingContext
pCtx
=
null
;
try
{
pCtx
=
xwssClient
.
createProcessingContext
(
msg
);
![]()
pCtx
.
setSOAPMessage
(
msg
);
![]()
SOAPMessage
secureMsg
=
xwssClient
.
secureOutboundMessage
(
pCtx
);
![]()
msgCtx
.
setMessage
(
secureMsg
);
![]()
if
(
trace
)
dump
(
"Outgoing message:"
,
secureMsg
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
return
true
;
}
public
boolean
handleFault
(
SOAPMessageContext
msgCtx
)
{
return
true
;
}
public
void
close
(
MessageContext
msgCtx
)
{
}
private
void
dump
(
String
msg
,
SOAPMessage
soapMsg
)
{
try
{
System
.
out
.
println
(
msg
);
soapMsg
.
writeTo
(
System
.
out
);
System
.
out
.
println
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
}
The ClientHandler
no-argument constructor creates an XWSSProcessor
(lines 1 through 3),
which generates the WS-Security artifacts that go into the revised SOAP message. Two arguments
are required for the creation of the XWSSProcessor
: a file from which configuration
information can be read and a CallbackHandler
, in this case the Prompter
, that provides the
username and password. The configuration file is minimalist:
<
xwss:
SecurityConfiguration
xmlns:
xwss
=
"http://java.sun.com/xml/ns/xwss/config"
dumpMessages
=
"true"
>
<
xwss:
UsernameToken
digestPassword
=
"false"
/>
</
xwss:
SecurityConfiguration
>
The code for the Prompter
is examined shortly.
A SOAP message handler must define four methods: getHeaders
, handleMessage
, handleFault
, and
close
. Of the four methods, getHeaders
executes first.
Earlier examples of SOAP handlers defined the getHeaders
method but never put this method
to work. In this case, the getHeaders
method (line 4) is put to work—the method adds an
empty header block in the SOAP message:
<
S:
Header
>
<
wsse:
Security
xmlns:
wsse
=
"http://docs.oasis-open.org/ \
wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
S:
mustUnderstand
=
"1"
>
</
wsse:
Security
>
</
S:
Header
>
Although this header block is empty, it does contain the mustUnderstand
attribute, with a value of
1
for true; WS-Security requires the attribute. Once the getHeaders
method has done its part,
the handleMessage
method takes over to complete the work. This method creates a WS-Security
processing context (line 5) that is used to transform the current SOAP message (line 6), with its newly added
wsse:Security
header block, into a secured SOAP message whose header contains the username and
password (lines 7 and 8). Behind the scenes, the Prompter
instance works with the XWSSProcessor
to provide
the required username and password. When the handleMessage
method exits, the SOAP message has been
transformed into something much larger.
The client-side SOAP message before the handler operates is small (see Example 6-20), but this message becomes significantly larger after the handler has done its work (see Example 6-21).
Example 6-20. The SOAP request before the ClientHandler
transforms the message
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
S:
Envelope
xmlns:
S
=
"http://schemas.xmlsoap.org/soap/envelope/"
>
<
S:
Body
>
<
ns2:
echo
xmlns:
ns2
=
"http://echoService/"
>
<
arg0
>
Goodbye
,
cruel
world
!</
arg0
>
</
ns2:
echo
>
</
S:
Body
>
</
S:
Envelope
>
Example 6-21. The SOAP request after the ClientHandler
transforms the message
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
S:
Envelope
xmlns:
S
=
"http://schemas.xmlsoap.org/soap/envelope/"
>
<
S:
Header
>
<
wsse:
Security
xmlns:
wsse
=
"http://docs.oasis-open.org/wss/2004/01/\
oasis-200401-wss-wssecurity-secext-1.0.xsd"
S:
mustUnderstand
=
"1"
>
<
wsse:
UsernameToken
xmlns:
wsu
=
"http://docs.oasis-open.org/wss/2004/01/\
oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:
Id
=
"XWSSGID-1365549760320-535388749"
>
<
wsse:
Username
>
fred
</
wsse:
Username
>
![]()
<
wsse:
Password
![]()
Type
=
"http://docs.oasis-open.org/wss/2004/01/\
oasis-200401-wss-username-token-profile-1.0#PasswordText"
>
****
</
wsse:
Password
>
<
wsse:
Nonce
![]()
EncodingType
=
"http://docs.oasis-open.org/wss/2004/01/\
oasis-200401-wss-soap-message-security-1.0#Base64Binary"
>
Q945eYMcu3NWuq90IjmNXjDy
</
wsse:
Nonce
>
<
wsu:
Created
>...</
wsu:
Created
>
</
wsse:
UsernameToken
>
</
wsse:
Security
>
</
S:
Header
>
<
S:
Body
>
<
ns2:
echo
xmlns:
ns2
=
"http://echoService/"
>
<
arg0
>
Goodbye
,
cruel
world
!</
arg0
>
</
ns2:
echo
>
</
S:
Body
>
</
S:
Envelope
>
The outgoing SOAP request now has, in the header, three items of security interest:
EchoClientWSS
sends a request
to the Echo service, the username and password might be the same but the nonce differs
from preceding ones; the WSS libraries on the service side validate the nonce.
The low-level work on the client side falls to the Prompter
class (see Example 6-22), which implements
the CallbackHandler
interface by defining the handle
method. The details are tedious but the
gist is clear: the Prompter
, in a production environment, would prompt for a username and
password by using a UsernameCallback
(line 1) and a PasswordCallback
, respectively. The
XWSSProcessor
, which has access to the two callbacks through the processing context,
extracts the username and password so that these can be inserted into the outgoing SOAP message.
Example 6-22. The Prompter
callback handler, which helps the ClientHandler
import
javax.security.auth.callback.Callback
;
import
javax.security.auth.callback.CallbackHandler
;
import
com.sun.xml.wss.impl.callback.PasswordCallback
;
import
com.sun.xml.wss.impl.callback.PasswordValidationCallback
;
import
com.sun.xml.wss.impl.callback.UsernameCallback
;
import
java.io.BufferedReader
;
import
java.io.InputStreamReader
;
// For ease of testing, the username and password are
// hard-wired in the handle method with local variables
// username and password. For production, the hard wirings
// would be removed.
public
class
Prompter
implements
CallbackHandler
{
private
String
readLine
()
{
String
line
=
null
;
try
{
line
=
new
BufferedReader
(
new
InputStreamReader
(
System
.
in
)).
readLine
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
return
line
;
}
// Prompt for and read the username and the password.
public
void
handle
(
Callback
[
]
callbacks
)
{
try
{
for
(
int
i
=
0
;
i
<
callbacks
.
length
;
i
++)
{
if
(
callbacks
[
i
]
instanceof
UsernameCallback
)
{
UsernameCallback
cb
=
(
UsernameCallback
)
callbacks
[
i
];
![]()
/* Disable for testing.
System.out.print("Username: ");
String username = readLine();
*/
String
username
=
"fred"
;
// hard-wire for testing
if
(
username
!=
null
)
cb
.
setUsername
(
username
);
}
else
if
(
callbacks
[
i
]
instanceof
PasswordCallback
)
{
PasswordCallback
cb
=
(
PasswordCallback
)
callbacks
[
i
];
![]()
/* Disable for testing
System.out.print("Password: ");
String password = readLine();
*/
String
password
=
"rockbed"
;
// hard-wire for testing
if
(
password
!=
null
)
cb
.
setPassword
(
password
);
}
}
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
}
On the client side, the XWSSProcessor
could do more than it does in this example. For instance,
the security processor could encrypt the blocks in the SOAP header, particularly the one with the password,
and encrypt even the payload in the
SOAP body. However, this first look at WS-Security is focused on the architecture and flow of control, and these additional steps
would distract from that focus. It is now time to move over to the service side.
On the service side, the incoming SOAP message goes to the ServiceHandler
(see Example 6-23), which
verifies the security header blocks that the ClientHandler
injects into the SOAP request. This handler
also pares down the incoming message (see Example 6-21) to an ordinary-looking SOAP request:
<
S:
Envelope
xmlns:
S
=
"http://schemas.xmlsoap.org/soap/envelope/"
>
<
S:
Header
/>
<
S:
Body
>
<
ns2:
echo
xmlns:
ns2
=
"http://echoService/"
>
<
arg0
>
Goodbye
,
cruel
world
!</
arg0
>
</
ns2:
echo
>
</
S:
Body
>
</
S:
Envelope
>
This is almost the very request that the EchoClientWSS
generates before the client-side handler
goes into action. The one difference is that the pared-down, incoming message has a SOAP header—but an empty one.
Example 6-23. The service-side ServiceHandler
package
echoService
;
import
java.util.Set
;
import
java.util.HashSet
;
import
javax.xml.namespace.QName
;
import
javax.xml.soap.SOAPMessage
;
import
javax.xml.ws.handler.MessageContext
;
import
javax.xml.ws.handler.soap.SOAPHandler
;
import
javax.xml.ws.handler.soap.SOAPMessageContext
;
import
java.io.ByteArrayInputStream
;
import
com.sun.xml.wss.ProcessingContext
;
import
com.sun.xml.wss.SubjectAccessor
;
import
com.sun.xml.wss.XWSSProcessorFactory
;
import
com.sun.xml.wss.XWSSProcessor
;
public
class
ServiceHandler
implements
SOAPHandler
<
SOAPMessageContext
>
{
private
XWSSProcessor
xwssServer
=
null
;
private
boolean
trace
;
public
ServiceHandler
()
{
XWSSProcessorFactory
fact
=
null
;
try
{
fact
=
XWSSProcessorFactory
.
newInstance
();
![]()
ByteArrayInputStream
config
=
getConfig
();
![]()
xwssServer
=
![]()
fact
.
createProcessorForSecurityConfiguration
(
config
,
new
Verifier
());
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
trace
=
true
;
// set to true to enable message dumps
}
public
Set
<
QName
>
getHeaders
()
{
![]()
String
uri
=
"http://docs.oasis-open.org/wss/2004/01/"
+
"oasis-200401-wss-wssecurity-secext-1.0.xsd"
;
QName
securityHdr
=
new
QName
(
uri
,
"Security"
,
"wsse"
);
![]()
HashSet
<
QName
>
headers
=
new
HashSet
<
QName
>();
headers
.
add
(
securityHdr
);
return
headers
;
![]()
}
public
boolean
handleMessage
(
SOAPMessageContext
msgCtx
)
{
Boolean
outbound
=
(
Boolean
)
msgCtx
.
get
(
MessageContext
.
MESSAGE_OUTBOUND_PROPERTY
);
SOAPMessage
msg
=
msgCtx
.
getMessage
();
if
(!
outbound
.
booleanValue
())
{
// Validate the message.
try
{
ProcessingContext
pCtx
=
xwssServer
.
createProcessingContext
(
msg
);
pCtx
.
setSOAPMessage
(
msg
);
SOAPMessage
verifiedMsg
=
xwssServer
.
verifyInboundMessage
(
pCtx
);
![]()
msgCtx
.
setMessage
(
verifiedMsg
);
![]()
if
(
trace
)
dump
(
"Incoming message:"
,
verifiedMsg
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
return
true
;
}
public
boolean
handleFault
(
SOAPMessageContext
msgCtx
)
{
return
true
;
}
public
void
close
(
MessageContext
msgCtx
)
{
}
private
void
dump
(
String
msg
,
SOAPMessage
soapMsg
)
{
try
{
System
.
out
.
println
(
msg
);
soapMsg
.
writeTo
(
System
.
out
);
System
.
out
.
println
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
private
ByteArrayInputStream
getConfig
()
{
![]()
String
config
=
"<xwss:SecurityConfiguration "
+
"xmlns:xwss=\"http://java.sun.com/xml/ns/xwss/config\" "
+
"dumpMessages=\"true\"><xwss:RequireUsernameToken "
+
"passwordDigestRequired=\"false\"/> "
+
"</xwss:SecurityConfiguration>"
;
return
new
ByteArrayInputStream
(
config
.
getBytes
());
}
}
The structure of the ServiceHandler
is very close to that of the ClientHandler
. In the
ServiceHandler
, the handleMessage
method is interested only in incoming SOAP messages,
that is, requests. This handler has a XWSSProcessor
(lines 1 through 3) created from a hard-wired configuration
document (line 9) and associated with a Verifier
instance, a Callbackhandler
that extracts the
security information—the nonce, the username, and the password—from the SOAP header for verification.
Once the SOAP request has been validated, the newly verified and simplified SOAP message is passed on to
the usual SOAP libraries, which transform the XML document into the appropriate Java objects so that the
Echo service can do its thing.
The ServiceHandler
also makes use of the getHeaders
method, which is particularly important with respect to
the SOAP response from the EchoService
.
Recall that the ServiceHandler
, like every handler, is inherently
bidirectional. The handleMessage
method is coded so that this method ignores outgoing messages, but
the getHeaders
method injects, into the SOAP response from the Echo service, a WS-Security
header with the mustUnderstand
attribute set to true (line 5). In effect, the ServiceHandler
is demanding
that any receiver of the SOAP response, including the EchoClientWSS
, stick by the WS-Security rules.
If the getHeaders
method simply returned null
, a client-side exception would be thrown because the
incoming message would not be formatted according to WS-Security standards.
The service-side Verifier
, like the client-side Prompter
, is a CallbackHandler
delegated to
do grunt work. In a production environment, the Verifier
might check the username and
password against a database record, but here, for simplicity, these are hard-wired in the code.
The Verifier
also uses a PlainTextPasswordVerifier
because the password itself rather than
a hash value of the password is sent in the message (Example 6-24).
Example 6-24. The service-side Verifier
, a callback handler that helps the ServiceHandler
package
echoService
;
import
javax.security.auth.callback.Callback
;
import
javax.security.auth.callback.CallbackHandler
;
import
javax.security.auth.callback.UnsupportedCallbackException
;
import
com.sun.xml.wss.impl.callback.PasswordCallback
;
import
com.sun.xml.wss.impl.callback.PasswordValidationCallback
;
import
com.sun.xml.wss.impl.callback.UsernameCallback
;
// Verifier handles service-side callbacks for password validation.
public
class
Verifier
implements
CallbackHandler
{
// Username/password hardcoded for simplicity and clarity.
private
static
final
String
_username
=
"fred"
;
private
static
final
String
_password
=
"rockbed"
;
// For password validation, set the validator to the inner class below.
public
void
handle
(
Callback
[
]
callbacks
)
throws
UnsupportedCallbackException
{
for
(
int
i
=
0
;
i
<
callbacks
.
length
;
i
++)
{
if
(
callbacks
[
i
]
instanceof
PasswordValidationCallback
)
{
PasswordValidationCallback
cb
=
(
PasswordValidationCallback
)
callbacks
[
i
];
if
(
cb
.
getRequest
()
instanceof
PasswordValidationCallback
.
PlainTextPasswordRequest
)
cb
.
setValidator
(
new
PlainTextPasswordVerifier
());
}
else
throw
new
UnsupportedCallbackException
(
null
,
"Not needed"
);
}
}
// Encapsulated validate method verifies the username/password.
private
class
PlainTextPasswordVerifier
implements
PasswordValidationCallback
.
PasswordValidator
{
public
boolean
validate
(
PasswordValidationCallback
.
Request
req
)
throws
PasswordValidationCallback
.
PasswordValidationException
{
PasswordValidationCallback
.
PlainTextPasswordRequest
plain_pwd
=
(
PasswordValidationCallback
.
PlainTextPasswordRequest
)
req
;
return_username
.
equals
(
plain_pwd
.
getUsername
())
&&
_password
.
equals
(
plain_pwd
.
getPassword
());
}
}
}
On a successful verification, the Verifier
validates fred
(the username) as an authenticated subject
whose public credential is the name fred
and whose private credential is the password that vouches
for Fred’s identity.
The security illustrated in this sample could be ratcheted up to Mutual Challenge Security (MCS) with digital certificates used on both sides for peer authentication. Further, the contents of SOAP messages could be encrypted at the SOAP level, which would result in significantly larger SOAP headers that specified all of the cryptographic information: encryption and message digest algorithms, digital certificate formats, policies on confidentiality, encoding practices, specification of which parts of the SOAP message are to be encrypted and even digitally signed, and so on.