I use rapid json to serialize a dict, the key is uint32 and the value is a long string. The code is:
rapidjson::StringBuffer buffer();
rapidjson::Write<< rapidjson::StringBuffer>> writer(buffer);
root.Accept(writer);
const char* json_str = buffer.GetString();
But, I found the final json_str is truncated. Does any one known how to avoid truncation?
If assume that the truncation has place due to zero character inside your long string then you can avoid it by stop using char * and use std::string instead.
rapidjson::StringBuffer buffer();
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
root.Accept(writer);
std::string json_str = std::string(buffer.GetString(), buffer.GetSize());
Related
I'm trying to base64 decode a string, then convert that value to a char array for later use. The decode works fine, but then I get garbage data when converting.
Here's the code I have so far:
std::string encodedData = "VGVzdFN0cmluZw=="; //"TestString"
std::vector<BYTE> decodedData = base64_decode(encodedData);
char* decodedChar;
decodedChar = new char[decodedData.size() +1]; // +1 for the final 0
decodedChar[decodedData.size() + 1] = 0; // terminate the string
for (size_t i = 0; i < decodedData.size(); ++i) {
decodedChar[i] = decodedData[i];
}
vector<BYTE> is a typedef of unsigned char BYTE, as taken from this SO answer. The base64 code is also from this answer (the most upvoted answer, not the accepted answer).
When I run this code, I get the following value in the VisualStudio Text Visualiser:
TestStringÍ
I've also tried other conversion methods, such as:
char* decodedChar = reinterpret_cast< char *>(&decodedData[0]);
Which gives the following:
TestStringÍÍÍýýýýÝÝÝÝÝÝÝ*b4d“
Why am I getting the garbage data at the end of the string? What am i doing wrong?
EDIT: clarified which answer in the linked question I'm using
char* decodedChar;
decodedChar = new char[decodedData.size() +1]; // +1 for the final 0
Why would you manually allocate a buffer and then copy to it when you have std::string available that does this for you?
Just do:
std::string encodedData = "VGVzdFN0cmluZw=="; //"TestString"
std::vector<BYTE> decodedData = base64_decode(encodedData);
std::string decodedString { decodedData.begin(), decodedData.end() };
std::cout << decodedString << '\n';
If you need a char * out of this, just use .c_str()
const char* cstr = decodedString.c_str();
If you need to pass this on to a function that takes char* as input, for example:
void someFunc(char* data);
//...
//call site
someFunc( &decodedString[0] );
We have a TON of functions and abstractions and containers in C++ that were made to improve upon the C language, and so that programmers wouldn't have to write things by hand and make same mistakes every time they code. It would be best if we use those functionalities wherever we can to avoid raw loops or to do simple modifications like this.
You are writing beyond the last element of your allocated array, which can cause literally anything to happen (according to the C++ standard). You need decodedChar[decodedData.size()] = 0;
My goal is to get this:
BYTE Data1[] = {0x6b,0x65,0x79};
BYTE Data2[] = {0x6D,0x65,0x73,0x73,0x61,0x67,0x65};
But my starting point is:
std::string msg = "message";
std::string key = "key";
I am not able to get from std::string to BYTE[].
I tried the following:
std::vector<BYTE> msgbytebuffer(msg.begin(), msg.end());
BYTE* Data1 = &msgbytebuffer[0];
This didn't cause compile or run time error. However, the end result (I feed this to a winapi function - crypto api) was not the same as when I used the actual byte array like in top most ({0x6D,0x65,0x73,0x73,0x61,0x67,0x65}).
You can use string::c_str() function which returns a pointer to c style string that can be passed to winapi functions like:
foo(string.c_str());
What it actually does is that it returns a pointer to an array that contains a null-terminated sequence of characters.
I suppose BYTE[] is actually a char array. You can assign your std::string to char array by doing:
std::string str = "hello";
BYTE byte[6]; // null terminated string;
strcpy(byte, str.c_str()); // copy from str to byte[]
If you want to copy the str without the 0 at the end, use strncpy instead:
BYTE byte[5];
strncpy(byte, str.c_str(), str.length());
Seems me that winapi is waiting a null terminated c-string. You can achieve that by using:
msg.c_str();
or, using your BYTE type, something like that:
std::vector<BYTE> msgbytebuffer(msg.length() + 1, 0);
std::copy(msg.begin(), msg.end(), msgbytebuffer.begin());
I would like to generate a random string with OpenSSL and use this as a salt in a hashing function afterwards (will be Argon2). Currently I'm generating the random data this way:
if(length < CryptConfig::sMinSaltLen){
return 1;
}
if (!sInitialized){
RAND_poll();
sInitialized = true;
}
unsigned char * buf = new unsigned char[length];
if (!sInitialized || !RAND_bytes(buf, length)) {
return 1;
}
salt = std::string (reinterpret_cast<char*>(buf));
delete buf;
return 0;
But a std::cout of salt doesn't seem to be a proper string (contains control symbols and other stuff). This is most likely only my fault.
Am I using the wrong functions of OpenSSL to generate the random data?
Or is my conversion from buf to string faulty?
Random data is random data. That's what you're asking for and that's exactly what you are getting. Your salt variable is a proper string that happens to contain unprintable characters. If you wish to have printable characters, one way of achieving that is using base64 encoding, but that will blow up its length. Another option is to somehow discard non-printable characters, but I don't see any mechanism to force RAND_bytes to do this. I guess you could simply fetch random bytes in a loop until you get length printable characters.
If encoding base64 is acceptable for you, here is an example of how to use the OpenSSL base64 encoder, extracted from Joe Linoff's Cipher library:
string Cipher::encode_base64(uchar* ciphertext,
uint ciphertext_len) const
{
DBG_FCT("encode_base64");
BIO* b64 = BIO_new(BIO_f_base64());
BIO* bm = BIO_new(BIO_s_mem());
b64 = BIO_push(b64,bm);
if (BIO_write(b64,ciphertext,ciphertext_len)<2) {
throw runtime_error("BIO_write() failed");
}
if (BIO_flush(b64)<1) {
throw runtime_error("BIO_flush() failed");
}
BUF_MEM *bptr=0;
BIO_get_mem_ptr(b64,&bptr);
uint len=bptr->length;
char* mimetext = new char[len+1];
memcpy(mimetext, bptr->data, bptr->length-1);
mimetext[bptr->length-1]=0;
BIO_free_all(b64);
string ret = mimetext;
delete [] mimetext;
return ret;
}
To this code, I suggest adding BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL), because otherwise you'll get a new line character inserted after every 64 characters. See OpenSSL's -A switch for details.
using rapidjson, how can i encode a number to json format? i have 123.321, i want to convert it to "123.321", then save it in a file. later using json convert it back to 123.321.
i don't want to use Document which saves as like "{"tag":"value"}", i want it to be saved as just a "value", then be converted back.
i have the following code to convert number to "number":
Value v(123);
StringBuffer mybuffer;
Writer<StringBuffer> mywriter(mybuffer);
v.Accept(mywriter);
const char* myjson = mybuffer.GetString();
how to convert it back? is the following a solution? i don't want to use handler!
Reader reader;
StringStream ss(myjson);
reader.Parse(ss, handler);
thanks for the upcoming helps.
I just found the answer after digging into google:
to encode a number to json using rapidjson:
Value v(123.321);
StringBuffer mybuffer;
Writer<StringBuffer> mywriter(mybuffer);
v.Accept(mywriter);
const char* myjson = mybuffer.GetString();
now myjson has "123.321" as its value.
then to decode myjson to a number:
Document d;
d.Parse(myjson); // myjson is "123.321"
assert(d.IsNumber());
value = d.GetDouble(); // now the value is 123.321
as simple as it gets.
I have RSA encrypted my string but it's now a unsigned char *. How do I create a human readable std::string that I can output for the user? I want to use it in an amazon signed url. Here are the meat and potatoes of the code from GitHub
unsigned char* RSA_SHA1_Sign(std::string policy, RSA *privateKey) throw(std::runtime_error)
{
//sha1 digest the data
unsigned char hash[SHA_DIGEST_LENGTH] = {'0'};
SHA1((const unsigned char *)policy.c_str(), policy.length(), hash);
// Sign the data
int rsaSize = RSA_size(privateKey);
// std::unique_ptr<unsigned char[]> signedData(new unsigned char[size]);//if c++11 available
unsigned char *signedData = (unsigned char *)malloc(sizeof(unsigned char) * rsaSize);
unsigned int signedSize = 0;
//use RSA_sign instead of RSA_private_encrypt
if(!RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, signedData, &signedSize, privateKey)){
throw std::runtime_error("Failed to sign");
}
return signedData;
}
std::string base64Encode(unsigned char *signedData)
{
//prepare
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bmem = BIO_new(BIO_s_mem());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64 = BIO_push(b64, bmem);
//write
BIO_write(b64, signedData, 256);
BIO_flush(b64);
//create string
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
std::string base64String(bptr->data);
BIO_free_all(b64);
return base64String;
}
int main(int argc, const char * argv[]) {
RSA *privateKey = createRSAFromPrivateKeyFile("/path/to/privatekey");
std::string sourceString = "testing";
std::string signature = RSA_SHA1_Sign(sourceString, privateKey);
std::string encodedSignature = base64Encode(signature);
std::cout << "RESULT: " << encodedSignature << std::endl;
return 0;
}
UPDATE: I was using the wrong sign function. Once updated, using base64 encode gave me the correct string.
RSA_PKCS1_PADDING
PKCS #1 v1.5 padding. This function does not handle the algorithmIdentifier specified in PKCS #1.
When generating or verifying PKCS #1 signatures, RSA_sign(3) and RSA_verify(3) should be used.
To save all the data, use this std::string constructor: std::string( char *data, int size ). The size will be useful as the output MIGHT contain a null character.
To send it to amazon over an url, consider using the base64 encoding, again, as the encrypted data might contain NULLs and other shenanigans.
Firstly, to get it into an std::string object, which will probably be helpful in general:
std::string s{private_key, size};
However, to then make that compatible with Amazon's scheme you'll need to pick out (or write your own) Base64 library and URL encoder to escape special URL chars. A cursory search of Google or StackOverflow will provide you with what you need in this respect and it's beyond the scope of this question to write out how to do Base64 encoding and URL escaping in C++.
Also, since you're using C++, consider std::unique_ptr<unsigned char[]> rather than straight-up malloc();
std::unique_ptr<unsigned char[]> signedData{new unsigned char[size]};