JSON Web Signature

From Crypto++ Wiki
Jump to navigation Jump to search

JSON Web Signature (JWS) provides content secured with digital signatures or Message Authentication Codes (MACs) using JSON-based data structures. JWS is specified in RFC 7515, JSON Web Signature . The code below shows you how to arrive at the known answers from the test vectors provided in RFC 7515, Appendix A, JWS Examples.

If you are using JSON Web Objects, then see Critical vulnerabilities in JSON Web Token libraries. The advisory shows several ways an attacker can manipulate JSON Web Signatures.

A related article is JSON Web Encryption, which shows how to encrypt messages with AES/GCM and encrypt Content Encryption Keys (CEK) with RSA.

HMAC/SHA256

Example A.1 in RFC 7515 provides a test vector for HMAC/SHA256. The code below shows you how to verify the test vector.

Also see HMAC for more details on mac'ing and verifying messages.

#include "cryptlib.h"
#include "hmac.h"
#include "sha.h"
#include "hex.h"
#include "files.h"
#include "base64.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    // A.1.1. Protected Header
    // {"typ":"JWT",
    //  "alg":"HS256"}
    const byte header[] = {
        123, 34, 116, 121, 112, 34, 58, 34, 74, 87, 84, 34, 44, 13, 10,
        32, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 125
    };

    // A.1.1. Payload
    // {"iss":"joe",
    //  "exp":1300819380,
    //  "http://example.com/is_root":true}
    const byte payload[] = {
        123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13,
        10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57,
        51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47,
        101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105,
        115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125
    };

    // A.1.1. Web key
    const byte webkey[] =
        "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
        "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";

    // Encode header and payload
    std::string h, p;
    StringSource(header, sizeof(header), true, new Base64URLEncoder(new StringSink(h)));
    StringSource(payload, sizeof(payload), true, new Base64URLEncoder(new StringSink(p)));

    std::cout << "Encoded header: ";
    StringSource (h, true, new FileSink(std::cout));
    std::cout << std::endl;

    std::cout << "Encoded payload: ";
    StringSource (p, true, new FileSink(std::cout));
    std::cout << std::endl;

    // Decode web key
    std::string k;
    StringSource(webkey, sizeof(webkey), true, new Base64URLDecoder(new StringSink(k)));

    std::cout << "Key: ";
    StringSource (k, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    // HMAC setup
    HMAC<SHA256> hmac((const byte*)&k[0], k.size());
    std::string result, message = h + "." + p;

    // Create HMAC over string
    StringSource(message, true, new HashFilter(hmac, new Base64URLEncoder(new StringSink(result))));

    std::cout << "HMAC: ";
    StringSource (result, true, new FileSink(std::cout));
    std::cout << std::endl;

    return 0;
}

Running the program results in the following output.

$ ./test.exe
Encoded header: eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
Encoded payload: eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF
tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
Key: 0323354B2B0FA5BC837E0665777BA68F5AB328E6F054C928A90F84B2D2502EBFD3FB5A92D20
647EF968AB4C377623D223D2E2172052E4F08C0CD9AF567D080A3
HMAC: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

RSA/PKCS1.5/SHA256

Example A.2 in RFC 7515 provides a test vector for RSA/PKCS1.5/SHA256. The code below shows you how to verify the test vector.

The "SSA" in the JSON name "RSASSA-PKCS1-v1_5 SHA-256" means "signature scheme with appendix." It means you have to present the message to the verifier function. This is opposed to a "signature scheme with recovery," which includes the message in the signature.

Also see RSA Signature Schemes for more details on signing and verifying messages.

#include "cryptlib.h"
#include "sha.h"
#include "hex.h"
#include "rsa.h"
#include "osrng.h"
#include "files.h"
#include "base64.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    // A.2.1. Protected Header
    // {"alg":"RS256"}
    const byte header[] = {
        123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 50, 53, 54, 34, 125
    };

    // A.2.1. Payload
    // {"iss":"joe",
    //  "exp":1300819380,
    //  "http://examplde.com/is_root":true}
    const byte payload[] = {
        123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13,
        10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57,
        51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47,
        101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105,
        115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125
    };

    // A.2.1. RSA parameters
    const std::string nz =
        "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
        "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
        "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
        "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
        "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
        "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ";
    const std::string ez = "AQAB";
    const std::string dz =
        "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
        "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
        "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
        "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
        "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
        "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ";
    const std::string pz = 
        "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
        "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
        "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc";
    const std::string qz =
        "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
        "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
        "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc";
    const std::string dpz =
        "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
        "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
        "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0";
    const std::string dqz =
        "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
        "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
        "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU";
    const std::string qiz =
        "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
        "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
        "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U";

    // Encode header and payload
    std::string hdr, pld;
    StringSource(header, sizeof(header), true, new Base64URLEncoder(new StringSink(hdr)));
    StringSource(payload, sizeof(payload), true, new Base64URLEncoder(new StringSink(pld)));

    std::cout << "Encoded header: ";
    StringSource (hdr, true, new FileSink(std::cout));
    std::cout << std::endl;

    std::cout << "Encoded payload: ";
    StringSource (pld, true, new FileSink(std::cout));
    std::cout << std::endl;

    // Decode RSA key parameters
    StringSource ssN(nz, true, new Base64URLDecoder);
    StringSource ssE(ez, true, new Base64URLDecoder);
    StringSource ssD(dz, true, new Base64URLDecoder);
    StringSource ssP(pz, true, new Base64URLDecoder);
    StringSource ssQ(qz, true, new Base64URLDecoder);
    StringSource ssDP(dpz, true, new Base64URLDecoder);
    StringSource ssDQ(dqz, true, new Base64URLDecoder);
    StringSource ssQinv(qiz, true, new Base64URLDecoder);

    Integer n(ssN, (size_t)ssN.MaxRetrievable());
    Integer e(ssE, (size_t)ssE.MaxRetrievable());
    Integer d(ssD, (size_t)ssD.MaxRetrievable());
    Integer p(ssP, (size_t)ssP.MaxRetrievable());
    Integer q(ssQ, (size_t)ssQ.MaxRetrievable());
    Integer dp(ssDP, (size_t)ssDP.MaxRetrievable());
    Integer dq(ssDQ, (size_t)ssDQ.MaxRetrievable());
    Integer qinv(ssQinv, (size_t)ssQinv.MaxRetrievable());

    // Setup objects
    AutoSeededRandomPool prng;

    RSA::PrivateKey key;
    key.Initialize(n, e, d ,p, q, dp, dq, qinv);

    RSASS<PKCS1v15, SHA256>::Signer signer(key);
    std::string result, message = hdr + "." + pld;

    // Create signature over string
    StringSource(message, true, new SignerFilter(prng, signer, new Base64URLEncoder(new StringSink(result))));

    std::cout << "Signature: ";
    StringSource (result, true, new FileSink(std::cout));
    std::cout << std::endl;

    return 0;
}

Running the program results in the following output.

$ ./test.exe
Encoded header: eyJhbGciOiJSUzI1NiJ9
Encoded payload: eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF
tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
Signature: cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHI
m4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnb
yTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwus
C-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhW
kWywlVmtVrBp0igcN_IoypGlUPQGe77Rw

To verify the message you only need [math]\displaystyle{ n }[/math] and [math]\displaystyle{ e }[/math]:

RSA::PublicKey key;
key.Initialize(n, e);

And then you would use a verifier instead of a signer:

RSASS<PKCS1v15, SHA256>::Verifier verifier(key);

ECDSA/P-256/SHA256

Example A.3 in RFC 7515 provides a test vector for ECDSA/P-256/SHA256. The P-256 curve used in JSON is secp256r1 in Crypto++. The code below shows you how to verify the test vector.

Note that ECDSA is a randomized scheme. Each run of the protocol will produce a different signature. Also note that JWS uses the signature format [math]\displaystyle{ S = r || s }[/math], and Crypto++ uses that format by default. You won't need any special conversions, like to or from ASN.1.

Also see Elliptic Curve Digital Signature Algorithm for more details on signing and verifying messages.

#include "cryptlib.h"
#include "sha.h"
#include "hex.h"
#include "osrng.h"
#include "files.h"
#include "base64.h"
#include "eccrypto.h"
#include "oids.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    // A.3.1. Protected Header
    // {"alg":"ES256"}
    const byte header[] = {
        123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 50, 53, 54, 34, 125
    };

    // A.3.1. Payload
    // {"iss":"joe",
    //  "exp":1300819380,
    //  "http://examplde.com/is_root":true}
    const byte payload[] = {
        123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13,
        10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57,
        51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47,
        101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105,
        115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125
    };

    // A.3.1. P-256 parameters
    // d is the private exponent (private key)
    // (x,y) is the public element (public key)
    const std::string xz =
        "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU";
    const std::string yz =
        "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0";
    const std::string dz =
        "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI";

    // Encode header and payload
    std::string hdr, pld;
    StringSource(header, sizeof(header), true, new Base64URLEncoder(new StringSink(hdr)));
    StringSource(payload, sizeof(payload), true, new Base64URLEncoder(new StringSink(pld)));

    std::cout << "Encoded header: ";
    StringSource (hdr, true, new FileSink(std::cout));
    std::cout << std::endl;

    std::cout << "Encoded payload: ";
    StringSource (pld, true, new FileSink(std::cout));
    std::cout << std::endl;

    // Decode P-256 key parameters
    // d is the private exponent (private key)
    // (x,y) is the public element (public key)
    StringSource ssX(xz, true, new Base64URLDecoder);
    StringSource ssY(yz, true, new Base64URLDecoder);
    StringSource ssD(dz, true, new Base64URLDecoder);

    Integer x(ssX, (size_t)ssX.MaxRetrievable());
    Integer y(ssY, (size_t)ssY.MaxRetrievable());
    Integer d(ssD, (size_t)ssD.MaxRetrievable());

    // Setup objects
    AutoSeededRandomPool prng;

    ECDSA<ECP, SHA256>::PrivateKey key;
    key.Initialize(ASN1::secp256r1(), d);

    ECDSA<ECP, SHA256>::Signer signer(key);
    std::string result, message = hdr + "." + pld;

    // Create signature over string
    StringSource(message, true, new SignerFilter(prng, signer, new Base64URLEncoder(new StringSink(result))));

    std::cout << "Signature: ";
    StringSource (result, true, new FileSink(std::cout));
    std::cout << std::endl;

    return 0;
}

Running the program results in the following output. Since the scheme is randomized, the signature is different than the one given in Example A.3.

$ ./test.exe
Encoded header: eyJhbGciOiJFUzI1NiJ9
Encoded payload: eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF
tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
Signature: mtYwk6kOvbfV1sI1npNdwWikYNfN-8CvXBejiCnnn-TdlyijrLwaK2kBkUxEMY6Gws95l
rdYD-tJ7GmrwSL63w

To verify the message you only need the public point [math]\displaystyle{ Q }[/math], which is [math]\displaystyle{ x }[/math] and [math]\displaystyle{ y }[/math]:

ECP::Point q(x, y);
ECDSA<ECP, SHA256>::PublicKey key;
key.Initialize(ASN1::secp256r1(), q);

// Verify the public key
AutoSeededRandomPool prng;
key.ThrowIfInvalid(prng, 3);

And then you would use a verifier instead of a signer:

ECDSA<ECP, SHA256>::Verifier verifier(key);

You can verify the signature from Example A.3 using this code:

// This is the signature provided in the example.
result =
    "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
    "pmWQxfKTUJqPP3-Kg6NU1Q";

std::string signature;
StringSource(result, true, new Base64URLDecoder(new StringSink(signature)));

ECP::Point q(x, y);
ECDSA<ECP, SHA512>::PublicKey pkey;
pkey.Initialize(ASN1::secp512r1(), q);
pkey.ThrowIfInvalid(prng, 3);

ECDSA<ECP, SHA256>::Verifier verifier(pkey);

// 1+16 is SIGNATURE_AT_BEGIN+THROW_EXCEPTION
StringSource(signature+message, true, new SignatureVerificationFilter(verifier, NULLPTR, 1+16));
std::cout << "Verified signature" << std::endl;

ECDSA/P-521/SHA512

Example A.4 in RFC 7515 provides a test vector for ECDSA/P-521/SHA512. The P-521 curve used in JSON is secp521r1 in Crypto++. The code below shows you how to verify the test vector.

Note that ECDSA is a randomized scheme. Each run of the protocol will produce a different signature. Also note that JWS uses the signature format [math]\displaystyle{ S = r || s }[/math], and Crypto++ uses that format by default. You won't need any special conversions, like to or from ASN.1.

Also see Elliptic Curve Digital Signature Algorithm for more details on signing and verifying messages.

#include "cryptlib.h"
#include "sha.h"
#include "hex.h"
#include "osrng.h"
#include "files.h"
#include "base64.h"
#include "eccrypto.h"
#include "oids.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    // A.3.1. Protected Header
    // {"alg":"ES512"}
    const byte header[] = {
        123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 53, 49, 50, 34, 125
    };

    // A.3.1. Payload
    // "Payload"
    const byte payload[] = {
        80, 97, 121, 108, 111, 97, 100
    };

    // A.3.1. P-521 parameters
    const std::string xz =
        "AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_"
        "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk";
    const std::string yz =
        "ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl"
        "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2";
    const std::string dz =
        "AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA"
        "xerEzgdRhajnu0ferB0d53vM9mE15j2C";

    // Encode header and payload
    std::string hdr, pld;
    StringSource(header, sizeof(header), true, new Base64URLEncoder(new StringSink(hdr)));
    StringSource(payload, sizeof(payload), true, new Base64URLEncoder(new StringSink(pld)));

    std::cout << "Encoded header: ";
    StringSource (hdr, true, new FileSink(std::cout));
    std::cout << std::endl;

    std::cout << "Encoded payload: ";
    StringSource (pld, true, new FileSink(std::cout));
    std::cout << std::endl;

    // Decode P-521 key parameters
    StringSource ssX(xz, true, new Base64URLDecoder);
    StringSource ssY(yz, true, new Base64URLDecoder);
    StringSource ssD(dz, true, new Base64URLDecoder);

    Integer x(ssX, (size_t)ssX.MaxRetrievable());
    Integer y(ssY, (size_t)ssY.MaxRetrievable());
    Integer d(ssD, (size_t)ssD.MaxRetrievable());

    // Setup objects
    AutoSeededRandomPool prng;

    ECDSA<ECP, SHA512>::PrivateKey key;
    key.Initialize(ASN1::secp521r1(), d);

    ECDSA<ECP, SHA512>::Signer signer(key);
    std::string result, message = hdr + "." + pld;

    // Create signature over string
    StringSource(message, true, new SignerFilter(prng, signer, new Base64URLEncoder(new StringSink(result))));

    std::cout << "Signature: ";
    StringSource (result, true, new FileSink(std::cout));
    std::cout << std::endl;

    return 0;
}

Running the program results in the following output. Since the scheme is randomized, the signature is different than the one given in Example A.4.

$ ./test.exe
Encoded header: eyJhbGciOiJFUzUxMiJ9
Encoded payload: UGF5bG9hZA
Signature: AdIjjg0L6q_6686pxHO6SSV5JVzIkzNEvf9BHzrIHWeK4XQ5PyQtv0x3aQSxIy8sKebkk
6mRgll3ssh0mUsaUvb3AK2VxU7gSQmPLDUlbbtmVdxymiI_Ie4tSZiOYHHmswNwUcl42akD5h6HR06__
OJcHrnAlYHETs3tJ24JNPjSutws

To verify the message you only need the public point [math]\displaystyle{ Q }[/math], which is [math]\displaystyle{ x }[/math] and [math]\displaystyle{ y }[/math]:

ECP::Point q(x, y);
ECDSA<ECP, SHA512>::PublicKey key;
key.Initialize(ASN1::secp521r1(), q);

// Verify the public key
AutoSeededRandomPool prng;
key.ThrowIfInvalid(prng, 3);

And then you would use a verifier instead of a signer:

ECDSA<ECP, SHA512>::Verifier verifier(key);

You can verify the signature from Example A.4 using this code:

// This is the signature provided in the example.
result =
    "AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq"
    "wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp"
    "EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn";

std::string signature;
StringSource(result, true, new Base64URLDecoder(new StringSink(signature)));

ECP::Point q(x, y);
ECDSA<ECP, SHA512>::PublicKey pkey;
pkey.Initialize(ASN1::secp521r1(), q);
pkey.ThrowIfInvalid(prng, 3);

ECDSA<ECP, SHA512>::Verifier verifier(pkey);

// 1+16 is SIGNATURE_AT_BEGIN+THROW_EXCEPTION
StringSource(signature+message, true, new SignatureVerificationFilter(verifier, NULLPTR, 1+16));
std::cout << "Verified signature" << std::endl;

Downloads

No downloads available.