You have some data, an RSA digital signature of that data, and the public key that you believe corresponds to the signature. You want to determine whether the signature is valid. A successful check would demonstrate both that the data was not modified from the time it was signed (message integrity) and that the entity with the corresponding public key signed the data (authentication).
Use the verification algorithm that corresponds to the chosen signing algorithm from Recipe 7.12. Generally, this should be included with your cryptographic library.
Recipe 7.12 explains the basic components of digital signatures with RSA. When verifying, you will generally need to provide the following inputs:
The signer's public key.
The signature to be verified.
The message digest corresponding to the message you want to authenticate. If it's a high-level API, you might be able to provide only the message.
An indication of the message digest algorithm used in the signing operation. Again, this may be assumed in a high-level API.
The API should simply return indication of success or failure.
Some implementations of RSA signature verification are susceptible to timing attacks. Basically, if RSA private key operations do not happen in a fixed amount of time, such attacks are possible. A technique called blinding can thwart timing attacks. The amount of time it takes to decrypt is randomized somewhat by operating on a random number in the process. To eliminate the possibility of such attacks, you should always turn blinding on before doing a signature validation operation.
With OpenSSL, blinding can be enabled with by calling
RSA_blinding_on(
)
, which has the following signature:
int RSA_blinding_on(RSA *r, BN_CTX *x);
This function has the following arguments:
r
RSA
object for which blinding should be enabled.
x
BN_CTX
object that will be used by the blinding
operations as scratch space. (See Recipe 7.4 for a discussion of
BN_CTX
objects.) It may be specified as
NULL
, in which case a new one will be allocated
and used internally.
The OpenSSL analog to RSA_sign( )
(discussed in
Recipe 7.12) is RSA_verify( )
, which has the
following signature:
int RSA_verify(int md_type, unsigned char *dgst, unsigned int dlen, unsigned char *sig, unsigned int siglen, RSA *r);
This function has the following arguments:
md_type
OpenSSL-specific identifier for the hash function. Possible values
are NID_sha1
, NID_ripemd
, or
NID_md5
. A fourth value,
NID_md5_sha1
, can be used to combine MD5 and SHA1
by hashing with both hash functions and concatenating the results.
These four constants are defined in the header file
openssl/objects.h.
dgst
Buffer containing the digest of the data whose signature is to be
verified. The digest should have been generated by the algorithm
specified by the md_type
argument.
dlen
Length in bytes of the digest buffer. For MD5, the digest buffer should always be 16 bytes. For SHA1 and RIPEMD, it should always be 20 bytes. For the MD5 and SHA1 combination, it should always be 36 bytes.
sig
Buffer containing the signature that is to be verified.
siglen
Number of bytes contained in the signature buffer. The number of
bytes should always be the same size as the public modulus, which can
be determined by calling RSA_size( )
with the
RSA
object that will be used to verify the
signature.
r
RSA
object to be used to verify the signature. The
RSA
object must contain the
signer's public key for verification to be
successful.
As we discussed in Recipe 7.12, OpenSSL RSA signatures only support PKCS #1 v1.5 and do not support RSASSA-PSS.
Here's code that implements verification on an arbitrary message, given a signature and the public RSA key of the signer:
#include <openssl/bn.h> #include <openssl/sha.h> #include <openssl/rsa.h> #include <openssl/objects.h> int spc_verify(unsigned char *msg, unsigned int mlen, unsigned char *sig, unsigned int siglen, RSA *r) { unsigned char hash[20]; BN_CTX *c; int ret; if (!(c = BN_CTX_new( ))) return 0; if (!SHA1(msg, mlen, hash) || !RSA_blinding_on(r, c)) { BN_CTX_free(c); return 0; } ret = RSA_verify(NID_sha1, hash, 20, sig, siglen, r); RSA_blinding_off(r); BN_CTX_free(c); return ret; }