JSON Web Encryption

From Crypto++ Wiki
Jump to navigation Jump to search

JSON Web Encryption (JWE) provides encrypted content using JSON-based data structures. JWE is specified in RFC 7516, JSON Web Encryption. The code below shows you how to arrive at the known answers from the test vectors provided in RFC 7516, Appendix A, JWE Examples.

A message is encrypted under JWE using a Content Encryption Key (CEK). The CEK is a symmetric key and used for bulk encryption. The CEK is also encrypted under the recipient's public RSA key and sent with the message so the receiver can decrypt the message.

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 Signature, which shows how to calculate HMACs and sign messages using RSA and ECDSA.

AES/GCM

A message is encrypted using a Content Encryption Key (CEK). The code that follows performs the authenticated encryption and arrives at the expected result from Appendix A.1.6, Content Encryption.

The cipher text is a concatenation of the confidential data and the authentication tag. If you want them as separate items, then you have to spit the byte array. Also see AuthenticatedEncryptionFilter on the wiki.

$ cat test.cxx
#include "cryptlib.h"
#include "aes.h"
#include "gcm.h"
#include "sha.h"
#include "files.h"
#include "base64.h"

#include <iostream>
#include <string>

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

    // A.1.2. Content Encryption Key (CEK)
    const byte cek[] = {
        177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
        212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
        234, 64, 252
    };

    // A.1.4. Initialization Vector
    const byte iv[] = {
        227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219
    };

    // A.1.5. Additional Authenticated Data
    const byte aad[] = {
        101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,
        116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73,
        54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81
    };

    // A.1. Plain text message
    const byte pdata[] =
        "The true sign of intelligence is not knowledge but imagination.";

    ByteQueue temp;
    GCM<AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(cek, sizeof(cek), iv, sizeof(iv));
    AuthenticatedEncryptionFilter filter(encryptor, new Redirector(temp));

    filter.ChannelPut(AAD_CHANNEL, aad, sizeof(aad));
    filter.ChannelMessageEnd(AAD_CHANNEL);
    filter.ChannelPut(DEFAULT_CHANNEL, pdata, sizeof(pdata)-1); // strip the C-string NULL
    filter.ChannelMessageEnd(DEFAULT_CHANNEL);

    // ByteQueue temp is 'confidential data || authentication tag'
    std::string cdata, atag;
    size_t len = temp.MaxRetrievable();

    // This separates confidential data and authentication tag
    temp.TransferTo(StringSink(cdata).Ref(), len-16);
    temp.TransferTo(StringSink(atag).Ref(), 16);

    std::cout << "Encrypted data: ";
    StringSource (cdata, true, new Base64URLEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    std::cout << "Auth tag: ";
    StringSource (atag, true, new Base64URLEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return 0;
}

Running the program results in the following.

$ g++ -Wall test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Encrypted data: 5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A
Auth tag: XFBoMYUZodetZdvTiFvSkQ

If you want to use a std::string instead of a ByteQueue then use the following code.

std::string temp;
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(cek, sizeof(cek), iv, sizeof(iv));
AuthenticatedEncryptionFilter filter(encryptor, new StringSink(temp));

filter.ChannelPut(AAD_CHANNEL, aad, sizeof(aad));
filter.ChannelMessageEnd(AAD_CHANNEL);
filter.ChannelPut(DEFAULT_CHANNEL, pdata, sizeof(pdata)-1); // strip the C-string NULL
filter.ChannelMessageEnd(DEFAULT_CHANNEL);

// std::string temp is 'confidential data || tag'
std::string cdata, atag;
cdata = temp.substr(0, temp.length() - 16);
atag = temp.substr(temp.length() - 16);

std::cout << "Encrypted data: ";
StringSource (cdata, true, new Base64URLEncoder(new FileSink(std::cout)));
std::cout << std::endl;

std::cout << "Auth tag: ";
StringSource (atag, true, new Base64URLEncoder(new FileSink(std::cout)));
std::cout << std::endl;

RSAES-OAEP

The Content Encryption Key (CEK) is encrypted with the receiver's public key. The code that follows performs the encryption of the CEK.

#include "cryptlib.h"
#include "oaep.h"
#include "rsa.h"
#include "sha.h"
#include "osrng.h"
#include "files.h"
#include "pubkey.h"
#include "base64.h"

#include <iostream>
#include <string>

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

    // A.1.2. Content Encryption Key (CEK)
    const byte cek[] = {
        177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
        212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
        234, 64, 252
    };

    // A.1.3. Key Encryption
    std::string nz =
        "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW"
        "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S"
        "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a"
        "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS"
        "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj"
        "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw";
    std::string ez = "AQAB";

    StringSource ssN(nz, true, new Base64URLDecoder);
    StringSource ssE(ez, true, new Base64URLDecoder);

    Integer n(ssN, (size_t)ssN.MaxRetrievable());
    Integer e(ssE, (size_t)ssE.MaxRetrievable());

    RSAES<OAEP<SHA256> >::Encryptor encryptor;
    encryptor.AccessKey().Initialize (n, e);
    
    AutoSeededRandomPool prng;
    std::string ekey;
    StringSource (cek, sizeof(cek), true, new PK_EncryptorFilter(prng, encryptor, new StringSink(ekey)));

    std::cout << "JWE Encrypted Key: ";
    StringSource (ekey, true, new Base64URLEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return 0;
}

OAEP uses randomized padding, so different runs of the protocol results in different cipher text.

$ ./test.exe | fold -w 80
JWE Encrypted Key: TsdyyuMutCN7NsgY1g7Bbru6hG6ghj_1c4OT4w88bdR6Bejcf0wDSaYBCFjEf
o7X_m9t35NFyXQq-6AxJIi4-vyEdNz6smj44L_Ofc01_jdy7kIA1jqqBuT4Rdgw__sjE1711D25c1_45
dmf2BbXojKH7o9pEGeGNKxM2CJAe1cWpf1LhQDg3u13ectHFPbCK_JdyGyjMPZQud79QIJicBEz-3SBd
aU8pg9T8N8qXaKDS0XG4ID-3cwtcXeFTSVxkBLM-7OZA200sC6xN130TGCmNkB_uHoogZWI-7KbePHmG
Yfd4jvWIQuHDLy2qJr6Nt2pRAcX8ZB4MlFTNk3xkg

$ ./test.exe | fold -w 80
JWE Encrypted Key: BqOMwOz9ZcSb54Z-vZfxc_o-9W-B0_8sTUYhkxR1muiVF3DUQ9LrylOOws2PI
-NGoEOibB1pXd8G7qfqW9xI_Hyp51Y80zvb2XNfTZ6UnJC8zZ0_By1u_AA3-UI_iDOjFkzX_gHYmTrlK
r-0eSkBpVLQzUV6qxgaD4HTGm6e61ISAu54L_K5ytu9STa-vGiUHCr-WJ1RjOQY_NcmlicbCgZbZBS53
VMK1ooWmfCMy4R2F3h8hAETLv9pNohVkHBd43clhGY6gxZTiLpWlE_U1La0patC_FIhJJuC0-iie99MT
7moqBmzYgfMoogjrJXj5ALceRg3VrX09OvsuasG-g

Downloads

No downloads available.