ProstDev ProstDev
Tutorials Jul 21, 2020 · 7 min read

Understanding the "illegal base64 character" error (Java, Groovy and Mule 4 - DW 2.0)

Are you familiar with the fromBase64 or the toBase64 functions from DataWeave 2.0? What about the getUrlEncoder or the getEncoder functions from Java? Do you know the differences between the "basic" Base 64 encoding and the "URL and Filename safe" Base 64 encoding? Maybe you're here because you keep getting this error.

By Alex Martinez
Understanding the "illegal base64 character" error (Java, Groovy and Mule 4 - DW 2.0)

Are you familiar with the fromBase64 or the toBase64 functions from DataWeave 2.0? What about the getUrlEncoder or the getEncoder functions from Java? Do you know the differences between the “basic” Base 64 encoding and the “URL and Filename safe” Base 64 encoding? Well, you may have guessed it by now, but you’re about to find out the answers to these questions!

Or maybe you’re here because you keep getting the “Illegal base64 character” error in DataWeave. Even if you get this error using any other programming language, this post can help you understand why it is happening.

The Problem

I was recently presented with this problem when Maria Isabel Vargas asked a question about base64 decoding in a Slack channel. She was using the fromBase64 function that’s available in Mule 4 - DataWeave 2.0 inside the dw::core::Binaries module to transform a basic Base 64 string into a binary value.

The problem was that the server was returning a Base 64 URL Encoded String (e.g. “cHJvc3RkZXY_YmxvZw==”) opposed to the basic Base 64 string from which the fromBase64 function attempts to transform. As a result, the Transform Message component was returning this error: “Illegal base64 character 5f”. This error happens when the string that you are trying to transform contains a character not recognized by the basic Base 64 Alphabet (in this case it was an underscore character). Below you can see which characters are accepted.

ietf.org - RFC 4648

Since the server was using the Base 64 URL encoding, the string that we were trying to decode in DataWeave contained different characters from the ones above because the Base 64 URL has a different alphabet. You can see this alphabet below.

ietf.org - RFC 4648

Notice that the characters 62 and 63 differ from the basic Base 64 Alphabet and the Base 64 URL Alphabet. The first one contains the characters plus (+) and slash (/), while the second one uses the characters minus (-) and underline (_).

In other words, if you have an encoded string like “cHJvc3RkZXY_YmxvZw==”, you wouldn’t be able to transform it using a basic Base64 decoder because it contains characters that are not recognized by its alphabet (the underline character).

You can get these two errors when you try to transform a Base 64 URL string using the fromBase64 DW function (at least when this post was created):

  • java.lang.IllegalArgumentException: Illegal base64 character 5f (when containing an underscore)
  • java.lang.IllegalArgumentException: Illegal base64 character 2d (when containing a minus)

DataWeave Playground running fromBase64 on a URL-encoded string and throwing an Illegal base64 character error.

The Solution

Java contains a function called getUrlDecoder, which is used to decode a string that was encoded using the Base 64 URL alphabet. This is exactly the problem that we were trying to solve. But before you start panicking and coding your solution using Java and creating a whole Java class, it’s way easier to just use the Mule 4 Scripting Module. I’ll show you how.

This module should already be installed in your Studio. However, if you can’t see it, you can download it from Exchange. Alternatively, you can use the “Add Modules” button from Anypoint Studio, which is located on your Mule Palette.

Anypoint Studio Mule Palette with the Add Modules and Search in Exchange options.

After that, you can simply take the Execute component and drag-and-drop it into your flow. For this demonstration, I will be using an HTTP Listener as a trigger, and I will send the encoded string in the body of the request from Postman.

Once you have the Execute component, you can select “Groovy” as your Engine and paste the following line of code into your script:

Base64.getUrlDecoder().decode(payload)

You should end up with this:

Flow with an HTTP Listener and an Execute component whose Groovy code calls Base64.getUrlDecoder().decode(payload).

As you can see in this Groovy script, we are using the getUrlDecoder function to decode whatever is in our payload. The result will be returned to our Postman application in the body of the response.

You need to make sure that the payload that is sent to the Groovy script is a Java String. So, let’s add a Transform Message right after the HTTP Listener and transform the payload into a Java string. Like this:

Flow with a Transform Message added between the Listener and Execute, casting the payload as String for Java.

Let’s start this Mule app and test!

We send the same string as before (cHJvc3RkZXY_YmxvZw==) from Postman, and we expect to get the decoded message as a response.

Postman POST to localhost:8081/flow1 returning 200 OK with the decoded "prostdev?blog" response.

And… it worked!

Try it yourself

Do you want to experiment a bit more with all of this? For sure! Let me give you a quick guide on how to do this.

1. Encode the bytes/string into a Basic 64 URL encoded string

You will need Java (or you can also do it in Mule 4 using a Groovy script) to get the encoded string first. You don’t need to download and install Java onto your computer to do this; you can search for a free online Java compiler like this one: TutorialsPoint Java Compiler

There, just copy and paste this Java code:

import java.util.Base64;
public class Base64UrlEncoding {
    public static void main(String []args) {
        byte[] bytes = "prostdev?blog".getBytes();
        String encodedString = new String(Base64.getUrlEncoder().encode(bytes));
        
        System.out.println(encodedString); 
    }
}

Next, click on “Execute” in the top left corner. You will see the encoded string on the right side of the website (the last line in the white background). Copy this string and save it somewhere so that you can use it in the next steps.

Online Java compiler running the Base64UrlEncoding class, printing the encoded string cHJvc3RkZXY_YmxvZw==.

2. Create your Mule App in Anypoint Studio

Follow the steps explained previously in “The Solution”, or paste this code into the XML configuration of your file:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
	xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
	xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd">
	<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="14ccf972-4bba-4789-b1f2-0ce9b8dde24f" >
		<http:listener-connection host="0.0.0.0" port="8081" />
	</http:listener-config>
	<flow name="testbase64Flow1" doc:id="ea17a087-9c19-4c9d-8379-23a7ffed04f2" >
		<http:listener doc:name="Listener" doc:id="87b87abf-95c5-4734-873d-c731ade282f4" config-ref="HTTP_Listener_config" path="/flow1"/>
		<ee:transform doc:name="Transform Message" doc:id="9bc88283-7ee0-4c5a-a466-a5cfc8fface5" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/java
---
payload as String]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<scripting:execute engine="groovy" doc:name="Execute" doc:id="7abfd446-8474-4512-a709-63c89bcd5cef" >
			<scripting:code ><![CDATA[Base64.getUrlDecoder().decode(payload)]]></scripting:code>
		</scripting:execute>
	</flow>
</mule>

3. Create a Postman request

If you don’t have Postman, you can download it for free from here: Postman Download. When you open it, you just have to click on the plus (+) button as you can see here:

Postman with no collections yet and an arrow pointing at the plus button to create a new request.

Then, add this in the Request URL section:

localhost:8081/flow1

And add the previously copied string inside the Request Body. Don’t forget to select “Raw” and “Text” from the two dropdowns that are over the request body text area.

Postman request set to localhost:8081/flow1 with the encoded string in a raw Text request body.

4. Run your Mule App

Once it is deployed successfully, you can click on the big blue “Send” button from Postman and see the results which should match the string you encoded in Step 1 (using Java).

Anypoint Studio console showing the testbase64 app starting and reaching DEPLOYED status.

Postman POST to localhost:8081/flow1 returning 200 OK with the decoded "prostdev?blog" response.

Recap

Let’s do a quick recap on what we just learned from this post:

  • The basic Base 64 alphabet can recognize the characters plus (+) and slash (/).
  • The Base 64 URL alphabet can recognize the characters minus (-) and underline (_).
  • Depending on how a string was encoded (using basic Base 64 or Base 64 URL encoding), it can include any of the characters from the chosen alphabet.
  • An encoded string with one alphabet may not be decoded using the other alphabet’s decoding system. If the decoder attempts to read an unrecognized character, it’ll send an Illegal base64 character error message.
  • You can’t decode a Base 64 URL string with the fromBase64 DataWeave 2.0 function.
  • You can decode a Base 64 URL string by using an Execute component along with a Groovy engine and the getUrlDecoder Java function.

Had you seen this error before? How did you solve it?

Prost!

-Alex

References

GitHub repository

ProstDev GitHub - testbase64

Reader notes

Helpful comments preserved from the original post.

  • wella test · May 10, 2023

    Hi Alex, I am trying to encode pdf containing some text and images/logos to base 64. I need to send endoded base 64 content to external system. I tried to encode pdf with tobase() function in dataweave and using the java function which you have given in your blog. But base64 content generated using these two ways is not correct. When external system is generating pdf from base 64 content , the resulting pdf is showing error as Message: Illegal character: 41 . Basically images/logos in PDF are not loading properly When I send base 64 content generated using online tool ( https://base64.guru/converter/encode/pdf ). then external system is able to generate PDF properly from it.

  • Reply to wella test

    Alex Martinez · May 10, 2023

    That is so interesting! I have never tried it with pdfs. If you think this is a bug, feel free to raise an issue here: https://github.com/mulesoft-labs/data-weave-rfc You can also ask a question to the wider community using Stack Overflow with the DataWeave tag: https://stackoverflow.com/questions/tagged/dataweave?sort=newest

  • tanushreemazumdar08 · Feb 20, 2021

    Hi Alexendra I have recieved your response via email so writing again. I am receiving below payload from an external system :

    payload=%7B%22type%22%3A%22block_actions%22%2C%22user%22%3A%7B%22id%22%3A%22U01M26265NU%22%2C%22username%22%3A%22connect2tanushree%22%2C%22name%22%3A%22connect2tanushree%22%2C%22team_id%22%3A%22T01LCAPH1BL%22%7D%2C%22api_app_id%22%3A%22A01L5J2FV8W%22%2C%22token%22%3A%22u2AkH2O8aShNqskXd1S464z5%22%2C%22container%22%3A%7B%22type%22%3A%22message%22%2C%22message_ts%22%3A%221612281350.000100%22%2C%22channel_id%22%3A%22D01LT6C44F3%22%2C%22is_ephemeral%22%3Afalse%7D%2C%22trigger_id%22%3A%221781264358804.aa5a08357f8700923ea6e05f4164de4d%22%2C%22team%22%3A%7B%22id%22%3A%22T01LCAPH1BL%22%2C%22domain%22%3A%22workdaytestteamgroup%22%7D%2C%22enterprise%22%3Anull%2C%22is_enterprise_install%22%3Afalse%2C%22channel%22%3A%7B%22id%22%3A%22D01LT6C44F3%22%2C%22name%22%3A%22directmessage%22%7D%2C%22message%22%3A%7B%22bot_id%22%3A%22B01KZPXR02K%22%2C%22type%22%3A%22message%22%2C%22text%22%3A%22Important+Message+%22%2C%22user%22%3A%22U01LBEQRAAH%22%2C%22ts%22%3A%221612281350.000100%22%2C%22team%22%3A%22T01LCAPH1BL%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22header%22%2C%22block_id%22%3A%22bBeX%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Track+Your+Time%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22r271%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AGeneral+%26amp%3B+Administrative%3A%2A%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22rSrvb%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22+%22%2C%22verbatim%22%3Afalse%7D%2C%22accessory%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22static_select-action%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Select+Number+of+Hours%22%2C%22emoji%22%3Atrue%7D%2C%22options%22%3A%5B%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%221%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%222%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-2%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%223%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-3%22%7D%5D%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22694Ex%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AProjects%3A%2A%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22yOkLb%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22Country+Expansion+ATP%22%2C%22verbatim%22%3Afalse%7D%2C%22accessory%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22static_select-action%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Select+Number+of+Hours%22%2C%22emoji%22%3Atrue%7D%2C%22options%22%3A%5B%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%221%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%222%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-2%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%223%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-3%22%7D%5D%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22Led%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AWhen%3A%2A%5CnJan+31+-+Feb+06%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22actions%22%2C%22block_id%22%3A%22eA%5C%2F%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22button%22%2C%22action_id%22%3A%22V2%2Bep%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit+Timesheet%22%2C%22emoji%22%3Atrue%7D%2C%22style%22%3A%22primary%22%2C%22value%22%3A%22click_me_123%22%7D%5D%7D%5D%7D%2C%22state%22%3A%7B%22values%22%3A%7B%22rSrvb%22%3A%7B%22static_select-action%22%3A%7B%22type%22%3A%22static_select%22%2C%22selected_option%22%3A%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%7D%7D%2C%22yOkLb%22%3A%7B%22static_select-action%22%3A%7B%22type%22%3A%22static_select%22%2C%22selected_option%22%3A%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%7D%7D%7D%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT01LCAPH1BL%5C%2F1775098058146%5C%2FbJ3EryWaj4hHIt0grYdt102B%22%2C%22actions%22%3A%5B%7B%22action_id%22%3A%22V2%2Bep%22%2C%22block_id%22%3A%22eA%5C%2F%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit+Timesheet%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22click_me_123%22%2C%22style%22%3A%22primary%22%2C%22type%22%3A%22button%22%2C%22action_ts%22%3A%221613828122.887562%22%7D%5D%7D

    My goal is to convert this to json. For this I did the same scripting tool->groovy script execution by feeding java input of this payload. I am receiving below error when posting from postman: java.lang.IllegalArgumentException: Illegal base64 character 25 Please let me know how can I fix this. Thank Yiu Tanushree

  • Reply to tanushreemazumdar08

    Alex Martinez · Mar 11, 2021

    Hi @Tanushree! Your string is not a Base64 encoded payload, it's a URI component string. To convert that string into a JSON format, you can use this code:

    %dw 2.0
    output application/json
    import * from dw::core::URL
    ---
    read(decodeURIComponent(payload))

    You can read more about the URL module here: https://docs.mulesoft.com/mule-runtime/4.3/dw-url and the read function here: https://docs.mulesoft.com/mule-runtime/4.3/dw-core-functions-read Why is it not a base64 encoded string? The error message mentions a character 25, which is a percentage (%) character (from Hex: https://ascii.cl/ ). If you take a look at the Base64 alphabet in this blog post, for both regular and URL encoding, none of them include the percentage character. For this same reason, it wouldn't be able to decode a string that contains a percentage character (% - 25 in Hex). This is why you get the illegal base64 character 25 error - it doesn't exist in its alphabet! Also, if you try to encode any string into a base64 encoding, you'll notice two things: 1) The base64 encoded string contains a final double equals sign (==), which your string doesn't contain. 2) The base64 encoded string is made of (what seems to be) random characters (like cHJvc3RkZXY_YmxvZw==), or not easily legible by a human. While your string can be read by a human, for example, right at the start of your string it contains %7B%22type%22%3A%22block_actions%22 - which can be read by a human. You can confirm these 2 points by testing with some Java code directly at https://www.tutorialspoint.com/compile_java_online.php

    import java.util.Base64;
    public class Base64UrlEncoding {
        public static void main(String []args) {
            byte[] bytes = "{ \"json example\": \"123\" }".getBytes();
            String encodedString = new String(Base64.getEncoder().encode(bytes));
            String decodedString = new String(Base64.getDecoder().decode(encodedString));
    
            System.out.println(encodedString); 
            //eyAianNvbiBleGFtcGxlIjogIjEyMyIgfQ==
            System.out.println(decodedString); //{ "json example": "123" }
        }
    }
  • Kishan Mohan · Sep 29, 2020

    Thanks for this post @Alex, for URL encoding do we need to use Java? Is there any option to generate byte array from a string payload? If yes, then it seems we can use Base64.getUrlencoder().encode(payload)

  • Reply to Kishan Mohan

    Alex Martinez · Mar 11, 2021

    Hi Kishan! No, you don't need to use Java. This was just an example that's using Java. And, yes! You can use that function to encode inside the Groovy script :) I didn't use it in this example because the problem was just to decode what was received from the server.

FAQs

Frequently asked questions about this post.

  • Why do I get the "Illegal base64 character" error in DataWeave?

    This error happens when the string you are trying to transform contains a character that is not recognized by the basic Base 64 alphabet. In the post's case, the server returned a Base 64 URL encoded string (like cHJvc3RkZXY_YmxvZw==) containing an underscore, which the basic alphabet doesn't include, so the fromBase64 function throws Illegal base64 character 5f.

  • What's the difference between the basic Base 64 alphabet and the Base 64 URL alphabet?

    They differ in characters 62 and 63: the basic Base 64 alphabet uses plus (+) and slash (/), while the Base 64 URL (and Filename safe) alphabet uses minus (-) and underline (_). A string encoded with one alphabet may not be decodable with the other's decoding system.

  • How do I decode a Base 64 URL encoded string in Mule 4?

    You can't use the fromBase64 DataWeave 2.0 function for a URL-encoded string, but you can use the Mule 4 Scripting Module: drop in an Execute component, select the Groovy engine, and run Base64.getUrlDecoder().decode(payload). Make sure to add a Transform Message before it that casts the payload to a Java String (output application/java with payload as String).

  • Why won't the fromBase64 DataWeave function decode my URL-encoded string?

    The fromBase64 function transforms a basic Base 64 string, whose alphabet does not include the minus or underline characters used by the Base 64 URL encoding. When it hits an unrecognized character it throws an error, such as Illegal base64 character 5f for an underscore or Illegal base64 character 2d for a minus.

  • Why does my URI component string throw an Illegal base64 character error?

    Because a URI component string is not a Base64 encoded payload. As Alex notes in the comments, a string like %7B%22type%22%3A%22block_actions%22 contains a percentage character (% - 25 in Hex) that doesn't exist in either Base 64 alphabet, and it's human-readable and lacks the trailing == of an encoded string. To convert it to JSON, use read(decodeURIComponent(payload)) with the dw::core::URL module instead.

Search

Loading search…