gSOAP WS-Security: The smdevp signed message digest engine
Main Page | Class List | File List | Class Members | File Members | Related Pages

The smdevp signed message digest engine

The gSOAP smdevp engine computes signed/unsigned message digests over any type of data using the EVP interface of OpenSSL. It currently supports MD5, SHA1, HMAC_SHA1, DSA_SHA1, and RSA_SHA1.

A digest or signature algorithm is selected with one the following:

Algorithm options:

The smdevp engine wraps the EVP API with three new functions:

A higher-level interface for computing (signed) message digests over messages produced by the gSOAP engine is defined by two new functions:

Compile all source codes with -DWITH_OPENSSL and link with ssl and crypto libraries.

Here is an example to sign an XML serialized C++ object using an RSA private key applied to the SHA1 digest of the serialized object:

    #include "smdevp.h"
    ns__Object object;
    int alg = SOAP_SMD_SIGN_RSA_SHA1;
    FILE *fd = fopen("key.pem", "r");
    EVP_PKEY *key = PEM_read_PrivateKey(fd, NULL, NULL, "password");
    char *sig = (char*)soap_malloc(soap, soap_smd_size(alg, key));
    int siglen;
    fclose(fd);
    if (soap_smd_begin(soap, alg, key, 0)
     || soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if (soap_smd_end(soap, sig, &siglen))
      soap_print_fault(soap, stderr);
    else
      ... // sig contains RSA-SHA1 signature of length siglen 
    EVP_PKEY_free(key);

Compile the gSOAP sources and your code with -DWITH_OPENSSL and link with OpenSSL libraries.

There is no XML output generated by this example, as the object is simply serialized to the smdevp engine. To actually pass the XML object through the smdevp engine and output it to a stream or file simultaneously, use the SOAP_SMD_PASSTHRU flag with the algorithm selection as follows:

    ns__Object object;
    int alg = SOAP_SMD_SIGN_RSA_SHA1;
    FILE *fd = fopen("key.pem", "r");
    EVP_PKEY *key = PEM_read_PrivateKey(fd, NULL, NULL, "password");
    char *sig = (char*)soap_malloc(soap, soap_smd_size(alg, key));
    int siglen;
    fclose(fd);
    soap->sendfd = open("object.xml", O_CREAT | O_WRONLY, 0600); // a file to save object to
    if (soap_smd_begin(soap, alg | SOAP_SMD_PASSTHRU, key, 0)
     || soap_begin_send(soap)
     || soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL) // save to "object.xml"
     || soap_end_send(soap))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if (soap_smd_end(soap, sig, &siglen))
      soap_print_fault(soap, stderr);
    else
      ... // sig contains RSA-SHA1 signature of length siglen 
    close(soap->sendfd);
    EVP_PKEY_free(key);

Note that we used soap_begin_send and soap_end_send to emit the XML to a stream. Each type also has a reader (e.g. soap_read_ns__Object) and writer (e.g. soap_write_ns__Object) that can be used instead as these include soap_begin_recv/soap_end_recv and soap_begin_send/soap_end_send call sequences.

To verify the signature of an object read from a stream or file, we pass it through the smdevp engine as follows:

    char *sig = ...;
    int siglen = ...;
    ns__Object object;
    int alg = SOAP_SMD_VRFY_RSA_SHA1;
    FILE *fd = fopen("key.pem", "r");
    EVP_PKEY *key;
    if (...) // key file contains public key?
      key = PEM_read_PUBKEY(fd, NULL, NULL, NULL);
    else // key file contains certificate
    { X509 *cert = PEM_read_X509(fd, NULL, NULL, NULL);
      key = X509_get_pubkey(cert);
      X509_free(cert);
    }
    fclose(fd);
    soap->recvfd = open("object.xml", O_RDONLY);
    if (soap_smd_begin(soap, alg, key, 0)
     || soap_begin_recv(soap)
     || soap_in_ns__Object(soap, "ns:Object", &object, NULL) == NULL
     || soap_end_recv(soap))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if (soap_smd_end(soap, sig, &siglen))
      soap_print_fault(soap, stderr);
    else
      ... // sig verified, i.e. signed object was not changed
    close(soap->recvfd);
    EVP_PKEY_free(key);

To verify the signature of an object stored in memory, we use the RSA public key and re-run the octet stream (by re-serialization in this example) through the smdevp engine using the SOAP_SMD_VRFY_RSA_SHA1 algorithm. Note that a PEM file may contain both the (encrypted) private and public keys.

    char *sig = ...;
    int siglen = ...;
    ns__Object object;
    int alg = SOAP_SMD_VRFY_RSA_SHA1;
    FILE *fd = fopen("key.pem", "r");
    EVP_PKEY *key;
    if (...) // key file contains public key?
      key = PEM_read_PUBKEY(fd, NULL, NULL, NULL);
    else // key file contains certificate
    { X509 *cert = PEM_read_X509(fd, NULL, NULL, NULL);
      key = X509_get_pubkey(cert);
      X509_free(cert);
    }
    fclose(fd);
    if (soap_smd_begin(soap, alg, key, 0)
     || soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if (soap_smd_end(soap, sig, &siglen))
      soap_print_fault(soap, stderr);
    else
      ... // sig verified, i.e. signed object was not changed
    EVP_PKEY_free(key);

The HMAC algorithm uses a shared secret key (hence both the sender and receiver must keep it secret) to sign and verify a message:

    ns__Object object;
    int alg = SOAP_SMD_HMAC_SHA1;
    static char key[16] =
    { 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
      0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
    char *sig = (char*)soap_malloc(soap, soap_smd_size(alg, NULL));
    int siglen;
    if (soap_smd_begin(soap, alg, key, sizeof(key))
     || soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if {soap_smd_end(soap, sig, &siglen))
      soap_print_fault(soap, stderr);
    else
      ... // sig holds the signature

Note: HMAC signature verification proceeds by recomputing the signature value for comparison.

A digest is a hash value of an octet stream computed using the MD5 or SHA1 algorithms:

    ns__Object object;
    int alg = SOAP_SMD_DGST_SHA1;
    char *digest = (char*)soap_malloc(soap, soap_smd_size(alg, NULL));
    int digestlen;
    if (soap_smd_begin(soap, alg, NULL, 0)
     || soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL))
    { soap_smd_end(soap, NULL, NULL); // clean up
      soap_print_fault(soap, stderr);
    }
    else if (soap_smd_end(soap, digest, &digestlen))
      soap_print_fault(soap, stderr);
    else
      ... // digest holds hash value of serialized object

Note that indentation (SOAP_XML_INDENT) and exc-c14n canonicalization (SOAP_XML_CANONICAL) affects the XML serialization format and, therefore, the digest or signature produced.


Generated on Fri Jan 14 09:45:26 2011 for gSOAP WS-Security by doxygen 1.3.8