|  | Home | Documentation | The smdevp signed message digest engine updated 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:

- SOAP_SMD_HMAC_MD5 to compute HMAC-MD5 message authentication code
- SOAP_SMD_HMAC_SHA1 to compute HMAC-SHA1 message authentication code
- SOAP_SMD_HMAC_SHA224 to compute HMAC-SHA224 message authentication code
- SOAP_SMD_HMAC_SHA256 to compute HMAC-SHA256 message authentication code
- SOAP_SMD_HMAC_SHA384 to compute HMAC-SHA384 message authentication code
- SOAP_SMD_HMAC_SHA512 to compute HMAC-SHA512 message authentication code
- SOAP_SMD_DGST_MD5 to compute MD5 128-bit digests
- SOAP_SMD_DGST_SHA1 to compute SHA1 160-bit digests
- SOAP_SMD_DGST_SHA224 to compute SHA224 224-bit digests
- SOAP_SMD_DGST_SHA256 to compute SHA256 256-bit digests
- SOAP_SMD_DGST_SHA384 to compute SHA384 384-bit digests
- SOAP_SMD_DGST_SHA512 to compute SHA512 512-bit digests
- SOAP_SMD_SIGN_DSA_SHA1 to compute DSA-SHA1 signatures
- SOAP_SMD_SIGN_DSA_SHA224 to compute DSA-SHA224 signatures
- SOAP_SMD_SIGN_DSA_SHA256 to compute DSA-SHA256 signatures
- SOAP_SMD_SIGN_DSA_SHA384 to compute DSA-SHA384 signatures
- SOAP_SMD_SIGN_DSA_SHA512 to compute DSA-SHA512 signatures
- SOAP_SMD_SIGN_RSA_SHA1 to compute RSA-SHA1 signatures
- SOAP_SMD_SIGN_RSA_SHA224 to compute RSA-SHA224 signatures
- SOAP_SMD_SIGN_RSA_SHA256 to compute RSA-SHA256 signatures
- SOAP_SMD_SIGN_RSA_SHA384 to compute RSA-SHA384 signatures
- SOAP_SMD_SIGN_RSA_SHA512 to compute RSA-SHA512 signatures
- SOAP_SMD_VRFY_DSA_SHA1 to verify DSA-SHA1 signatures
- SOAP_SMD_VRFY_DSA_SHA224 to verify DSA-SHA224 signatures
- SOAP_SMD_VRFY_DSA_SHA256 to verify DSA-SHA256 signatures
- SOAP_SMD_VRFY_DSA_SHA384 to verify DSA-SHA384 signatures
- SOAP_SMD_VRFY_DSA_SHA512 to verify DSA-SHA512 signatures
- SOAP_SMD_VRFY_RSA_SHA1 to verify RSA-SHA1 signatures
- SOAP_SMD_VRFY_RSA_SHA224 to verify RSA-SHA224 signatures
- SOAP_SMD_VRFY_RSA_SHA256 to verify RSA-SHA256 signatures
- SOAP_SMD_VRFY_RSA_SHA384 to verify RSA-SHA384 signatures
- SOAP_SMD_VRFY_RSA_SHA512 to verify RSA-SHA512 signatures

Algorithm options:

- SOAP_SMD_PASSTHRU to pass XML through the smdevp engine

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

- soap_smd_init to initialize the engine
- soap_smd_update to update the state with a message part
- soap_smd_final to compute the digest, signature, or verify a signature and to deallocate the engine
- soap_smd_cleanup to deallocate the engine

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;
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);
smdevp.h
soap_smd_end
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:458
soap_smd_size
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:367
soap_smd_begin
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:414
SOAP_SMD_SIGN_RSA_SHA1
#define SOAP_SMD_SIGN_RSA_SHA1
Definition smdevp.h:153

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);
SOAP_SMD_PASSTHRU
#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;
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);
SOAP_SMD_VRFY_RSA_SHA1
#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;
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
}
SOAP_SMD_HMAC_SHA1
#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;
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
}
SOAP_SMD_DGST_SHA1
#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.