1

I am lost with decoding the following base64 string

nVJPb4IwFL/7KUjvAgUM8CIuZiabicsSNR68deXhWKBteGVx336FbJnz4MG+U997/f1L5yTaxsBGn3Rvt0hGK0LPO7eNIhhnBes7BVpQTaBEiwRWwm75soHID8F02mqpGzZZrwpGScZjkUgpMolpFCfRLH/DPKlmaZXGMkqrMq/CMi6Zd8COaq0K5lCYtybqca3ICmVdK+TZlIfTONxzDtEMeHZk3grJ1krY8dW7tQaCgEepH7rikLoTEHaf2AWNPtXqodUlFonDVr++9rpgH1jq82BsusT8eWPa1yd9RLHdf7HFZD4MYBTTXWRwOwJBjnZQxRaDKnKy6tL4RFrWnWzQl7qdBxfIPzwGdlbYnu4I+wrh0Tm9A8U7iKbH28s0EsCulxKJBuLgmvm693f//6sW3w==

It should be valid base64 data representing deflate data of original XML. When I try online decoder here: https://www.samltool.com/decode.php it gives me the proper XML.

I am doing these two steps:

string text = MyClass::decode_base64(input);
text = MyClass::stringDeflateDecode(text);

First I decode the base64 string:

string MyClass::decode_base64(string str)
{
    using namespace boost::archive::iterators;
    typedef transform_width<binary_from_base64<remove_whitespace<string::const_iterator> >, 8, 6> ItBinaryT;
    try {
        boost::erase_all(str, "\r");
        boost::erase_all(str, "\n");
        // If the input isn't a multiple of 4, pad with =
        size_t num_pad_chars((4 - str.size() % 4) % 4);
        str.append(num_pad_chars, '=');
        size_t pad_chars(std::count(str.begin(), str.end(), '='));
        std::replace(str.begin(), str.end(), '=', 'A'); // replace '=' by base64 encoding of '\0'
        string output(ItBinaryT(str.begin()), ItBinaryT(str.end()));
        output.erase(output.end() - pad_chars, output.end());
        return output;
    } catch (...) {
        return string("");
    }
}

The code is basically from here Decode Base64 String Using Boost and it was working fine for text-only base64 decoding (no binary deflate data).

Then I would like to decode the deflate:

string MyClass::stringDeflateDecode(const std::string& data)
{
    stringstream compressed(data);
    stringstream decompressed;

    boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
    in.push(boost::iostreams::zlib_decompressor());
    in.push(compressed);
    boost::iostreams::copy(in, decompressed);

    return decompressed.str();
}

but ::copy operation throws an exception: zlib error: iostream error

Thanks for any hints!

Martin
  • 165
  • 1
  • 15
  • Since it complains about zlib, did you try to decompress deflated binary data provided by SAML tool? That way you'll be able to check if it's about the algorithm being used or the C++ code snippet you are using in a wrong way. – karastojko Oct 30 '18 at 14:50
  • Yeah, a simple check using `base64 -d | zlib-flate -uncompress` fails too – sehe Oct 30 '18 at 17:50
  • Hmm, well, so I changed the zlib_decompressor() constructor line to boost::iostreams::zlib_decompressor(boost::iostreams::zlib_params(boost::iostreams::zlib::default_compression, boost::iostreams::zlib::deflated, 15, 8, boost::iostreams::zlib::default_strategy, true)) and it works now, the text is successfully decoded. But I am not sure if it is not just a coincidence. I am probably providing information which should be in header. Maybe if encoded with different parameters it will fail? Maybe it is default always... – Martin Oct 31 '18 at 08:14

2 Answers2

3

That is Base-64 encoded raw deflate data. That means compressed data in the deflate format, but no zlib nor gzip wrapper around that deflate data. It looks like zlib_decompressor has a noheader option that you should set to true.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
1

Wikipedia specifies:

SAML requests or responses transmitted via HTTP Redirect have a SAMLRequest or SAMLResponse query string parameter, respectively. Before it's sent, the message is deflated (without header and checksum), base64-encoded, and URL-encoded, in that order. Upon receipt, the process is reversed to recover the original message.

The problem here is the absense of the header and checksum. I don't think boost has the library functions you need.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for hint. Yes, I am already sending my SAML requests for a few weeks and it is working fine with multiple different public Identity Providers. That's exactly what I had to do: deflate, base64 and URL-encode. For deflate I am using the reversed mechanism with boost: boost::iostreams::filtering_streambuf out; out.push(boost::iostreams::zlib_decompressor(boost::iostreams::zlib_params(boost::iostreams::zlib::default_compression, boost::iostreams::zlib::deflated, 15, 8, boost::iostreams::zlib::default_strategy, true))); IdPs accepted it. Other way is not working – Martin Oct 31 '18 at 07:59
  • Maybe they are just tolerant and accept also with header and checksum. So I guess I need to find some other way how to "inflate" the data, as the information is there available as samltool page proves. – Martin Oct 31 '18 at 08:00
  • I cannot edit anymore, but it was supposed to be zlib_compressor of course in the sample above. – Martin Oct 31 '18 at 08:12