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)