4
$\begingroup$

I am trying to recreate a Java algorithm in Mathematica. It is taking a key and signing a message using the HMAC and SHA256 algorithm.

The Java code for which I am basing it off is here

I have dragged the apache commons 1.11 jar to (using a mac) /Applications/Mathematica.app/Contents/SystemFiles/Converters/Java/

Needs["JLink`"]
InstallJava[];

Mac = LoadJavaClass["javax.crypto.Mac"];
secretKeySpec = LoadJavaClass["javax.crypto.spec.SecretKeySpec"];
base64 = LoadJavaClass["org.apache.commons.codec.binary.Base64"];

key = "DDDD20942134FBB95AAF88198F260E06";
message = 
  "gethttps://api.gtcprojects.com/trade/orders1507882361.6882524";

keyJava = JavaNew["java.lang.String", key]
messageJava = JavaNew["java.lang.String", message]

Which gives

« JavaObject[java.lang.String]»
« JavaObject[java.lang.String]»

keyBytes = keyJava@getBytes[]
messageBytes = messageJava@getBytes[]

Which gives

{68, 68, 68, 68, 50, 48, 57, 52, 50, 49, 51, 52, 70, 66, 66, 57, 53, 65, 65, 70, 56, 56, 49, 57, 56, 70, 50, 54, 48, 69, 48, 54}

{103, 101, 116, 104, 116, 116, 112, 115, 58, 47, 47, 97, 112, 105, 46, 103, 116, 99, 112, 114, 111, 106, 101, 99, 116, 115, 46, 99, 111, 109, 47, 116, 114, 97, 100, 101, 47, 111, 114, 100, 101, 114, 115, 49, 53, 48, 55, 56, 56, 50, 51, 54, 49, 46, 54, 56, 56, 50, 53, 50, 52}

signingKey = JavaNew[secretKeySpec, keyBytes, "HmacSHA256"]

Which gives

« JavaObject[javax.crypto.spec.SecretKeySpec]»

mac = Mac`getInstance["HmacSHA256"]

Which gives

« JavaObject[javax.crypto.Mac]»

mac@init[signingKey]
test = mac@doFinal[messageBytes]

Which gives

{47, 105, 21, 95, -108, -119, 124, -33, -39, -98, 79, -98, 4, 87, 44, 88, 67, -53, 96, -1, 120, 91, 89, 114, -72, 23, -11, -115, 109, -24, -25, -39}

It appears everything is going well until I need to base64 encode it.

base64`encodeBase64String[test]

Which gives:

base64`encodeBase64String[{47, 105, 21, 95, -108, -119, 124, -33, -39, -98, 79, -98, 4, 87, 44, 88, 67, -53, 96, -1, 120, 91, 89, 114, -72, 23, -11, -115, 109, -24, -25, -39}]

It is also a little scary that there is negative bytes (CharacterCodes in Mathematica) in the results of the HMAC.

The correct answer is (confirmed by python)

L2kVX5SJfN/Znk+eBFcsWEPLYP94W1lyuBf1jW3o59k=
$\endgroup$
  • $\begingroup$ Does it work if you use Base64`encodeBase64String[...] (starting with a capital B)? $\endgroup$ – WReach Nov 2 '17 at 22:02
  • 1
    $\begingroup$ Could also use Mathematica's Developer`EncodeBase64[test] $\endgroup$ – ilian Nov 3 '17 at 3:25
  • 1
    $\begingroup$ @WReach that did it! ilian that also worked! Maybe I am just new to JLink and wondering why when I assigned base64 to the class it didn't call that class but needed to use the Java class name Base64. Interesting. Thank you for the help. If you put either of these in as an answer, I will accept. If not, I will answer so people see that there was a solution to the problem. $\endgroup$ – Ray Troy Nov 3 '17 at 12:27
3
$\begingroup$

Use Base64`encodeBase64String -- with a capital "B".

The expression:

base64 = LoadJavaClass["org.apache.commons.codec.binary.Base64"];

returns a JavaClass object which is being stored into the variable base64. As a side-effect, it also creates symbols which can access the static methods on the class. By default, those symbols are placed into a context that has the same name as the Java class. In this case, Base64`encodeBase64String is one of those static methods. The JavaClass object stored into the variable base64 has various uses, but static methods are not normally invoked through it.

As ilian notes in a comment, Mathematica has built-in Base64 functionality. Developer`EncodeBase64 is applicable in the present case.

$\endgroup$
3
$\begingroup$

The recently released version 11.3.0 introduced BaseEncode which would be useful here.

A complete example (including the HMAC computation) in Mathematica:

hmacBase64[method_, message_, key_] := 
 Module[{dkey, opad, ipad, blocksize}, 
  blocksize = If[method === "SHA384" || method === "SHA512", 128, 64];
  dkey = StringToByteArray[key];
  If[Length[dkey] > blocksize, dkey = Hash[dkey, method, "ByteArray"]];
  dkey = Normal[dkey];
  If[Length[dkey] < blocksize, dkey = PadRight[dkey, blocksize, 0]];
  {opad, ipad} = ByteArray[BitXor[dkey, ConstantArray[#, blocksize]]] & /@ {92, 54};
  BaseEncode[Hash[Join[opad, Hash[Join[ipad, StringToByteArray[message]], method, 
      "ByteArray"]], method, "ByteArray"]]
]

hmacBase64["SHA256","gethttps://api.gtcprojects.com/trade/orders1507882361.6882524", \
"DDDD20942134FBB95AAF88198F260E06"]

(* "L2kVX5SJfN/Znk+eBFcsWEPLYP94W1lyuBf1jW3o59k=" *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.