Genivia Home Documentation
The smdevp signed message digest engine

updated Tue Nov 23 2021 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:

  • soap_smd_begin to start a digest or signature computation/verification
  • soap_smd_end to finalize the digest or signature and clean up

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);
SOAP_FMAC1 size_t SOAP_FMAC2 soap_smd_size(int alg, const void *key)
Returns the number of octets needed to store the digest or signature returned by soap_smd_end.
Definition: smdevp.c:339
SOAP_FMAC1 int SOAP_FMAC2 soap_smd_end(struct soap *soap, char *buf, int *len)
Completes a digest or signature computation. Also deallocates temporary storage allocated by soap_smd...
Definition: smdevp.c:430
#define SOAP_SMD_SIGN_RSA_SHA1
Definition: smdevp.h:153
SOAP_FMAC1 int SOAP_FMAC2 soap_smd_begin(struct soap *soap, int alg, const void *key, int keylen)
Initiates a digest or signature computation.
Definition: smdevp.c:386

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);
#define SOAP_SMD_PASSTHRU
Definition: smdevp.h:122

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);
#define SOAP_SMD_VRFY_RSA_SHA1
Definition: smdevp.h:192

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
#define SOAP_SMD_HMAC_SHA1
Definition: smdevp.h:127

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
#define SOAP_SMD_DGST_SHA1
Definition: smdevp.h:140

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.