Genivia Home Documentation
The smdevp signed message digest engine

updated Thu Mar 21 2024 by Robert van Engelen
 
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/224/256/384/512, HMAC_SHA1/224/256/384/512, DSA_SHA1/224256/384/512, and RSA_SHA1/224/256/384/512.

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 SHA digest of the serialized object:

#include "smdevp.h"
ns__Object object;
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;
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;
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;
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;
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
}

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 SHA algorithms:

ns__Object object;
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.