coldfusion calculating HMAC256 of a getHTTPRequestData - java

I'm working with Shopify at the moment and using their webhook notifications so I can save stuff to our database.
Within their webhook headers, they provide a header of: X-Shopify-Hmac-Sha256
which is:
Each Webhook request includes a X-Shopify-Hmac-SHA256 header which is generated using the app's shared secret (looks like: '267bb1719a8e6ff75c4f2d709be0ca11'), along with the data sent in the request.
This is jolly wonderful; However, I'm really struggling to calculate the value of the X-Shopify-Hmac-Sha256.
I have a .cfm page which the webhook hits and I pass through the getHTTPRequestData to a function like thus:
<cfscript>
variables.stArgs = {};
variables.stArgs.stWebHookData = getHTTPRequestData();
application.stObj.stShopify.oShopifyWebHookBusiness.receiveWebHook(argumentCollection=variables.stArgs);
</cfscript>
I then stole some code from StackOverflow and Ben Nadel, but neither seem to end up giving me the value that I want. Currently I'm using Ben Nadels code like thus:
local.data = arguments.stWebHookData.toString();
local.macClass = createObject( "java", "javax.crypto.Mac" );
local.secretkeySpec = createObject( "java", "javax.crypto.spec.SecretKeySpec" ).init(toBinary(toBase64(arguments.key)),'HmacSHA256');
local.mac = local.macClass.getInstance('HmacSHA256');
local.mac.init(local.secretkeySpec );
local.hashedBytes = local.mac.doFinal(toBinary(toBase64(local.data)));
return lcase( binaryEncode( local.hashedBytes, "base64" ) );
(arguments.key is the shared secret)
Somewhere along the way, I am going wrong. Have I completely misunderstood what I am meant to be doing. This looks so easy in PHP.

So, getHTTPRequestData() returns a struct with a number of members. The one we're interested is content, which will be a byte array.
The MAC classes' doFinal() method expects an array of bytes (in our case the HTTP request's content) and returns an array of bytes (the HMac of the content)
The returned byte array needs to be base-64 encoded in order to compare it to the one Shopify puts in the webhook's headers. toBase64() will do that for us.
Putting it all together, you get this:
toBase64(local.mac.doFinal(getHTTPRequestData().content))

Related

Trying to serve image stored in GAE Datastore originally created by canvas.toDataURL()

I have a canvas painted by the user.
In the JavaScript I do:
var data = canvas.toDataURL().substr(22);
// snipped code that sets up xhr POST to "d/"
var params = "img=" + encodeURIComponent(data);
xhr.send(params);
I substr(22) to get rid of "data:image/png;base64,"
Then in app engine I do:
doodle.setProperty("img", new Text(req.getParameter("img")));
So I am setting the img property of the doodle Entity to the canvas.toDataURL().substr(22)
Now, when I want to retrieve the image, I do:
if (debugimg) {
resp.setContentType("text/plain");
resp.getWriter().print(((Text)groove.getProperty("img")).getValue());
}
else {
resp.setContentType("image/png;base64");
resp.getWriter().print(((Text)groove.getProperty("img")).getValue());
}
But for the life of me, the image never comes up.
Here is an example. I drew this, and can save it and render it in JavaScript.
https://yougotadoodle.appspot.com/d.jsp?id=1483002
If I use debugimg, this is what is being saved:
http://yougotadoodle.appspot.com/d?id=1483002&img=true&debugimg=true
But when I try to serve it with setContentType("image/png;base64") or even just "image/png" you get a broken picture:
http://yougotadoodle.appspot.com/d?id=1483002&img=true
I have tried a few different things, including not substr(22)ing it. Any ideas?
I tried using a Blob(), so storing it like this:
doodle.setProperty("img", new Blob(req.getParameter("img").getBytes()));
and reading it like this:
resp.getWriter().print(((Blob)groove.getProperty("img")).getBytes());
But that seemed to spit out somethign like this:
[B#1f11e0f
You have to decode this string before serving it as image/png because it is the Base64 encoded version.
I tested it locally in Python and your Hello SO! worked perfectly after decoding the given string. I'm not sure how to do it in Java but it should be fairly easy.
Here are three code snippets that have worked for me on the JS side (jpg) through the put to a blob property. May not be optimal, but it does work. HTH. -stevep
Create canvas render:
imgFinalData = canvas.toDataURL('image/jpg', 1.0);
Setup variable for POST to GAE:
f64 = imgFinalData.substr(imgFinalData.indexOf(',')+1).toString();
Post to GAE (fd is an array used to store mutiple POST vars):
fd.push('bytes=' + escape(f64));
//Here is the call with fd that handles the post:
postXmlHttpRequest(url, fd.join('&'), handlePostFinal);
One the GAE side (Python):
Property that stores POST data (line from entity class):
bytes = db.BlobProperty(required=True, indexed=False)
How the post data is processed b/4 put:
data = urllib.unquote(self.request.get('bytes'))
data = data.replace(' ','+')
bytes = base64.b64decode(data + '=' * (4 - len(data) % 4))
Property line inside the entity statement for put:
bytes = db.Blob(bytes),

Jersey Post request - How to perform a file upload with an unknown number of additional parameters?

I asked something like this previously, but upon re-reading my original post, it was not easy to understand what I was really asking. I have the following situation. We have (or at least I'm trying to get working) a custom file upload procedure that will take in the file, a set number of 'known' metadata values (and they will always be there), as well as potentially an unknown number of additional metadata values. The service that exists currently uses the Jersey framework (1.16)
I currently have both client and server code that handles dealing with the file upload portion and the known metadata values (server code below)
#POST
#Path("asset/{obfuscatedValue0}/")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public UUID uploadBlob(#PathParam("obfuscatedValue0") Integer obfuscatedValue0,
#FormDataParam("obfuscatedValue1") String obfuscatedValue1,
#FormDataParam("obfuscatedValue2") String obfuscatedValue2,
#FormDataParam("obfuscatedValue3") String obfuscatedValue3,
#FormDataParam("obfuscatedValue4") String obfuscatedValue4,
#FormDataParam("obfuscatedValue5") String obfuscatedValue5,
#FormDataParam("file") InputStream uploadedInputStream) {
.....
}
...and excerpt of client code:
Builder requestBuilder = _storageService
.path("asset")
.path(obfuscatedValue0.toString())
.type(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON);
FormDataMultiPart part = new FormDataMultiPart()
.field("file", is, MediaType.TEXT_PLAIN_TYPE) // 'is' is an inputstream from earlier in code.
.field("obfuscatedValue1", obfuscatedValue1)
.field("obfuscatedValue2", obfuscatedValue2)
.field("obfuscatedValue3", obfuscatedValue3)
.field("obfuscatedValue4", obfuscatedValue4)
.field("obfuscatedValue5", obfuscatedValue5);
storedAsset = requestBuilder.post(UUID.class, part);
However, I need to pass a map of additional parameters that will have an unknown number of values/names. From what I've seen, there is no easy way to do this using the FormDataParam annotation like my previous example.
Based upon various internet searches related to Jersey file uploads, I've attempted to convert it to use MultivaluedMap with the content type set to "application/x-www-form-urlencoded" so it resembles this:
#POST
#Path("asset/{value}/")
#Consumes("application/x-www-form-urlencoded")
public UUID uploadBlob(#PathParam(value), MultivaluedMap<String,String> formParams) {
....
}
It's my understanding that MultivaluedMap is intended to obtain a general map of form parameters (and as such, cannot play nicely together in the same method bearing #FormDataParam annotations.) If I can pass all this information from the Client inside some sort of map, I think I can figure out how to handle parsing the map to grab and 'doMagic()' on the data to get what I want done; I don't think I'll have a problem there.
What I AM fairly confused about is how to format the request client-side code when using this second method within the jersey framework. Can anyone provide some guidance for the situation, or some suggestions on how to proceed? I'm considering trying the solution proposed here and developing a custom xml adapter to deal with this situation, and sending xml instead of multipart-form-data but I'm still confused how this would interact with the InputStream value that will need to be passed. It appears the examples with MultivaluedMap that I've seen only deal with String data.

Url Routing with Encoding in Java Web API

I'm new to java and I'm trying to write a utility api to encrypt/decrypt a string. I have my controller and request mapping working, and I have the encrypt/decrypt methods working.
The problem I'm running into is decrypting a string that contains a forward slash ('/'). If I leave it unencoded in the url (such as http://localhost:8080/api/package/util/decrypt/oJfTtchpM9WC/4Oqpu7FZQ==) then url routing breaks (which is obvious why).
If I url encode it (such as http://localhost:8080/api/package/util/decrypt/oJfTtchpM9WC%2F4Oqpu7FZQ==) then the value in the path variable is empty.
Also note that http://localhost:8080/api/package/util/decrypt/oJfTtchpM9WC (no slash) works fine.
It seems whenever I put in a '%' into the path variable, I just get a blank page in response. Doesn't hit the method. Doesn't throw an exception (that I can find - again, I'm learning the environment still)
I've simplified my decrypt method to only output the path variable denoted as data
#RequestMapping(value = "/util/decrypt/{data}", method = RequestMethod.GET)
#ResponseBody
public String decrypt(HttpServletResponse httpResponse,
#PathVariable String data) throws Exception
{
return data;
// return URLDecoder.decode(data, "UTF-8");
}
Can anyone explain to me what is happening and how I can get this working?
Many thanks!!
I updated my encrypt method to use the function encodeBase64URLSafeString (vs just encodeBase64String) and also changed it to pass the data as a standard request param (ie ?data=string). This removed the problem.
Encodes binary data using a URL-safe variation of the base64 algorithm
but does not chunk the output. The url-safe variation emits - and _
instead of + and / characters. Note: no padding is added.

Implementing password digest for ws-security UsernameToken in Java

I am trying to make a call to a ws-security secured webservice from a server which unfortunately does not support this natively. The approach I have taken is to implement a .jsp which acts as reverse proxy to the actual end point URL, in the process adding the element with ws-security elements.
This seems to be working quite well and I am confident I've constructed the XML correctly with the correct namespaces etc. I've verified this by comparing the XML with XML produced by SOAP-UI.
The problem is in implementing the password digest generator. I don't get the same result as what SOAP-UI does using the same inputs for NOnce, xsd:dateTime and password, and the following code.
StringBuffer passwordDigestStr_ = new StringBuffer();
// First append the NOnce from the SOAP header
passwordDigestStr_.append(Base64.decode("PzlbwtWRpmFWjG0JRIRn7A=="));
// Then append the xsd:dateTime in UTC timezone
passwordDigestStr_.append("2012-06-09T18:41:03.640Z");
// Finally append the password/secret
passwordDigestStr_.append("password");
System.out.println("Generated password digest: " + new String(com.bea.xbean.util.Base64.encode(org.apache.commons.codec.digest.DigestUtils.sha(passwordDigestStr_.toString())), "UTF-8"));
I think the problem is with implementing the hashing of the first two elements as explained by http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
Note that the nonce is hashed using the octet sequence of its decoded value while the timestamp is hashed using the octet sequence of its UTF8 encoding as specified in the contents of the element.
If anyone could help me solve this problem that would be great because it's beginning to drive me crazy! It would be ideal if you could provide source code.
I'll take a crack at it without SOAP-UI. The input to the hash function is supposed to be bytes, not a string. DigestUtils.sha() will allow you to use a string, but that string must be properly encoded. When you wrote the nonce, you were calling StringBuffer.append(Object) which ends up calling byte[].toString(). That gives you something like [B#3e25a5, definitely not what you want. By using bytes everywhere, you should avoid this problem. Note that the example below uses org.apache.commons.codec.binary.Base64, not the Base64 class you were using. It doesn't matter, that's just the one I had handy.
ByteBuffer buf = ByteBuffer.allocate(1000);
buf.put(Base64.decodeBase64("PzlbwtWRpmFWjG0JRIRn7A=="));
buf.put("2012-06-09T18:41:03.640Z".getBytes("UTF-8"));
buf.put("password".getBytes("UTF-8"));
byte[] toHash = new byte[buf.position()];
buf.rewind();
buf.get(toHash);
byte[] hash = DigestUtils.sha(toHash);
System.out.println("Generated password digest: " + Base64.encodeBase64String(hash));
Apologies for the delay in replying, especially considering your initial quick response. I have now been able to get this to work using the essence of your approach to avoid any character encoding issues. However, java.nio.ByteBuffer caused me issues so I modified the code to use basic byte[]s which I combined using System.arrayCopy(). The problem I faced with java.nio.ByteBuffer was that despite 'buf.position()' returning an appropriate number of bytes, all the bytes injected into byte[] toHash through buf.get(toHash) were 0s!
Thanks very much for your assistance.

Altering a multipart/XXX content type without altering the underlying parts

I have an instance of MimeMessage which contains encrypted Parts.
The original content type is "multipart/encrypted; protocol="application/pgp-encrypted"; boundary="EncryptedBoundary12312345654654"
After decryption of each parts, I want the multipart header to change as:
"multipart/mixed; boundary="EncryptedBoundary12312345654654"
The boundary number is obviously dynamic, then I cannot just make
mime.setHeader("Content-Type", "multipart/mixed;" );
Do you have an idea about the best practice for that case?
I don't understand what you mean when you say you "want the multipart header to change".
Are you trying to decrypt the message "in place"? That's probably not going to work well.
You can create a new message using the decrypted contents of the original message.
If it's important to you that things like the "boundary" value remain the same,
you'll probably need to subclass MimeMultipart and use the ContentType class to
construct a new content type value.
I answer to publish the code of my solution:
// source is the encrypted MimeMessage
// MimeMessageWrapper is a wrapper which can copy a messgae but keep the message ID unchanged
boolean keepMessageId = true;
MimeMessageWrapper newMime = new MimeMessageWrapper(source, keepMessageId);
MimeMultipart mmp = new MimeMultipart("mixed");
List<MimePart> parts = MimeMultipartUtils.findPartsByMimeType(mime, "*");
for (MimePart part : parts) {
// Do some part processing
// Decrypt Adn verify individual parts
// End of processing
ContentType type = new ContentType(part.getContentType());
String encoding = part.getEncoding();
String name = type.getParameter("name");
part.setContent(new String(decPart.toByteArray()), type.toString());
// Add the part to the brand new MimeMultipart
mmp.addBodyPart((BodyPart) part);
}
// Set the original copy Message with the new modified content (decrypted parts)
mime.setContent(mmp);
mime.saveChanges();
In fact it seems there is no another way to alter the original message but create a copy was enough for me. The important point was just to create a new MimeMultipart object which will contains the decrypted parts and then given as the content to the MimeMessage(Wrapper). This will generate the new content type values "automagically".
For information, we did use a MimeMessageWrapper which is just a wrapper class that enable to keep the message ID unchanged (or not) to the copies. One possible implementation is on the Apache James project.
Another important point, finally in that solution, the underlying parts were changed but the boundary was adapted as well (it is not said EncryptedXXXX anymore) which is even cleaner for our case.

Categories