RSA Signature Schemes

From Crypto++ Wiki
Jump to navigation Jump to search
RSA Signature Schemes
Documentation
#include <cryptopp/rsa.h>

This articles shows you how to perform signing using RSA.

For the main RSA page, visit RSA Cryptography. For encryption schemes, visit RSA Encryption Schemes. Raw RSA provides information about low level RSA operations. An example of blind signatures using RSA is available at Blind Signature. For a detailed treatment of key generation, loading, saving, validation, and formats, see Keys and Formats.

Sample Programs

The following is a handful of sample programs demonstrating ways to create keys, sign messages and verify messages.

The examples below use SHA256. You should avoid SHA1 because it is considered weak and wounded. You can use other HashTransformation derived hashes, like Whirlpool, SHA512, SHA3_256 or SHA3_512.

When pairing RSA modulus sizes with hashes, be sure to visit Security Levels. For example, RSA 1024 can be paired with SHA1 because the security levels are mostly equivalent. That is, neither the modulus or the hash is significantly weaker than the other.

Generate Keys

///////////////////////////////////////
// Pseudo Random Number Generator
AutoSeededRandomPool rng;

///////////////////////////////////////
// Generate Parameters
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);

///////////////////////////////////////
// Generated Parameters
Integer n = params.GetModulus();
Integer p = params.GetPrime1();
Integer q = params.GetPrime2();
Integer d = params.GetPrivateExponent();
Integer e = params.GetPublicExponent();

///////////////////////////////////////
// Dump
cout << "RSA Parameters:" << endl;
cout << " n: " << n << endl;
cout << " p: " << p << endl;
cout << " q: " << q << endl;
cout << " d: " << d << endl;
cout << " e: " << e << endl;
cout << endl;

///////////////////////////////////////
// Create Keys
RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);

Signature Scheme with Appendix

AutoSeededRandomPool rng;

InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 3072);

RSA::PrivateKey privateKey(parameters);
RSA::PublicKey publicKey(parameters);

// Message
string message = "Yoda said, Do or Do Not. There is no try.";

// Signer object
RSASS<PSS, SHA256>::Signer signer(privateKey);

// Create signature space
size_t length = signer.MaxSignatureLength();
SecByteBlock signature(length);

// Sign message
length = signer.SignMessage(rng, (const byte*) message.c_str(),
    message.length(), signature);

// Resize now we know the true size of the signature
signature.resize(length);

// Verifier object
RSASS<PSS, SHA256>::Verifier verifier(publicKey);

// Verify
bool result = verifier.VerifyMessage((const byte*)message.c_str(),
    message.length(), signature, signature.size());

// Result
if(true == result) {
    cout << "Signature on message verified" << endl;
} else {
    cout << "Message verification failed" << endl;
}

Signature Scheme with Appendix (Filters)

// Generate ot Load keys
RSA::PrivateKey privateKey = ...;
RSA::PublicKey publicKey = ...;

// Message
string message = "Yoda said, Do or Do Not. There is no try.";
string signature, recovered;

////////////////////////////////////////////////
// Sign and Encode
RSASS<PSS, SHA256>::Signer signer(privateKey);

StringSource ss1(message, true, 
    new SignerFilter(rng, signer,
        new StringSink(signature)
  ) // SignerFilter
); // StringSource

////////////////////////////////////////////////
// Verify and Recover
RSASS<PSS, SHA256>::Verifier verifier(publicKey);

StringSource ss2(message+signature, true,
    new SignatureVerificationFilter(
        verifier,
        new StringSink(recovered),
        SignatureVerificationFilter::THROW_EXCEPTION |
        SignatureVerificationFilter::PUT_MESSAGE
  ) // SignatureVerificationFilter
); // StringSource

////////////////////////////////////////////////
// No exception - use recovered message
...

Probabilistic Signature Scheme with Recovery

////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;

InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);

RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);

// Signing      
RSASS<PSSR, SHA256>::Signer signer(privateKey);
RSASS<PSSR, SHA256>::Verifier verifier(publicKey);

// Setup
byte message[] = "RSA-PSSR Test";
size_t messageLen = sizeof(message);      

////////////////////////////////////////////////
// Sign and Encode
SecByteBlock signature(signer.MaxSignatureLength(messageLen));

size_t signatureLen = signer.SignMessageWithRecovery(rng, message,
    messageLen, NULL, 0, signature);

// Resize now we know the true size of the signature
signature.resize(signatureLen);

////////////////////////////////////////////////
// Verify and Recover
SecByteBlock recovered(
    verifier.MaxRecoverableLengthFromSignatureLength(signatureLen)
);

DecodingResult result = verifier.RecoverMessage(recovered, NULL,
    0, signature, signatureLen);

if (!result.isValidCoding) {
    throw Exception(Exception::OTHER_ERROR, "Invalid Signature");
}

////////////////////////////////////////////////
// Use recovered message
//  MaxSignatureLength is likely larger than messageLength
recovered.resize(result.messageLength);
...

Probabilistic Signature Scheme with Recovery (Filter)

////////////////////////////////////////////////
// Generate or Load keys
RSA::PrivateKey privateKey = ...;
RSA::PublicKey publicKey = ...; 

////////////////////////////////////////////////
// Setup
string message = "RSA-PSSR Test", signature, recovered;    

////////////////////////////////////////////////
// Sign and Encode
RSASS<PSSR, SHA256>::Signer signer(privateKey);

StringSource ss1(message, true, 
    new SignerFilter(rng, signer,
        new StringSink(signature),
        true // putMessage for recovery
   ) // SignerFilter
); // StringSource

////////////////////////////////////////////////
// Verify and Recover
RSASS<PSSR, SHA256>::Verifier verifier(publicKey);

StringSource ss2(signature, true,
    new SignatureVerificationFilter(
        verifier,
        new StringSink(recovered),
        THROW_EXCEPTION | PUT_MESSAGE
   ) // SignatureVerificationFilter
); // StringSource

cout << "Verified signature on message" << endl;

Signature Scheme (PKCS v1.5)

Though similar to RSA-SSA, RSASSA_PKCS1v15_SHA_Signer and RSASSA_PKCS1v15_SHA_Verifier uses PKCS v1.5 padding. The MD2 and MD5 variants of RSASSA_PKCS1v15_<Digest>_Signer and RSASSA_PKCS1v15_<Digest>_Verifier should not be used.

AutoSeededRandomPool rng;

InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 3072);

RSA::PrivateKey privateKey(parameters);
RSA::PublicKey publicKey(parameters);

// Message
string message = "Yoda said, Do or Do Not. There is no try.";

// Signer object
RSASSA_PKCS1v15_SHA_Signer signer(privateKey);

// Create signature space
size_t length = signer.MaxSignatureLength();
SecByteBlock signature(length);

// Sign message
length = signer.SignMessage(rng, (const byte*) message.c_str(),
    message.length(), signature);

// Resize now we know the true size of the signature
signature.resize(length);

// Verifier object
RSASSA_PKCS1v15_SHA_Verifier verifier(publicKey);

// Verify
bool result = verifier.VerifyMessage((const byte*)message.c_str(),
    message.length(), signature, signature.size());

// Result
if(true == result) {
    cout << "Signature on message verified" << endl;
} else {
    cout << "Message verification failed" << endl;
}

Signature Generation Given d and n

Given Integers d and n rather than a RSA::PrivateKey, perform the following to create a signer object [1]. Attempting to use a RSA::PrivateKey by calling Initialize (i.e., not factoring n) will result in an exception [2].

// Use InvertibleRSAFunction to factor 'n'
InvertibleRSAFunction params;
params.Initialize(n, e, d);

RSA::PrivateKey(params);

...

If the public exponent has been misplaced, common values for the exponent are 3 (Microsoft CAPI/C#), 17 (Crypto++), and 65535 (Java).

Signature Verification Given e and n

Given Integers e and n rather than a RSA::PublicKey, perform the following to create a verifier object.

RSASS<PSS, SHA>::Verifier verifier(n, e);
RSASS<PSS, SHA>::Verifier verifier;
verifier.AccessKey().Initialize(n, e);

Signature Verification of a File

The sample code above used a couple of small strings, and it allows you to use StringSource ss(plain + signature, true, ...). Sometimes you want to use a large file, but loading a large file into memory is not economical. In this case you can use both a StringSource (for the signature) and a FileSource (for the file data). The code below shows you how to do it.

// Generate key material
AutoSeededRandomPool prng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(prng, 2048);

// Create the keys
RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);

// Create the signature on the file
std::string signature;
RSASS<PSS, SHA256>::Signer signer(privateKey);
FileSource("zero.dat", true, new SignerFilter(prng, signer, new StringSink(signature)));

// Print the signature
std::cout << "Signature: ";
StringSource(signature, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;

// Create a verifier
byte result = 0;
RSASS<PSS, SHA256>::Verifier verifier(publicKey);
SignatureVerificationFilter filter(verifier, new ArraySink(&result, sizeof(result)));

// Wrap the data in sources
StringSource ss(signature, true);
FileSource fs("zero.dat", true);

// Add the data to the filter
ss.TransferTo(filter);
fs.TransferTo(filter);

// Signal end of data. The filter will
// verify the signature on the file
filter.MessageEnd();

if (result)
    std::cout << "Verified signature on file" << std::endl;
else
    std::cout << "Failed to signature hash on file" << std::endl;

Bouncy Castle

If you are using Bouncy Castle and need to recover the signature under a PSSR scheme, then see Iso9796d2PssSigner class in the Org.BouncyCastle.Crypto.Signers namespace. Also see BouncyCastle RSA Probabilistic Signature Scheme with Recovery on Stack Overflow.

Downloads

RSA-SSA-Test.zip - Demonstrates RSA-SSA (Appendix)

RSA-SSA-Filter-Test.zip - Demonstrates RSA-SSA (Appendix) using Filters

RSA-PSSR-Test.zip - Demonstrates RSA-PSSR (Recovery)

RSA-PSSR-Filter-Test.zip Demonstrates RSA-PSSR (Recovery) using Filters

RSA-SSA-PKCSv15-Test.zip - Demonstrates RSA-SSA (PKCS v1.5)