Implementing WS-Security in a SOAP::Lite client

SOAP::Lite does not contain support for WS-Security, especially for the userToken profile as specified in The good news is, that there is a quite simple way to integrate it. This will be shown in the next lines.

The userToken profile requires additional header data to be sent. An example:

        <wsse:Password Type=""

To create data which get serialized into the soap header, one have to use SOAP::Header instead of SOAP::Data and pass it to the webservice call as an additional parameter, e.g.:


There is no much magic in creating the wsse:Security element, one issue should be mentioned:
The specification requires the client to provide a countermeasure for replay attacs. Beside a username and a (hashed) password, the client has to provide a timestamp and a random value called "Nonce". The client does not hash the password alone, but a concatenated string consisting of passwort, timestamp and nonce. One easy way to create a nonce is to use a sequence generator, e.g.:

sub create_generator {
    my ($name,$start_with) = @_;
    my $i = $start_with;
    return sub {  $name . ++$i; };

*default_nonce_generator = create_generator( "a value of ", int(1000*rand()) );

With some supporting subs, the wsse:Security element can be generated as follows:

use Time::Local;
use Digest::SHA1;
use MIME::Base64;

sub xml_quote {
    my ($value) = @_;
    $value =~ s/&/&amp;/;
    $value =~ s/</&lt;/;
    $value =~ s/>/&gt;/;

sub _complex_type {
    my ($name,@childs) = @_;
    my $data = SOAP::Data->new( name => $name );
    $data->value( \SOAP::Data->value(@v));

sub _typeless {
    my ($name,$value) = @_;
    my $data = SOAP::Data->new( name => $name );

    $value = xml_quote($value);

    $data->value( $value );
    $data->type( "" );

sub timestamp {
    my ($sec,$min,$hour,$mday,$mon,$year,undef,undef,undef) = gmtime(time);
    $year = $year + 1900;
    return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",$year,$mon,$mday,$hour,$min,$sec);

sub create_generator {
    my ($name,$start_with) = @_;
    my $i = $start_with;
    return sub {  $name . ++$i; };

*default_nonce_generator = create_generator( "a value of ", int(1000*rand()) );

sub ws_authen {
    my($username,$passwort,$nonce_generator) = @_;
    if(!defined($nonce_generator)) {
        $nonce_generator = \&default_nonce_generator;
    my $nonce = $nonce_generator->();
    my $timestamp = timestamp();

    my $pwDigest =  Digest::SHA1::sha1( $nonce . $timestamp . $passwort );
    my $passwortHash = MIME::Base64::encode_base64($pwDigest,"");
    my $nonceHash = MIME::Base64::encode_base64($nonce,"");

    my $auth = SOAP::Header->new( name => "wsse:Security" );
    $auth->attr( {
        "xmlns:wsse" => "",
        "xmlns:wsu"  => "",

    $auth->value( \SOAP::Data->value(
         _complex_type ( "wsse:UsernameToken",
                "Type" => ""

Finally, to add the WS-Security data to your call, just the result of a ws_authen() function call to parameter list:


Enjoy it!