Wednesday, September 28, 2011

Java Web services: Axis2 WS-Security basics - (Rampart )

WS-Security
WS-Security is a standard for adding security to SOAP Web service message exchanges (see Resources). It uses a SOAP message-header element to attach the security information to messages, in the form of tokens conveying different types of claims (which can include names, identities, keys, groups, privileges, capabilities, and so on) along with encryption and digital-signature information. WS-Security supports multiple formats for tokens, multiple trust domains, multiple signature formats, and multiple encryption technologies, so in most cases the header information needs to include specific format and algorithm identification for each component. The added information can result in a complex structure for the header information, as shown in the (heavily edited) Listing 1 — a sample message with signing and encryption:

Listing 1. Sample message with signing and encryption
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
 <soap:Header>
  <wsse:Security soap:mustUnderstand="1">
   <wsu:Timestamp wsu:Id="Timestamp-d2e3c4aa-da82-4138-973d-66b596d66b2f">
    <wsu:Created>2006-07-11T21:59:32Z</wsu:Created>
    <wsu:Expires>2006-07-12T06:19:32Z</wsu:Expires>
   </wsu:Timestamp>
   <wsse:BinarySecurityToken ValueType="...-x509-token-profile-1.0#X509v3"
     EncodingType="...-wss-soap-message-security-1.0#Base64Binary"
     xmlns:wsu="...oasis-200401-wss-wssecurity-utility-1.0.xsd"
     wsu:Id="SecurityToken-faa295...">MIIEC56MQswCQY...</wsse:BinarySecurityToken>
   <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
     <wsse:SecurityTokenReference>
      <wsse:KeyIdentifier ValueType=
       "...#X509SubjectKeyIdentifier">LlYsHyhNnOVA9Aj7...</wsse:KeyIdentifier>
     </wsse:SecurityTokenReference>
    </KeyInfo>
    <xenc:CipherData>
     <xenc:CipherValue>g+A2WJhsoGBKUydZ9Za...</xenc:CipherValue>
    </xenc:CipherData>
    <xenc:ReferenceList>
     <xenc:DataReference URI="#EncryptedContent-ba0556c3-d443-4f34-bcd1-14cbc32cd689" />
    </xenc:ReferenceList>
   </xenc:EncryptedKey>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
     <ds:CanonicalizationMethod
       Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
       xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
     <Reference URI="#Id-c80f735c-62e9-4001-8094-702a4605e429">
      <Transforms>
       <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      </Transforms>
      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
      <DigestValue>lKjc5nyLQDZAIu/hZb4B6mLquow=</DigestValue>
     </Reference>
     ...
    </SignedInfo>
    <SignatureValue>TiLmWvlz3mswinLVQn58BgYS0368...</SignatureValue>
    <KeyInfo>
     <wsse:SecurityTokenReference>
      <wsse:Reference URI="#SecurityToken-faa295..."
        ValueType="...-x509-token-profile-1.0#X509v3" />
     </wsse:SecurityTokenReference>
    </KeyInfo>
   </Signature>
  </wsse:Security>
 </soap:Header>
 <soap:Body wsu:Id="Id-8db9ff44-7bef-4737-8091-cdac51a34db8">
  <xenc:EncryptedData Id="EncryptedContent-ba05..."
    Type="http://www.w3.org/2001/04/xmlenc#Content"
    xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
   <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
   <xenc:CipherData>
    <xenc:CipherValue>mirmi0KuFEEI56eu2U3cICz...</xenc:CipherValue>
   </xenc:CipherData>
  </xenc:EncryptedData>
 </soap:Body>
</soap:Envelope>

In this article, you'll see some simple examples of WS-Security headers, with only a single token. The next article in the series will go further in discussing the type of complex structure shown in Listing 1.
WS-Security applies to actual SOAP message exchanges. The service implementation can verify that WS-Security has been properly applied to incoming messages, but clients need to know in advance what they must implement in order to use the service. With the complexity of WS-Security and the number of options supported, it can be difficult just to use a text description for this purpose, and manual configuration of WS-Security handling can cause errors. WS-Policy is a general-purpose structure for specifying extension requirements for Web services, and WS-SecurityPolicy is an extension of WS-Policy specifically for WS-Security support. Together these two standards support describing WS-Security requirements in machine-readable form. The WS-Policy and WS-SecurityPolicy information can be used alone or embedded directly inside Web Services Description Language (WSDL) documents, so that Web service frameworks can configure themselves automatically to a service's requirements.
Introducing Rampart
Rampart is the Axis2 security module, supporting WS-Security, WS-SecurityPolicy, WS-SecureConversation, and WS-Trust. In this article, you'll only see Rampart's WS-Security and WS-SecurityPolicy functions; later articles will give you a look at the other features.
Because Rampart is implemented as a module (actually a pair of modules — rampart.mar and rahas.mar), it plugs into the Axis2 processing framework and does its job by intercepting messages at particular points in the inbound and outbound processing, checking or making changes to the messages as appropriate.
Installing Rampart
Rampart comes with several .jar files (in the distribution's lib directory), along with a pair of .mar module files (in the dist directory). You must add the .jar files to your classpath in order to use Rampart with Axis2, and you must add the .mar files to either your classpath or an Axis2 repository structure.
The easiest way of handling the Rampart .jar and .mar files is to add them into your Axis2 installation. You can just directly copy the .jar files from the Rampart lib directory into your Axis2 lib directory, and the .mar files from the Rampart dist directory into your Axis2 repository/modules directory. (You can also use the Ant build.xml in the Rampart samples directory to copy the files across to your Axis2 installation. Just set the AXIS2_HOME environmental variable to your Axis2 installation directory and run ant from a console open to the Rampart sample directory.)
For many WS-Security features, you also need to add the Bouncy Castle security provider to your JVM security configuration and the Bouncy Castle .jar to your Axis2 installation. This step — not needed for the UsernameToken you'll learn about in this article — is required for other security features to be covered later in the series. Because of patent issues with some of the security algorithms, the Bouncy Castle .jar is a separate download from Rampart (see Resources). Download the appropriate version of the .jar for your Java runtime, and add the .jar to the Axis2 lib directory. You then need to modify your Java installation's security policies to use the Bouncy Castle code by adding a line to the java.security file found in your Java runtime's lib/security directory. Look for the section of the file with several different security.provider lines, and add the following line:
security.provider.99=org.bouncycastle.jce.provider.BouncyCastleProvider

The ordering of the security.provider lines in the file doesn't matter, but it's a good idea to add this after the other lines for predefined security provider implementations.
To use the Rampart code in an Axis2 server installation, you need to create a new axis2.war file, one that includes the added Rampart .jar and .mar files. You can use the Ant build.xml provided in the webapp directory to create axis2.war, provided you make one change: delete the line <exclude name="axis2-codegen*.jar"/> near the end of the file. Then open a console to the Axis2 webapp directory and run ant. After the build.xml runs, you can find the created axis2.war Web application in the Axis2 installation dist directory.
A sample application
The application provided in the example code (see Downloads) is based on one I used in "Axis2 Data Binding" to demonstrate data-binding alternatives for Axis2. For this article and the following ones on Axis2 WS-Security support, I've trimmed it down to just three operations: getBook, addBook, and getBooksByType. To keep things simple, only the Axis Data Binding (ADB) version of the code is provided, but this is not a requirement of working with WS-Security in Axis2 — Rampart implements WS-Security at a level that's independent of the data-binding technique your code uses, so it works with all forms of data binding supported by Axis2.
The root directory of the example code is jws04code. Inside this directory, you'll find Ant build.xml and build.properties files, the library.wsdl file giving the service definition for the example application, a log4j.properties file used to configure client-side logging, and several property-definition XML files (all named XXX-policy-client.xml or XXX-policy-server.xml). The build.properties file configures the operation of the example application. Listing 2 shows the supplied version of this properties file:

Listing 2. Supplied build.properties file
# set axis-home to your Axis2 installation directory
axis-home=PATH_TO_AXIS2_INSTALLATION
# set the connection protocol to be used to access services (http or https)
protocol=http
# set the name of the service host
host-name=localhost
# set the port for accessing the services (change this for monitoring)
host-port=8080
# set the base path for accessing all services on the host
base-path=/axis2/services/
# set the name of the policy file to be used by the client
client-policy=plain-policy-client.xml
# set the name of the policy file to be used by the server
server-policy=plain-policy-server.xml

Before trying out the examples, you need to edit the build.properties file and set the actual path to your Axis2 installation (with Rampart added, as discussed in the preceding section). If you're using a different host or port number for your server, you also need to modify the host-name and host-port values. I'll discuss the remaining values later in this article.
Giving WS-Security a try
WS-Security defines several types of security tokens (including tokens that are part of the core specification, and those defined by profiles as plug-in extensions to the specification), with many options for how the tokens are constructed and used. The point of this article is the configuration and use of Rampart with Axis2, so I'll just use the simplest useful token as an example: the UsernameToken, defined by the UsernameToken profile.
UsernameToken WS-SecurityPolicy
The purpose of a UsernameToken is just to convey username and password information as part of the WS-Security headers. The most basic form of UsernameToken sends both the username and password as plain text. This isn't optimal from a security standpoint (though there's nothing wrong with using this approach over secure connections), but it's easy to see what's being sent, making it a useful starting point.
The WS-SecurityPolicy configuration for a UsernameToken sent as text can be as simple as shown in Listing 3. This policy (shown here with one line split in two to fit the page width — not valid for actual use) consists of a standard WS-Policy wrapper (the elements using the wsp prefix) around a WS-SecurityPolicy UsernameToken assertion.

Listing 3. WS-SecurityPolicy for plain-text UsernameToken
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
               ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

The UsernameToken in Listing 3 uses an IncludeToken attribute to specify the type of message flow that is to include the token — in this case, all message flows that go from the initiator of a request (that is, the client) to the recipient of a request (the server). You could use other defined values for the IncludeToken attribute to specify other uses of the token, but for a UsernameToken this is generally the only value that makes sense.
Applying the policy
WS-Policy and WS-SecurityPolicy are designed to support embedding within WSDL service definitions. References are used to associate a policy to one or more <wsdl:binding>, <wsdl:binding>/<wsdl:operation>, or <wsdl:message> definitions. Axis2 1.4.X implements preliminary handling for policies embedded in WSDL, but as of Axis2 1.4.1 the implementation is not yet robust. This article instead attaches the policies directly to the client and server in order to be compatible with the 1.4.1 code.
Server-side policy handling
For the server side, you apply a policy by adding it into the services.xml configuration file included in each Axis2 .aar service archive. The policy can be added directly as the child of a <service> element to apply to all operations defined by that service. You also need to add a <module> element to services.xml in order to tell Axis2 that the Rampart module must be included in the configuration for the service. Listing 4 is an edited version of the services.xml used by the example application, with the added module reference and policy information shown in bold:

Listing 4. services.xml with embedded policy
<serviceGroup>
  <service name="library-username">
    <messageReceivers>
      <messageReceiver
          class="com.sosnoski.ws.library.adb.LibraryUsernameMessageReceiverInOut"
          mep="http://www.w3.org/ns/wsdl/in-out"/>
    </messageReceivers>
    <parameter
        name="ServiceClass">com.sosnoski.ws.library.adb.LibraryUsernameImpl</parameter>
    <parameter name="useOriginalwsdl">true</parameter>
    <parameter name="modifyUserWSDLPortAddress">true</parameter>
    <operation mep="http://www.w3.org/ns/wsdl/in-out" name="getBook"
        namespace="http://ws.sosnoski.com/library/wsdl">
      <actionMapping>urn:getBook</actionMapping>
      <outputActionMapping>http://.../getBookResponse</outputActionMapping>
    </operation>
    ...

    <module ref="rampart"/>
    <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        xmlns:wsu="http://.../oasis-200401-wss-wssecurity-utility-1.0.xsd"
        wsu:Id="UsernameToken">
      <wsp:ExactlyOne>
        <wsp:All>
          <sp:SupportingTokens
              xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
            <wsp:Policy>
              <sp:UsernameToken
                  sp:IncludeToken="http://.../IncludeToken/AlwaysToRecipient">
                <wsp:Policy>
                  <sp:HashPassword/>
                </wsp:Policy>
              </sp:UsernameToken>
            </wsp:Policy>
          </sp:SupportingTokens>

          <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
            <ramp:passwordCallbackClass>...PWCBHandler</ramp:passwordCallbackClass>
          </ramp:RampartConfig>

        </wsp:All>
      </wsp:ExactlyOne>
    </wsp:Policy>
  </service>
</serviceGroup>

Regenerating services.xml the easy way

When you use the Axis2 Wsdl2Java tool to generate server-side deployment files it creates a services.xml file as part of the generated artifacts (in the resources directory, under your generation target directory). Any time your WSDL service definition changes, you must regenerate this file, so the need to embed module reference and policy information in the file can be painful. The example code includes a tool to automate this modification to the generated file: the com.sosnoski.ws.MergeServerPolicy class. (Source and binary are in the mergetool directory.) The build.xml file's generate-server target runs this tool to insert the module reference and appropriate policy information into the generated services.xml file each time the server code is generated. You can use the tool for your own projects if you want. It takes the path to the services.xml file as the first command-line parameter, the path to the policy file as the second, and the names of any modules to be added as the remaining command-line parameters.
If you compare the embedded policy in Listing 4 with the basic policy in Listing 3, you'll see one addition — a <ramp:RampartConfig> element. This element provides Rampart-specific extensions to the policy information, in this case giving the name of a class to be used for handling password callbacks. The callback is how your server code can verify the username-and-password combination supplied by the client on a request.
Listing 5 shows the actual implementation of the callback class, as used for the plain-text password. In this case, both the username and password are supplied to the callback, and all the callback needs to do is verify the combination. If the username and password match the expected values, this just returns; otherwise, it throws an exception to indicate the error.

Listing 5. Password callback code
import org.apache.ws.security.WSPasswordCallback;

public class PWCBHandler implements CallbackHandler
{
    public void handle(Callback[] callbacks)
        throws IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
            String id = pwcb.getIdentifer();
            if (pwcb.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {

                // used when plain-text password in message
                if (!"libuser".equals(id) || !"books".equals(pwcb.getPassword())) {
                    throw new UnsupportedCallbackException(callbacks[i], "check failed");
                }

            }
        }
    }
}

For a real application, you'd naturally want to use some other mechanism (such as a database or an external security mechanism) to verify the username and password combination. The callback technique lets you use any verification technique you want as an extension of the Rampart security handling.
Client-side configuration
To use Rampart for your client code, you first need to have the module available for use with Axis2. You can do this by configuring an Axis2 repository structure for the client, but it's generally easier just to include the rampart.mar module file (and any other modules you need to use) in your classpath. The supplied example uses the classpath approach.
You then need to configure the security policy and any related parameters for the client. The easiest way to handle this configuration is to set values directly on the service stub. Listing 6 shows how the configuration is done in the example code:

Listing 6. Client configuration
/**
     * Load policy file from classpath.
     */
    private static Policy loadPolicy(String name) throws XMLStreamException {
        ClassLoader loader = WebServiceClient.class.getClassLoader();
        InputStream resource = loader.getResourceAsStream(name);
        StAXOMBuilder builder = new StAXOMBuilder(resource);
        return PolicyEngine.getPolicy(builder.getDocumentElement());
    }

    public static void main(String[] args) throws IOException, XMLStreamException {

        // check for required command line parameters
        if (args.length < 4) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.adb.WebServiceClient protocol host port path");
            System.exit(1);
        }

        // create the client stub
        String target = args[0] + "://" + args[1] + ":" + args[2] + args[3];
        System.out.println("Connecting to " + target);
        LibraryUsernameStub stub = new LibraryUsernameStub(target);

        // configure and engage Rampart
        ServiceClient client = stub._getServiceClient();
        Options options = client.getOptions();
        options.setProperty(RampartMessageData.KEY_RAMPART_POLICY,
            loadPolicy("policy.xml"));
        options.setUserName("libuser");
        options.setPassword("books");
        client.engageModule("rampart");

The configuration portion is the final code block in Listing 6. This gets the org.apache.axis2.client.ServiceClient instance from the created stub and sets the policy information (loaded from the classpath) and username/password in the client options. It then engages the Rampart module in the Axis2 configuration used by the client. Once this is done, you can use the stub to access the service just as you would without WS-Security, and Rampart adds the UsernameToken automatically to each request.
Confirming the results
With Ant installed, you can just run ant from a console open to the example code directory to build both client and server code. You can then deploy the created library-username.aar file to your Axis2 server installation (one that includes the Rampart .jars and .mars, of course), and try out the client by entering ant run at the console. If everything is set up correctly, you should see the output shown in Figure 1:

Figure 1. Console output when running application
Console output when running application
Just running the client with the server doesn't show you what's happening, of course. You can use a tool such as TCPMon to act as an intermediary between the client and server and capture the message exchange to see the WS-Security UsernameToken in action (see Resources). To do this, you'd first need to get TCPMon set up and accepting connections from the client on one port, which it then forwards to the server running on a different port (or a different host). You can then edit the build.properties file and change the host-port value to the listening port for TCPMon. If you again enter ant run at the console, you should then see the messages being exchanged. Listing 7 shows a sample client-message capture:

Listing 7. Client message with UsernameToken
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="...wss-wssecurity-secext-1.0.xsd"
        soapenv:mustUnderstand="1">
      <wsse:UsernameToken xmlns:wsu="...wss-wssecurity-utility-1.0.xsd"
          wsu:Id="UsernameToken-1815911473">
        <wsse:Username>libuser</wsse:Username>
        <wsse:Password Type="...wss-username-token-profile-1.0#PasswordText"
            >books</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ns2:getBooksByType xmlns:ns2="http://ws.sosnoski.com/library/wsdl">
      <ns2:type>scifi</ns2:type>
    </ns2:getBooksByType>
  </soapenv:Body>
</soapenv:Envelope>

Securing UsernameToken
A basic plain-text UsernameToken doesn't provide much security directly, because both the username and the corresponding password are visible to anyone able to monitor a message. If you use an encrypted communication channel, this isn't a real problem — as long as the channel encryption is solid, no outside party can monitor a message. WS-SecurityPolicy conveniently defines a way to require the use of an encrypted channel, as shown in Listing 8 (again with a line split to fit page width — see the example code package's secure-policy-server.xml file for the real policy):

Listing 8. Policy requiring HTTPS connection
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
  "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
 <wsp:ExactlyOne>
  <wsp:All>
   <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
     <wsp:Policy>
      <sp:TransportToken>
        <wsp:Policy>
         <sp:HttpsToken RequireClientCertificate="false"/>
        </wsp:Policy>
      </sp:TransportToken>
     </wsp:Policy>
   </sp:TransportBinding>
   <sp:SupportingTokens
     xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <wsp:Policy>
     <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
          ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"/>
    </wsp:Policy>
   </sp:SupportingTokens>
  </wsp:All>
 </wsp:ExactlyOne>
</wsp:Policy>

The portion of Listing 8 shown in bold is the added part, consisting of a <sp:TransportBinding> element and nested <sp:HttpsToken> element. The <sp:HttpsToken> element says that a secure HTTPS connection must be used in communicating with the service. If you try building the service .aar with this policy (by changing the server-policy value in build.properties to secure-policy-server.xml, and then running ant build-server) and deploying it, you'll see that Rampart enforces this policy requirement, rejecting any normal HTTP connections.
If you want to try out an HTTPS connection to the service you can do so, but you first need to configure your Web server to support HTTPS. (Tomcat has good instructions for this, at /tomcat-docs/ssl-howto.html.) You also need to change the protocol value in build.properties to https, and if you're using a self-signed certificate for the Web server, you need to pass a trust store to the client when running the Ant test target. The supplied build.xml has a commented-out line to do this, so you can just uncomment the line and set the location of the appropriate trust store file on your system.
Another way of making UsernameToken more secure works even over unencrypted links. This method uses a digest value computed over a string made up of two other text values combined with the password. One of the text values, the nonce, is a random value generated by the sender for each request. The other, the created timestamp, is just the time at which the sender created the UsernameToken. Both these values are included in the UsernameToken as plain text. When properly used by both client and server, the combination of these values with the password in the digest makes it possible for the server to verify that the correct password was used when generating the digest, while making it difficult for any outside party to fake a valid password. Listing 9 gives a policy example for using the digest password, followed by an actual capture from a message using the digest password (both reformatted to fit page width — see the hash-policy-client.xml file for the real policy). The differences from the original policy are again shown in bold.

Listing 9. Policy using password digest, and sample message
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
            <wsp:Policy>
              <sp:HashPassword/>
            </wsp:Policy>
          </sp:UsernameToken>
        </wsp:Policy>
      </sp:SupportingTokens>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse=".../oasis-200401-wss-wssecurity-secext-1.0.xsd"
        soapenv:mustUnderstand="1">
      <wsse:UsernameToken xmlns:wsu="...wss-wssecurity-utility-1.0.xsd"
          wsu:Id="UsernameToken-1421876889">
        <wsse:Username>libuser</wsse:Username>
        <wsse:Password Type="...wss-username-token-profile-1.0#PasswordDigest"
          >/Wt/2yDdZwa8a5qd7U70hrp29/w=</wsse:Password>
        <wsse:Nonce>4ZQz5ytME/RXfChuKJ03iA==</wsse:Nonce>
        <wsu:Created>2009-03-17T11:20:57.467Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ns2:getBooksByType xmlns:ns2="http://ws.sosnoski.com/library/wsdl">
      <ns2:type>scifi</ns2:type>
    </ns2:getBooksByType>
  </soapenv:Body>
</soapenv:Envelope>

No comments:

Post a Comment