0

I have the below openssl code to decode json content. It works fine if the encoded data is

eyJ0ZXN0MSI6eyJ2YWwyIjotOTEuNiwidmFsMyI6NDAuMTIzNH19

but it does not work if the encoded data is

eyJ0ZXN0MSI6eyJsYXRpdHVkZSI6LTkxLjYsInZhbDMiOjQwfX0

It's not decoding the last 2 right curly braces. Can somebody help me to understand why this is happening?

Note: the encoded string is the payload generated in jwt.io.

  1. eyJ0ZXN0MSI6eyJ2YWwyIjotOTEuNiwidmFsMyI6NDAuMTIzNH19 is encoded from

    { "test1": { "val2": -91.6, "val3": 40.1234 } }

decoded data with the below code:

(gdb) p *(decoded._M_impl._M_start)@decoded.size()

$12 = "{\"test1\":{\"val2\":-91.6,\"val3\":40.1234}}\000\000"
  1. eyJ0ZXN0MSI6eyJsYXRpdHVkZSI6LTkxLjYsInZhbDMiOjQwfX0 is encoded from

    { "test1": { "latitude": -91.6, "val3": 40 } }

decoded data with below code:

(gdb) p *(decoded._M_impl._M_start)@decoded.size()

$5 = "{\"test1\":{\"latitude\":-91.6,\"val3\":40\000\000"

The last 2 braces go missing only for this encoded data whereas the first one is decoded properly.

vector<unsigned char> Base64Decode(const char* encoded)
{
unique_ptr<BIO,BIOFreeAll> b64(BIO_new(BIO_f_base64()));
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
BIO* source = BIO_new_mem_buf((void*)encoded, -1); // read-only source
BIO_push(b64.get(), source);
const int maxlen = (strlen(encoded) / 4 * 3 + 1)+2;
vector<unsigned char> decoded(maxlen);
const int len = BIO_read(b64.get(), decoded.data(), maxlen);
decoded.resize(len);
return decoded;
}
schroeder
  • 123,438
  • 55
  • 284
  • 319
Scarlet
  • 11
  • 1

1 Answers1

4

The reason is that base64-encoded strings should be a multiple of 4 characters in length. If the base-64 string is not a multiple of 4 characters in length, then pad it using the '=' characters.

The following command runs successfully (as you stated in your question):

echo -n 'eyJ0ZXN0MSI6eyJ2YWwyIjotOTEuNiwidmFsMyI6NDAuMTIzNH19' | base64 -d
{"test1":{"val2":-91.6,"val3":40.1234}}

The following command throws an error (as you also stated in your question):

echo -n 'eyJ0ZXN0MSI6eyJsYXRpdHVkZSI6LTkxLjYsInZhbDMiOjQwfX0' | base64 -d
{"test1":{"latitude":-91.6,"val3":40}}base64: invalid input

The reason for the error is that the base-64 encoded string above is 51 characters in length. To solve the problem, pad the base-64 encoded string with an '=' character, so that it's 52 characters in length (52 is a multiple of 4):

echo -n 'eyJ0ZXN0MSI6eyJsYXRpdHVkZSI6LTkxLjYsInZhbDMiOjQwfX0=' | base64 -d
{"test1":{"latitude":-91.6,"val3":40}}
mti2935
  • 19,868
  • 2
  • 45
  • 64
  • Plus, though it doesn't affect the case posted, OpenSSL and (some? most?) older standalones use the original PEM/MIME alphabet with + / while JWT/JWS/JWE uses a different 'URL-safe' alphabet with - _; see rfc4648. – dave_thompson_085 Aug 04 '20 at 01:30