[ Team LiB ] Previous Section Next Section

7.13 Verifying Signed Data Using an RSA Public Key

7.13.1 Problem

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

7.13.2 Solution

Use the verification algorithm that corresponds to the chosen signing algorithm from Recipe 7.12. Generally, this should be included with your cryptographic library.

7.13.3 Discussion

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

7.13.4 See Also

Recipe 7.4, Recipe 7.12

    [ Team LiB ] Previous Section Next Section