earthtrip

earthtrip

Port of Java to Elixir for HMAC signature generation causes invalid security header error in Apigee API

Hi all

I’m banging my head against the wall on this.. I have to port a bit of Java code over to Elixir/erlang and can’t seem to get this working.. I mean I can get my code to output base64 encoded strings but the server is rejecting them as invalid.

This is the function in question:

    private static String generateMacSignature(final String payload, final String resourceURI, final String host,
					       final String port, final String macId, final String key, final String httpMethod) {
    	String signature = null;
    	try {
	    String timestamp = Long.toString(System.currentTimeMillis()).trim();
	    String nonce = UUID.randomUUID().toString().trim();
	    String bodyHash = encode(payload, key);
	    // Create MAC input string
	    String macInput = timestamp + "\n" + nonce + "\n" + httpMethod + "\n" + resourceURI + "\n" + host + "\n"
		+ port + "\n" + bodyHash + "\n";
	    String encodedMacInput = encode(macInput, key);
	    String macRequestAuthFmt = "MAC id={0},ts={1},nonce={2},bodyhash={4},mac={3}";
	    String[] authHeaderInputs = new String[]{"\"" + macId + "\"", "\"" + timestamp + "\"", "\"" + nonce + "\"",
						     "\"" + encodedMacInput + "\"", "\"" + bodyHash + "\""};
	    signature = MessageFormat.format(macRequestAuthFmt, authHeaderInputs);
    	} catch (Exception e) {
	    System.err.println("Exception while generating MAC Signature: " + e.getMessage());
    	}
    	return signature;
    }

    private static String encode(String data, String key) throws Exception {
    	String encodedData = null;
    	try {
	    // get an HmacSHA256 signing- key from the raw key bytes
	    SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
	    // get an HmacSHA256Mac instance and initialize with the signing key
	    Mac mac = Mac.getInstance("HmacSHA256");
	    mac.init(signingKey);
	    // compute the hmac on input data bytes
	    byte[] rawHmac = mac.doFinal(data.getBytes());
	    // base64-encode the hmac
	    encodedData = new String(Base64.encodeBase64(rawHmac)).replace("\r\n", StringUtils.EMPTY);
    	} catch (Exception e) {
	    throw e;
    	}
    	return encodedData;
    }

I’ve basically tried a few different variants of this based on some posts I’ve seen here and SO as well as asking GPT4 and haven’t been able to get the server integration to work.

Here’s my basic function but I’m not completely sure how those Java libraries work or what they’re doing that’s different.

  defp encode(data, secret) do
    :crypto.mac(:hmac, :sha256, secret, data) |> Base.encode64
  end

Thanks for any assistance!

Most Liked

al2o3cr

al2o3cr

To clarify, I meant “good” and “bad” outputs for the same request. Those two don’t have the same bodyhash value… :thinking:

What we can spot from the “good” sample:

  • the output is expected in the standard base64 alphabet (with +, versus the “url-safe” one that substitutes -)
  • the output should include base64 padding (the trailing =s)

Together those mean that Elixir’s Base.encode64 will work with no additional options.

HOWEVER

The base64 flavor is the least of the problems. HMAC is explicitly designed to change a lot (and unpredictably) for even a single-bit difference in its input, so any mis-formatting before the HMAC will produce wrong values that don’t provide any clues to the problem.

This isn’t a “try it until it works” situation; the results of the failures aren’t going to give any feedback about why things are wrong. Can you post a link to the documentation for the specific API you’re trying to call?

As for troubleshooting the whole thing, you’ll want to capture the inputs & outputs of the pieces of generateMacSignature for comparison with the reimplementation:

  • what is the value of payload? Your Elixir code should be able to produce an identical bodyhash value for the same payload.
  • what is the exact value (newlines included!) of macInput? Again, your Elixir code needs to produce the same bytes given the same inputs
Hermanverschooten

Hermanverschooten

I would start by printing all intermediate values in both the original and the elixir code, arriving with what is passed to the base64 encoding, checking for differences along the way. eg what does the SecretKeySpec do in regards with just passing the key to :crypto.mac/4, etc.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127089 1222
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement