0

I need to decode some base64 encoded strings in Javascript. At the beginning, I was using atob() / btoa() functions. But IE does not support those functions. So I chose to use a javascript library for that (https://code.google.com/p/stringencoders/source/browse/trunk/javascript/base64.js)

If I try to decode the following string with btoa / atob , I don't have any problem (on chrome) But, when I try to decode with the base64 library, the operation fails.

Here is the chain I try to decode :

eyJpZHMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMTEiLCIiLCIxMSIsIiIsIiJdLCJkdXJhdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjUiLCIiLCIiXSwic2xvdE51bWJlcnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMjMiLCIiLCIzMSIsIiIsIiJdLCJwb3NpdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjE1IiwiIiwiIl19

According to the base64 library's code, the string length must be a multiple of 4. In this case, the string length is 280. 280%4 = 0 so the decoding should not fail, but it does !

  • The string has been encoded with the same library.

  • I have the same problem with other base64 libraries I found on internet

What is wrong with this string ? Why does the decoding fails with the library but not with the atob()'s chrome implementation ?

Cheers, Steven

Steven
  • 313
  • 3
  • 10
  • How far back do you plan on supporting IE for? `atob` and `btoa` are supported in IE10 and up. – pseudosavant Jul 24 '14 at 20:59
  • What browser are you encountering this error in? The library is working fine for me in IE10 and Chrome 36. See this jsbin: http://jsbin.com/xukiwu/1/edit?html,js,console – pseudosavant Jul 24 '14 at 21:05
  • Hi, It is supposed to work on IE8. My problem is that it is not stable, sometimes the decoding fails, sometimes it doesn't . – Steven Jul 24 '14 at 21:06
  • For me on Chrome 36, base64.decode("eyJpZHMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMTEiLCIiLCIxMSIsIiIsIiJdLCJkdXJhdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjUiLCIiLCIiXSwic2xvdE51bWJlcnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMjMiLCIiLCIzMSIsIiIsIiJdLCJwb3NpdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjE1IiwiIiwiIl19"); Fails – Steven Jul 24 '14 at 21:09
  • This is a screen shot I took from my chrome console : http://s28.postimg.org/o9axv9mm5/b64.png – Steven Jul 24 '14 at 21:18

2 Answers2

1

Try this library: http://jsbase64.codeplex.com/releases/view/89265. It is fast and seems to be reliable; it actually has a test suite. I tested it in IE8 mode using the HTML below and it worked well. I also created a JSBin (JSBin doesn't support IE8) with the library inlined here: http://jsbin.com/faxawa/1/edit?js,console.

There is also a long StackOverflow thread on various base64 libraries that is worth looking at here: Base64 encoding and decoding in client-side Javascript.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>    
</head>
<body>
    <script>
    /* B64 code from here: http://jsbase64.codeplex.com/releases/view/89265 */

/*
Copyright Vassilis Petroulias [DRDigit]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var B64 = {
    alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
    lookup: null,
    ie: /MSIE /.test(navigator.userAgent),
    ieo: /MSIE [67]/.test(navigator.userAgent),
    encode: function (s) {
        var buffer = B64.toUtf8(s),
            position = -1,
            len = buffer.length,
            nan0, nan1, nan2, enc = [, , , ];
        if (B64.ie) {
            var result = [];
            while (++position < len) {
                nan0 = buffer[position];
                nan1 = buffer[++position];
                enc[0] = nan0 >> 2;
                enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
                if (isNaN(nan1))
                    enc[2] = enc[3] = 64;
                else {
                    nan2 = buffer[++position];
                    enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
                    enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
                }
                result.push(B64.alphabet.charAt(enc[0]), B64.alphabet.charAt(enc[1]), B64.alphabet.charAt(enc[2]), B64.alphabet.charAt(enc[3]));
            }
            return result.join('');
        } else {
            var result = '';
            while (++position < len) {
                nan0 = buffer[position];
                nan1 = buffer[++position];
                enc[0] = nan0 >> 2;
                enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
                if (isNaN(nan1))
                    enc[2] = enc[3] = 64;
                else {
                    nan2 = buffer[++position];
                    enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
                    enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
                }
                result += B64.alphabet[enc[0]] + B64.alphabet[enc[1]] + B64.alphabet[enc[2]] + B64.alphabet[enc[3]];
            }
            return result;
        }
    },
    decode: function (s) {
        if (s.length % 4)
            throw new Error("InvalidCharacterError: 'B64.decode' failed: The string to be decoded is not correctly encoded.");
        var buffer = B64.fromUtf8(s),
            position = 0,
            len = buffer.length;
        if (B64.ieo) {
            var result = [];
            while (position < len) {
                if (buffer[position] < 128) 
                    result.push(String.fromCharCode(buffer[position++]));
                else if (buffer[position] > 191 && buffer[position] < 224) 
                    result.push(String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63)));
                else 
                    result.push(String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63)));
            }
            return result.join('');
        } else {
            var result = '';
            while (position < len) {
                if (buffer[position] < 128) 
                    result += String.fromCharCode(buffer[position++]);
                else if (buffer[position] > 191 && buffer[position] < 224) 
                    result += String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63));
                else 
                    result += String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63));
            }
            return result;
        }
    },
    toUtf8: function (s) {
        var position = -1,
            len = s.length,
            chr, buffer = [];
        if (/^[\x00-\x7f]*$/.test(s)) while (++position < len)
            buffer.push(s.charCodeAt(position));
        else while (++position < len) {
            chr = s.charCodeAt(position);
            if (chr < 128) 
                buffer.push(chr);
            else if (chr < 2048) 
                buffer.push((chr >> 6) | 192, (chr & 63) | 128);
            else 
                buffer.push((chr >> 12) | 224, ((chr >> 6) & 63) | 128, (chr & 63) | 128);
        }
        return buffer;
    },
    fromUtf8: function (s) {
        var position = -1,
            len, buffer = [],
            enc = [, , , ];
        if (!B64.lookup) {
            len = B64.alphabet.length;
            B64.lookup = {};
            while (++position < len)
                B64.lookup[B64.alphabet.charAt(position)] = position;
            position = -1;
        }
        len = s.length;
        while (++position < len) {
            enc[0] = B64.lookup[s.charAt(position)];
            enc[1] = B64.lookup[s.charAt(++position)];
            buffer.push((enc[0] << 2) | (enc[1] >> 4));
            enc[2] = B64.lookup[s.charAt(++position)];
            if (enc[2] == 64) 
                break;
            buffer.push(((enc[1] & 15) << 4) | (enc[2] >> 2));
            enc[3] = B64.lookup[s.charAt(++position)];
            if (enc[3] == 64) 
                break;
            buffer.push(((enc[2] & 3) << 6) | enc[3]);
        }
        return buffer;
    }
};

var encoded = 'eyJpZHMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMTEiLCIiLCIxMSIsIiIsIiJdLCJkdXJhdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjUiLCIiLCIiXSwic2xvdE51bWJlcnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiMjMiLCIiLCIzMSIsIiIsIiJdLCJwb3NpdGlvbnMiOlsiIiwiIiwiIiwiIiwiIiwiIiwiIiwiNyIsIiIsIjE1IiwiIiwiIl19';

var decodedNative = (typeof atob === 'function' ? atob(encoded) : undefined);
var decodedLibrary = B64.decode(encoded);

console.log(decodedNative);
console.log(decodedLibrary);
console.log('Match: ' + (decodedLibrary === decodedNative));
    </script>
</body>
</html>
Community
  • 1
  • 1
pseudosavant
  • 7,056
  • 2
  • 36
  • 41
0

If you are using angular or nodeJs just do this:

let b64Data = myBase64Url.split(',', 2)[1];
var byteArray = new Buffer(b64Data ,'base64');

This serves as replacement for atob() method which is not supported in IE. This works on almost all the browsers.

Additionally if you want to create a blob out of the ByteArray, this is the way to go

let contentType = myBase64Url.split(';', 2)[0].split(':')[1];
var blob = new Blob([byteArray], {type: contentType });
hakuna
  • 6,243
  • 10
  • 52
  • 77