Genivia - The smdevp signed message digest engine

|  | Home | Documentation | The smdevp signed message digest engine updated Thu Mar 21 2024 by Robert van Engelen |

- Main Page
- Related Pages
- Classes
- Files

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);

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
}

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
}

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.