0

I am working on translating an API from Java into Javascript (nodeJs). The problem is that the signatures generated by the Java code are much shorter than the one in javascript. The results from the getSignature function have different length and as such whenever I generate a signature in javascript the server won't recognize it but it will when it is generated in Java.

I have verified that the values in getSignatureKey are the same in both functions and the getSignature function uses the output from getSignatureKey to encrypt "SOME MESSAGE TO ENCRYPT" which will be the request body in plain text (verified both have the same content and format).

Is there any reason why the output differs in length? Perhaps some encoding problem or something else I'm not seeing.

Using the native crypto library in nodeJs as follows:

var getSignatureKey = function(key, api_key, dateStamp){
    let kUser = HMACSHA256("CAS"+api_key, key);
    let kDate = HMACSHA256(dateStamp, kUser);
    let kService = HMACSHA256(SERVICE_NAME, kDate);
    let kSigning = HMACSHA256("cas_request", kService);
    return kSigning;
}

var getSignature = function(signature_key){
    let signature_bytes = HMACSHA256("SOME MESSAGE TO ENCRYPT", signature_key);
    let signature = Buffer.from(signature_bytes).toString('base64');
    return signature;
}

var HMACSHA256 = function(message, secret){
    let key_bytes = encoder.encode(secret);
    let message_bytes = encoder.encode(message);
    let hash = crypto.createHmac('sha256', key_bytes).update(message_bytes).digest();
    return Uint8Array.from(hash);
}

While in java I have the following code:

public static byte[] getSignatureKey(String key, String apiKey, String dateStamp, String serviceName)
        throws Exception {
    byte[] kSecret = key.getBytes("UTF8");
    byte[] kUser = HmacSHA256("CAS" + apiKey, kSecret);
    byte[] kDate = HmacSHA256(dateStamp, kUser);
    byte[] kService = HmacSHA256(serviceName, kDate);
    byte[] kSigning = HmacSHA256("cas_request", kService);
    return kSigning;
}

public static String getSignature(byte[] signature_key) throws Exception {
    return Base64.encodeBase64String(HmacSHA256("SOME MESSAGE TO ENCRYPT", signature_key));
}

public static byte[] HmacSHA256(String data, byte[] key) throws Exception {
    String algorithm = "HmacSHA256";
    Mac mac = Mac.getInstance(algorithm);
    mac.init(new SecretKeySpec(key, algorithm));
    return mac.doFinal(data.getBytes("UTF8"));
}
  • Replace in the NodeJS code in the `createHmac` call of the `HMACSHA256` function the variable `key_bytes` with `secret`. Furthermore it should be enough to return only `hash` instead of `Uint8Array.from(hash)`, although the latter is not critical. – Topaco Jul 14 '20 at 18:25
  • @Topaco Generally just `digest()` will return a `Buffer` instance containing the binary data. Possibly that buffer is not interpreted as bytes. You certainly don't wan't to interpret the buffer contents as UTF-8. – Maarten Bodewes Jul 14 '20 at 19:34
  • If anything try `binary` or `Latin1`. Latin 1 is a 8 bit character encoding, which generally means it keeps the byte values. – Maarten Bodewes Jul 14 '20 at 20:38
  • @MaartenBodewes - I don't actually see where the buffer (`hash`) could be interpreted as UTF8. The line `let key_bytes = encoder.encode(secret);` has no more effect with the proposed change: Thus, there is no encoding involved, neither for a key passed to `HMACSHA256` as a buffer, nor for the buffer returned by `HMACSHA256` (which again is directly passed as a key to `HMACSHA256` in a subsequent call). A UTF8 encoding is performed (by default) when the key is passed as a string (possibly at the first call in `getSignatureKey`) and for `message` (via `TextEncoder`) but both aren't critical. – Topaco Jul 14 '20 at 23:45
  • Still, we have the "Is there any reason why the output differs in length" to deal with. That's could be just the interpretation of the bytes on the screen though. Hylandude, can you share the hashes you get when performing the methods? What do you see? – Maarten Bodewes Jul 15 '20 at 10:07
  • The different lengths are most likely caused by the line `let key_bytes = encoder.encode(secret);` which UTF8 encodes the buffer (containing arbitrary binary data). As a result, values that are undefined for UTF8 (i.e. values larger than 0x7F) are replaced by the 3 byte UTF8 replacement character `0xefbfbd` (codepoint `U+FFFD`), which increases the data volume. – Topaco Jul 15 '20 at 11:13

0 Answers0