Related
here is the json I want to parse:
{"jsonrpc":"2.0","method":"subscription","params":{"channel":"book.BTC-PERPETUAL.raw","data":{"type":"change","timestamp":1635513739435,"prev_change_id":6807100702,"instrument_name":"BTC-PERPETUAL","change_id":6807100703,"bids":[["new",60772.0,50.0], "demo"],"asks":[]}}}
and here is my code to get the values from a child:
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
std::cout << "\njsonrpc: " << pt.get<std::string>("jsonrpc") << std::endl;
std::cout << "\nmethod: " << pt.get<std::string>("method") << std::endl;
std::cout << "\nparams: " << pt.get<std::string>("params") << std::endl;
std::cout << "\nparams.channel: " << pt.get<std::string>("params.channel") << std::endl;
std::cout << "\nparams.data: " << pt.get<std::string>("params.data") << std::endl;
std::cout << "\nparams.data.timestamp: " << pt.get<std::string>("params.data.timestamp") << std::endl;
std::cout << "\nparams.data.instrument_name: " << pt.get<std::string>("params.data.instrument_name") << std::endl;
but when I tried to parse the array values like ("params.data.bids") it return nothing
I need help in parsing an array
You should use a JSON Library:
Live On Compiler Explorer
#include <boost/json.hpp>
#include <boost/json/src.hpp> // header-only for Compiler Explorer
#include <iostream>
namespace json = boost::json;
int main() {
auto req = json::parse(R"({
"jsonrpc": "2.0",
"method": "subscription",
"params": {
"channel": "book.BTC-PERPETUAL.raw",
"data": {
"type": "change",
"timestamp": 1635513739435,
"prev_change_id": 6807100702,
"instrument_name": "BTC-PERPETUAL",
"change_id": 6807100703,
"bids": [
["new", 60772.0, 50.0], "demo"],
"asks": []
}
}
})") .as_object();
auto& params = req["params"].as_object();
auto& data = params["data"].as_object();
std::cout << "jsonrpc: " << req["jsonrpc"].as_string() << "\n";
std::cout << "methdo: " << req["method"].as_string() << "\n";
std::cout << "params.channel: " << params["channel"].as_string() << "\n";
std::cout << "params.data: " << params["data"] << "\n";
std::cout << "params.data.timestamp: " << data["timestamp"] << "\n";
std::cout << "params.data.instrument_name: " << data["instrument_name"] << "\n";
}
Prints
jsonrpc: "2.0"
methdo: "subscription"
params.channel: "book.BTC-PERPETUAL.raw"
params.data: {"type":"change","timestamp":1635513739435,"prev_change_id":6807100702,"instrument_name":"BTC-PERPETUAL","change_id":6807100703,"bids":[["new",6.0772E4,5E1],"demo"],"asks":[]}
params.data.timestamp: 1635513739435
params.data.instrument_name: "BTC-PERPETUAL"
bid: ["new",6.0772E4,5E1]
bid: "demo"
Adding the asks/bids:
for (auto& bid : data["bids"].as_array()) {
std::cout << "bid: " << bid << "\n";
}
for (auto& ask : data["asks"].as_array()) {
std::cout << "ask: " << ask << "\n";
}
Prints
bid: ["new",6.0772E4,5E1]
bid: "demo"
Alternative / Bonus
You can also use value_to conversion to your own types:
struct request {
std::string jsonrpc, method;
struct params_t {
std::string channel;
struct data_t {
std::string type;
size_t timestamp, prev_change_id, change_id;
std::string instrument_name;
std::vector<json::value> bids, asks;
} data;
} params;
};
See it Live
Or flattened:
struct request {
using values = std::vector<json::value>;
std::string jsonrpc, method, channel, type, instrument_name;
size_t timestamp, prev_change_id, change_id;
values bids, asks;
friend request tag_invoke(json::value_to_tag<request>, json::value const& v)
{
auto& params = v.at("params");
auto& data = params.at("data");
return request{
value_to<std::string>(v.at("jsonrpc")),
value_to<std::string>(v.at("method")),
value_to<std::string>(params.at("channel")),
value_to<std::string>(data.at("type")),
value_to<std::string>(data.at("instrument_name")),
value_to<size_t>(data.at("timestamp")),
value_to<size_t>(data.at("prev_change_id")),
value_to<size_t>(data.at("change_id")),
value_to<values>(data.at("bids")),
value_to<values>(data.at("asks")),
};
}
};
See it Live As Well
#include <boost/json.hpp>
#include <boost/json/src.hpp> // header-only for Compiler Explorer
#include <iostream>
namespace json = boost::json;
struct request {
using values = std::vector<json::value>;
std::string jsonrpc, method, channel, type, instrument_name;
size_t timestamp, prev_change_id, change_id;
values bids, asks;
friend request tag_invoke(json::value_to_tag<request>, json::value const& v)
{
auto& params = v.at("params");
auto& data = params.at("data");
return request{
value_to<std::string>(v.at("jsonrpc")),
value_to<std::string>(v.at("method")),
value_to<std::string>(params.at("channel")),
value_to<std::string>(data.at("type")),
value_to<std::string>(data.at("instrument_name")),
value_to<size_t>(data.at("timestamp")),
value_to<size_t>(data.at("prev_change_id")),
value_to<size_t>(data.at("change_id")),
value_to<values>(data.at("bids")),
value_to<values>(data.at("asks")),
};
}
};
int main() {
auto req = value_to<request>(json::parse(R"({
"jsonrpc": "2.0",
"method": "subscription",
"params": {
"channel": "book.BTC-PERPETUAL.raw",
"data": {
"type": "change",
"timestamp": 1635513739435,
"prev_change_id": 6807100702,
"instrument_name": "BTC-PERPETUAL",
"change_id": 6807100703,
"bids": [
["new", 60772.0, 50.0], "demo"],
"asks": []
}
}
})"));
std::cout << "jsonrpc: " << req.jsonrpc << "\n";
std::cout << "methdo: " << req.method << "\n";
std::cout << "params.channel: " << req.channel << "\n";
std::cout << "data.timestamp: " << req.timestamp << "\n";
std::cout << "data.instrument_name: " << req.instrument_name << "\n";
for (auto& bid : req.bids) std::cout << "bid: " << bid << "\n";
for (auto& ask : req.asks) std::cout << "ask: " << ask << "\n";
}
I am referring to sample code on Crypto++ wiki at CCM Mode. I have done few modifications for testing purposes and now it is throwing:
terminate called after throwing an instance of 'HashVerificationFilter::HashVerificationFailed'
what(): HashVerificationFilter: message hash or MAC not valid
Aborted (core dumped)
I have seen 'message hash or MAC not valid' exception after decryption. But I would like to pass byte array instead of string, so I think I have to use sizeof() operator instead of size(). The code is below.
When pass key and iv, sizeof function is giving their sizes as 8 instead 16 bytes.
Is the error because of AuthenticatedDecryptionFilter::DEFAULT_FLAGS?
Here is the code:
#include <iostream>
#include <cstdio>
//#include "libcryptoWrapperGCM.h"
using std::cout;
using std::endl;
using std::cerr;
#include <string>
#include <stdint.h>
using std::string;
#include "hex.h"
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;
#include "cryptlib.h"
using CryptoPP::BufferedTransformation;
using CryptoPP::AuthenticatedSymmetricCipher;
#include "filters.h"
using CryptoPP::Redirector;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
#include "aes.h"
using CryptoPP::AES;
#include "gcm.h"
using CryptoPP::GCM;
#include "assert.h"
void GCM_Encode(byte key[], byte iv[], string pdata, string cipher, const int TAG_SIZE)
{
// Encrypted, with Tag
string encoded;
cout << "key size= " << sizeof(key) << endl;
cout << "IV size= " << sizeof(iv) << endl;
/*********************************\
\*********************************/
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
StringSource ss1( pdata, true,
new AuthenticatedEncryptionFilter( e,
new StringSink( cipher ), false, TAG_SIZE
) // AuthenticatedEncryptionFilter
); // StringSource
cout << "Encoded value: " << cipher << endl;
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
}
/*********************************\
\*********************************/
void GCM_Decode(byte key[], byte iv[], string cipher, string rpdata, const int TAG_SIZE)
{
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
AuthenticatedDecryptionFilter df( d,
new StringSink( rpdata ),
AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE
); // AuthenticatedDecryptionFilter
// The StringSource dtor will be called immediately
// after construction below. This will cause the
// destruction of objects it owns. To stop the
// behavior so we can get the decoding result from
// the DecryptionFilter, we must use a redirector
// or manually Put(...) into the filter without
// using a StringSource.
StringSource ss2( cipher, true,
new Redirector( df /*, PASS_EVERYTHING */ )
); // StringSource
// If the object does not throw, here's the only
// opportunity to check the data's integrity
if( true == df.GetLastResult() ) {
cout << "recovered text: " << rpdata << endl;
}
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
}
int main()
{
byte key[]= {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};
byte iv[] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f};
string pdata = "Authenticated Encryption";
const int TAG_SIZE = 12;
string rpdata, cipher;
//void GCM_Encode(byte key[], byte iv[], string pdata, string cipher, const int TAG_SIZE)
GCM_Encode( key, iv, pdata, cipher, TAG_SIZE);
cout << "======INT MAIN======" << endl;
cout << "Encryption complete" << endl;
cout << "cipher= " << cipher << endl;
//void GCM_Decode(byte key[], byte iv[], string cipher, string rpdata, const int TAG_SIZE)
GCM_Decode( key, iv, cipher, rpdata, TAG_SIZE);
cout << "Decryption complete" << endl;
cout << "rpdata= " << rpdata << endl;
return 0;
}
GDB output is,
Encryption complete
cipher=
HashVerificationFilter: message hash or MAC not valid
[Inferior 1 (process 16632) exited with code 01]
Compilation command
g++ -g3 -O3 -fPIC GCM_Test2.cpp -o GCMTest3 -lcryptopp -pthread -lrt -lc
Include path added using,
export CPLUS_INCLUDE_PATH=/usr/local/include/cryptopp
No output on locate raise.c
Problem is that both functions GCM_Encode were interpreting wrong size of key and iv.
cout << "sizeof(key)= " << sizeof(key) << endl;
cout << "sizeof(iv)= " << sizeof(iv) << endl;
is 8 and not 16. So idea is to convert it to string and get its size using .size()
std::string key1( reinterpret_cast<char const*>(key), sizeof(key) )
std::string iv1( reinterpret_cast<char const*>(iv), sizeof(iv) )
But remember to provide original byte array to SetKeyWithIV function as it does not accept string as its argument.
e.SetKeyWithIV( key, key1.size(), iv, iv1.size() )
I have a JSON string, in which value is actually a string, but I need to access it like JSON array. {"data" : "[A,B,C]"}.
Is there any way to parse VALUE as Indexed array using RapidJSON so that I get a document like: {"0" : "A", "1" : "B", "2" : "C"}
RapidJSON Should be fully in compliance With RFC7159 / ECMA-404.
No there is no way, you need to do it manually.
For example using regex:
#include <iostream>
#include <iterator>
#include <string>
#include <regex>
static std::regex value_regex("[\\s]*([\\w\\s]+),?", std::regex::optimize);
std::string parse(const std::string& value) {
if (value.at(0) != '[' || value.at(value.size() - 1) != ']') {
throw std::invalid_argument("Error in Parse [must start with '[' and end with ']']: " + value);
}
std::string result("{");
std::sregex_iterator next(++value.begin(), --value.end(), value_regex, std::regex_constants::match_continuous);
std::sregex_iterator end;
size_t len = 0, cont = 0;
while (next != end) {
if (cont) {
result.append(", \"").append(std::to_string(cont)).append("\":\"").append(next->str(1)).append(1, '\"');
} else {
result.append(1, '\"').append(std::to_string(cont)).append("\":\"").append(next->str(1)).append(1, '\"');
}
len += next->length(0);
++next;
++cont;
}
if (len != value.size() - 2) {
throw std::invalid_argument("Error in Parse [" + std::to_string(len) + "]: " + value);
}
result.append(1, '}');
return result;
}
int main() {
// Tests
try {
std::string value("[A,B,C,D,E]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[ A, B X, Z]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[A,BaaayyX, Z]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[A,B,C,]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
// Errors
try {
std::string value("A,B,C");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("A,B,C]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[A,B,C");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[,A,B,C]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::invalid_argument& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::exception& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("[");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::exception& err) {
std::cout << err.what() << std::endl;
}
try {
std::string value("]");
std::cout << value << " -> " << parse(value) << std::endl;
} catch (const std::exception& err) {
std::cout << err.what() << std::endl;
}
}
Output:
[A,B,C,D,E] -> {"0":"A", "1":"B", "2":"C", "3":"D", "4":"E"}
[ A, B X, Z] -> {"0":"A", "1":"B X", "2":"Z"}
[A,BaaayyX, Z] -> {"0":"A", "1":"BaaayyX", "2":"Z"}
[A,B,C,] -> {"0":"A", "1":"B", "2":"C"}
[] -> {}
A,B,C -> Error in Parse [must start with '[' and end with ']']: A,B,C
A,B,C] -> Error in Parse [must start with '[' and end with ']']: A,B,C]
[A,B,C -> Error in Parse [must start with '[' and end with ']']: [A,B,C
[,A,B,C] -> Error in Parse [0]: [,A,B,C]
-> basic_string
[ -> Error in Parse [must start with '[' and end with ']']: [
] -> Error in Parse [must start with '[' and end with ']']: ]
You can customize your regex to become more flexible or more strict.
I have noticed that whenever I use dark background images for tesseract I am getting a segmentation fault. I was trying to extract symbols using this code
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include <iostream>
#include <map>
#include <bits/stdc++.h>
using namespace std;
int main()
{
char *outText;
map<pair<char*,char*>,float> matrix;
set<char> allChars;
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
// Initialize tesseract-ocr with English, without specifying tessdata path
if (api->Init(NULL, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
exit(1);
}
int a[256][256];
for(int i=0;i<256;i++){
for(int j=0;j<256;j++){
a[i][j]=0;
}
}
// Open input image with leptonica library
string images[] = {List of images};
for (int ii=0;ii<7;ii++){
Pix *image = pixRead((const char*) images[ii].c_str());
cout << images[ii] << endl;
api->Init(NULL, "eng");
api->SetImage(image);
string valid_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890~`!##$%^&*()_-+=,./<>/:;'[]{}|";
api->SetVariable("tessedit_char_whitelist", valid_set.c_str());
api->SetVariable("save_blob_choices", "T");
//api->SetRectangle(37, 128,648, 523);
//api->SetRectangle(30, 86, 590, 100);
//api->SetRectangle(30,100,430,30);
api->Recognize(NULL);
tesseract::ResultIterator* ri = api->GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
if(ri != 0) {
do {
const char* symbol = ri->GetUTF8Text(level);
//cout << symbol << endl;
if(ri!=0){
float conf = ri->Confidence(level);
}
//cout << "err" << endl;
if(symbol != 0) {
//printf("symbol %s, conf: %f", symbol, conf);
bool indent = false;
tesseract::ChoiceIterator ci(*ri);
do {
const char* choice = ci.GetUTF8Text();
if (indent) //printf("\t\t ");
// printf("\t- ");
//cout << symbol<<" Look up "<<choice << endl;
matrix[make_pair(strdup(symbol), strdup(choice))]=ci.Confidence();
//printf("%s conf: %f\n", choice, ci.Confidence());
indent = true;
} while(ci.Next());
}
//printf("---------------------------------------------\n");
delete[] symbol;
} while((ri->Next(level)));
}
int count = 0;
for(map<pair<char*,char*>,float>::iterator it = matrix.begin();it!=matrix.end();it++){
allChars.insert((strdup)(it->first.first)[0]);
allChars.insert((strdup)(it->first.second)[0]);
//cout<<it->first.first<<" "<<it->first.second<<endl;
//cout << (strdup)(it->first.first)[0]<<" "<<(strdup)(it->first.second)[0]<<endl;
a[(strdup)(it->first.first)[0]][(strdup)(it->first.second)[0]]+=it->second;
count++;
}
// cout << count << endl;
for(set<char>::iterator it = allChars.begin();it!=allChars.end();it++){
//cout << *it << endl;
}
for(int i=0;i<256;i++){
for(int j=0;j<256;j++){
if(a[i][j]!=0){
///cout << i << " " <<j<<endl;
//cout << a[i][j]<<endl;
}
//cout << a[i][j] << endl;
}
}
api->End();
pixDestroy(&image);
}
return 0;
}`
Specifically in the code where
float conf = ri->Confidence(level);
So what can be the solution to this problem? Should we train with more dark images?
Edit:
Sample Image
I have this JWK (from https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-signature-26#appendix-A.1):
{"kty":"RSA",
"n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx
HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs
D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH
SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV
MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8
NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ",
"e":"AQAB",
"d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I
jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0
BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn
439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT
CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh
BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ"
}
I need to load it into an openSSL rsa struct, so that I can feed this into an EVP_SignFinal call. What format is "d"? PEM? or binary? how do I load it into a rsa struct?
I was able to do this using jwk-to-pem (https://github.com/Brightspace/node-jwk-to-pem#readme). I had problems getting jwk-to-pem working correctly in the nodejs environment on my server, so I just did it online here:
https://tonicdev.com/npm/jwk-to-pem
Put this is the code box at the top (this uses the values you provided):
var jwkToPem = require('jwk-to-pem');
var options = { private: false };
var jwk = {
"kty":"RSA",
"n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ",
"e":"AQAB",
"d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ"
}, pem = jwkToPem(jwk, options);
console.log(pem);
Then hit the "-> run" button, and you will get the following results:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0l8W2
6mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMs
D1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5
z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxv
b3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8q2et
hFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQAB
-----END RSA PUBLIC KEY-----
I was able to use this to convert my private user key for letsencrypt (letsencrypt.org) from JWK to PEM.
To convert a private key, change the value of the private option to true, and change the value of the jwk variable to your specific key in JWK format.
Obviously, you can output the contents of the pem variable in other methods than the console.log() javascript function.
What format is "d"? PEM? or binary?
The format is Base64URL encoding or "Base 64 Encoding with URL and Filename Safe Alphabet" of RFC 4648 (see section 5, Table 2 on page 7).
how do I load it into a rsa struct?
OK, so OpenSSL is painful. To load it in a RSA struct, you need no convert n, e and d from Base64URL to Base64. Here's how I did it in Crypto++ (you can do it in OpenSSL, but its going to hurt):
string nz = "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ";
string ez = "AQAB";
string dz = "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ";
string nn, ee, dd;
// First, convert Base64URL encoding to Base64
std::replace(nz.begin(), nz.end(), '-', '+');
std::replace(ez.begin(), ez.end(), '-', '+');
std::replace(dz.begin(), dz.end(), '-', '+');
std::replace(nz.begin(), nz.end(), '_', '/');
std::replace(ez.begin(), ez.end(), '_', '/');
std::replace(dz.begin(), dz.end(), '_', '/');
// Now, Base64 decode
StringSource ss1(nz, true, new Base64Decoder(new StringSink(nn)));
StringSource ss2(ez, true, new Base64Decoder(new StringSink(ee)));
StringSource ss3(dz, true, new Base64Decoder(new StringSink(dd)));
EDIT: Crypto++ now has the Base64URLEncoder and Base64URLDecoder classes, so you don't need the find/replace operations.
After the code above runs, nn, ee and dd are binary strings (i.e., non-ASCII characters). From there, you can load them into Integer's and get the Base 10 string with:
Integer n((byte*)nn.data(), nn.size());
Integer e((byte*)ee.data(), ee.size());
Integer d((byte*)dd.data(), dd.size());
cout << "N: " << endl << n << endl << endl;
cout << "E: " << endl << e << endl << endl;
cout << "D: " << endl << d << endl << endl;
$ ./cryptopp-test.exe
N:
20446702916744654562596343388758805860065209639960173505037453331270270518732245
08977372301204320323609709562340204469011575537734525469644875960570778896584888
95017468362112062706438336639499925362469853626937363871851454247879222415857219
92924045675229348655595626434390043002821512765630397723028023792577935108185822
75369257422156693093780503115582009714681996492027000881132703628678639279359312
17624250488602118597634417704467037220158572506211078553986931332640811506974231
88751482418465308470313958250757758547155699749157985955379381294962058862159085
915015369381046959790476428631998204940879604226680285601.
E:
65537.
D:
23583109899396195101799862623499368829246520235662137651186064319555667005065389
11356936879137503597382515919515633242482643314423192704128296593672966061810149
31632061789402182278402640746140338406535182197235078430096761014345948432406842
76746396884059179774424728049430754391920261073195321175575450790865379829879825
22396626690057355718157403493216553255260857777965627529169195827622139772389760
13057175483467867984218114225248961766503010944557397801270779301059273764049922
00150833924259148778478404572782464027609558833769999511998277062853834711506435
61410605789710883438795588594095047409018233862167884701.
OpenSSL needs n, e, d, p and q for private key operations. d mod p-1, d mod q-1 and inv q mod p are optional. With only n, e, d, you need to solve for the missing parameters (at minimum p and q). The two tough ones are p and q. Here's the Crypto++ code to solve for them (feel free to convert to OpenSSL):
Integer p, q;
RSA_solve(n, e, d, p, q);
cout << "P: " << endl << p << endl << endl;
cout << "Q: " << endl << q << endl << endl;
And:
void RSA_solve(const Integer& n, const Integer& e, const Integer& d, Integer& p, Integer& q)
{
AutoSeededRandomPool prng;
Integer g = 1;
unsigned int SAFETY = 0;
STEP_1:
const Integer k = e * d - 1;
if(!k.IsEven())
throw runtime_error("e * d - 1 is not even");
STEP_2:
// g = 3, 5, 7, ...
g += 2; while(!VerifyPrime(prng, g)) g += 2;
Integer t = k;
STEP_3:
if(SAFETY++ > 128)
throw runtime_error("could not factor n");
if(!t.IsEven())
goto STEP_2;
t /= 2;
Integer x = a_exp_b_mod_c(g, t, n);
STEP_4:
if(!(x > 1))
goto STEP_3;
Integer y = GCD(x-1, n);
if(!(y > 1))
goto STEP_3;
p = std::max(y, n/y);
q = std::min(y, n/y);
Integer check = p * q;
if(n != check)
throw runtime_error("n != p * q");
}
That results in:
P:
15737705590244743839558616502896029191493197327877753279847020015603526753735923
90718294084119093232085749598005372477289597182368848096852332845373492076546615
30801859889389455120932077199406250387226339056140578989122526711937239401762061
949364440402067108084155200696015505170135950332209194782224750221639.
Q:
12992175256740635899099334754006444501823007340248226099417932857332386190837921
12746269565434716649972371852989646481333243433270528522640603220881224011247812
49085873464824282666514908127141915943024862618996371026577302203267804867959037
802770797169483022132210859867700312376409633383772189122488119155159.
d mod p-1, d mod q-1 and inv q mod p are left as an exercise to the reader (but they are easy, especially in Crypto++). Your modified RSA_solve might look like:
void RSA_solve(const Integer& n, const Integer& e, const Integer& d,
Integer& p, Integer& q,
Integer& dmodp1, Integer& dmodq1, Integer& invqmodp)
Now, switch to OpenSSL with your Base 10 (decimal) strings:
const char nz[] =
"20446702916744654562596343388758805860065209639960173505037453331270270518732245"
"08977372301204320323609709562340204469011575537734525469644875960570778896584888"
"95017468362112062706438336639499925362469853626937363871851454247879222415857219"
"92924045675229348655595626434390043002821512765630397723028023792577935108185822"
"75369257422156693093780503115582009714681996492027000881132703628678639279359312"
"17624250488602118597634417704467037220158572506211078553986931332640811506974231"
"88751482418465308470313958250757758547155699749157985955379381294962058862159085"
"915015369381046959790476428631998204940879604226680285601";
const char ez[] = "65537";
const char dz[] =
"23583109899396195101799862623499368829246520235662137651186064319555667005065389"
"11356936879137503597382515919515633242482643314423192704128296593672966061810149"
"31632061789402182278402640746140338406535182197235078430096761014345948432406842"
"76746396884059179774424728049430754391920261073195321175575450790865379829879825"
"22396626690057355718157403493216553255260857777965627529169195827622139772389760"
"13057175483467867984218114225248961766503010944557397801270779301059273764049922"
"00150833924259148778478404572782464027609558833769999511998277062853834711506435"
"61410605789710883438795588594095047409018233862167884701";
const char pz[] =
"15737705590244743839558616502896029191493197327877753279847020015603526753735923"
"90718294084119093232085749598005372477289597182368848096852332845373492076546615"
"30801859889389455120932077199406250387226339056140578989122526711937239401762061"
"949364440402067108084155200696015505170135950332209194782224750221639";
const char qz[] =
"12992175256740635899099334754006444501823007340248226099417932857332386190837921"
"12746269565434716649972371852989646481333243433270528522640603220881224011247812"
"49085873464824282666514908127141915943024862618996371026577302203267804867959037"
"802770797169483022132210859867700312376409633383772189122488119155159";
using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
#define UNUSED(x) ((void)x)
int main(int argc, char* argv[])
{
UNUSED(argc); UNUSED(argv);
int rc;
long err;
RSA_ptr rsa(RSA_new(), ::RSA_free);
BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
rc = BN_dec2bn(&n, nz);
if(rc == 0 || n == NULL) {
cerr << "BN_dec2bn failed for n" << endl;
exit(1);
}
rsa->n = n;
rc = BN_dec2bn(&e, ez);
if(rc == 0 || e == NULL) {
cerr << "BN_dec2bn failed for e" << endl;
exit(1);
}
rsa->e = e;
rc = BN_dec2bn(&d, dz);
if(rc == 0 || d == NULL) {
cerr << "BN_dec2bn failed for d" << endl;
exit(1);
}
rsa->d = d;
rc = BN_dec2bn(&p, pz);
if(rc == 0 || p == NULL) {
cerr << "BN_dec2bn failed for p" << endl;
exit(1);
}
rsa->p = p;
rc = BN_dec2bn(&q, qz);
if(rc == 0 || q == NULL) {
cerr << "BN_dec2bn failed for q" << endl;
exit(1);
}
rsa->q = q;
[Exercise left to the reader]
rc = RSA_check_key(rsa.get());
err = ERR_get_error();
if(rc != 1) {
cerr << "RSA_check_key failed, error 0x" << std::hex << err << endl;
exit(1);
}
[Continues at next question below]
...
}
Here are the fields you need to provide in struct rsa (from <openssl dir>/crypto/rsa/rsa.h):
struct rsa_st
{
...
/* functional reference if 'meth' is ENGINE-provided */
ENGINE *engine;
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
BIGNUM *dmp1;
BIGNUM *dmq1;
BIGNUM *iqmp;
...
};
so that I can feed this into an EVP_SignFinal call...
EVP_SignFinal needs an EVP_PKEY and you have an RSA. So:
EVP_PKEY_ptr pkey(EVP_PKEY_new(), ::EVP_PKEY_free);
rc = EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
err = ERR_get_error();
if(rc != 1) {
cerr << "EVP_PKEY_set1_RSA failed, error 0x" << std::hex << err << endl;
exit(1);
}
The set1 means the reference count is bumped on the RSA*. That's OK. If it s was set0, you would have had to release your copy (that is, use rsa.release() rather than rsa.get()) to avoid a double free.
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
EVP_MD_CTX_init(ctx.get());
const EVP_MD* md = EVP_sha256();
rc = EVP_SignInit(ctx.get(), md);
err = ERR_get_error();
if(rc != 1) {
cerr << "EVP_SignInit_ex failed, error 0x" << std::hex << err << endl;
exit(1);
}
const char message[] = "Now is the time for all good men...";
rc = EVP_SignUpdate(ctx.get(), message, (unsigned int)sizeof(message));
err = ERR_get_error();
if(rc != 1) {
cerr << "EVP_SignUpdate failed, error 0x" << std::hex << err << endl;
exit(1);
}
const unsigned int req = std::max(EVP_MD_size(md), EVP_PKEY_size(pkey.get()));
unique_ptr<unsigned char[]> signature(new unsigned char[req]);
unsigned int size = req;
rc = EVP_SignFinal(ctx.get(), signature.get(), &size, pkey.get());
err = ERR_get_error();
if(rc != 1) {
cerr << "EVP_SignFinal failed, error 0x" << std::hex << err << endl;
exit(1);
}
size = std::min(size, (unsigned int)EVP_MD_size(md));
cout << "Signature: ";
for(unsigned i = 0; i < size; i++)
cout << std::hex << (signature[i] & 0xFF);
cout << endl;
Here's the Pastebin of the Crypto++ code used above: http://pastebin.com/9Rm7bxZp.
Here's the Pastebin of the OpenSSL code used above: http://pastebin.com/aGVpj4FW.
Here's the output of the OpenSSL program:
$ ./openssl-test.exe
Signature: 78f2c9af23b9a2a42e3b57dec454fa43ea6627992f48d40a33da6a7c93f98b4
Correction of the jwktopem example above:
var options = { private: true };
It's simple, really. If it contains d, it's a private rsa key, not a public one.