Using JavaScipt for calculating HMAC-SHA256 signature - javascript

I need to calculate the HMAC-sha256 signature in JavaScript. I am using the following code.
crypto.createHmac('sha256','abc123').update('{"video-id":"212zpS6bjN77eixPUMUEjR", "exp-time": 1458396066}').digest('hex');
console.log( '1458396066' + '~'+ res);
The resulting hash I get is: 1458396066~d87d121117b46dc28ffec1117cd44cb114b32c1d7bfe5db30ebee7cb89221d3e
This is not the hash that I am expecting. I have implemented code in PHP and Java which seems to work fine.
PHP Code
<?php
$videoId = "212zpS6bjN77eixPUMUEjR";
$sharedSecret = "abc123";
function generateToken($videoId, $sharedSecret, $lifeTime)
{
$expiryTime = "1458396066";
$data = sprintf("{\"video-id\":\"%s\", \"exp-time\": %s}" , $videoId, "1458396066");
$hash = hash_hmac ( "sha256", $data , hex2bin($sharedSecret) );
$token = sprintf ("%s~%s","1458396066" , $hash);
return $token;
}
$token = generateToken($videoId, $sharedSecret, 5);
echo $token;
?>
JAVA Code
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.math.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class VMProToken {
public static void main(String[] args) {
final String videoID = "212zpS6bjN77eixPUMUEjR";
final String sharedSecret = "abc123";
try {
final String token = generateToken(videoID, sharedSecret);
System.out.println(token);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
private static String generateToken(String videoId, String sharedSecret)
throws NoSuchAlgorithmException, InvalidKeyException {
final String HASH_PATTERN = "{\"video-id\":\"%s\", \"exp-time\": %s}";
final String HASH_ALGORITHM = "HmacSHA256";
final String tokenCalcBase = String.format(HASH_PATTERN, videoId, 1458396066);
System.out.println(tokenCalcBase);
final Mac hmac = Mac.getInstance(HASH_ALGORITHM);
final byte[] keyBytes = DatatypeConverter.parseHexBinary(sharedSecret);
final SecretKeySpec secretKey = new SecretKeySpec(keyBytes, HASH_ALGORITHM);
hmac.init(secretKey);
final byte[] hmacBytes = hmac.doFinal(tokenCalcBase.getBytes());
System.out.println(String.format("%064x", new BigInteger(1, hmacBytes)));
final String hash = String.format("%064x", new BigInteger(1, hmacBytes));
return 1458396066 + "~" + hash;
}
}
The above two codes result in the correct answer which is
1458396066~62dcbe0e20827245454280c51129a9f30d1122eaeafc5ce88f0fec527631f1b5
Can somebody please let me know what I'm doing wrong here?

The key is processed as a hexadecimal encoded string in the PHP and Java code, but not in the NodeJS code. To do the same in the NodeJS code, replace 'abc123' with Buffer.from('abc123', 'hex') in the createHmac call.

Related

Android Capacitor JS Plugin does not reply

I am working on a java plugin that is supposed to recieve some info from a js vue3 program and then do a URL post operation, and then return some of the info found back to the js code. I am using capacitor and android. This is my error message:
2022-08-22 13:46:23.773 27544-27544/org.theguy.GptEtc E/Capacitor/Console: File: http://localhost/js/app.6577adf2.js - Line 1 - Msg: Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1
I think this means that something other than valid JSON is being delivered to the js code. I know that the app is delivering info to the java android class. This is some of my java code.
#CapacitorPlugin(name = "URLPOST")
public class PluginURLPost extends Plugin {
#PluginMethod()
public void post(PluginCall call) {
String post_url = call.getString("post_url", "");
String bearer = call.getString("bearer", "pipeline_");
JSObject ret = new JSObject();
try {
String value = this.doPost(post_url, bearer);
System.out.println("value " + value);
Gson gson = new Gson();
JsonReader reader = new JsonReader(new StringReader(value));
ResultPreview preview = gson.fromJson(reader, ResultPreview.class);
String val = preview.getResult_preview()[0][0];
val = "result string here."; // <-- add this for easy testing
ret.put("response_text", val.replace("\n", "\\n"));
System.out.println("response here: " + val);
}
catch (Exception e) {
e.printStackTrace();
}
//call.setKeepAlive(true);
call.resolve(ret);
}
OkHttpClient client = new OkHttpClient();
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
String doPost(String post_url, String bearer ) throws IOException {
// ... do some post request here ...
return response_body;
}
}
class ResultPreview {
#SerializedName("result_preview")
String [][] result_preview ;
public void setResult_preview(String[][] result) {
this.result_preview = result;
}
public String[][] getResult_preview() {
return this.result_preview;
}
}
This is some of my js code.
import { registerPlugin } from "#capacitor/core";
const URLPOST = registerPlugin("URLPOST");
const request = {
"line": line,
"pipeline_model": details[engine]["app_model"].trim(),
"bearer": details[engine]["api_key"].trim(),
"post_url": details[engine]["url"].trim(),
"length": 25,
"top_k": 50
};
console.log("request", request);
var {response_text} = await URLPOST.post(request);
console.log("response_text 1",response_text);
I don't know what to do.
I tried this, and things work better. I don't know if this is the ultimate solution.
#PluginMethod()
public void post(PluginCall call) {
bridge.saveCall(call); // <-- add this
call.release(bridge); // <-- add this
String pipeline_model = call.getString("pipeline_model", "pipeline_");
String post_url = call.getString("post_url", "");
JSObject ret = new JSObject();
try {
String value = this.doPost(post_url);
Gson gson = new Gson();
JsonReader reader = new JsonReader(new StringReader(value));
ResultPreview preview = gson.fromJson(reader, ResultPreview.class);
String val = preview.getResult_preview()[0][0];
ret.put("response_text", val.replace("\n", "\\n"));
System.out.println("response here: " + val);
}
catch (Exception e) {
e.printStackTrace();
}
call.resolve(ret);
}
This is not found on the capacitor site, but instead I found it digging around the internet.

How to get same base64 hmac in java as in nodejs code?

I am trying to create a base64 hmac for sha256. I have two codes for the same, one in JS and other in Java,though I am doing it in android and a kotlin one will help me as well. I have mostly used codes from other SO answers only. The one in node js seems to give correct results and matches with the backend but in java is does not. Here are the codes
const crypto = require('crypto')
const base64urlm = require('base64url')
console.log('hello')
var yourMessage = 'Pritish8-s9';
var sharedSecret = 'Nilesh/ev12/';
//generate hmac sha256 hash
var hmacSignature = crypto.createHmac('SHA256', new Buffer(sharedSecret, 'base64')).update(yourMessage).digest('base64');
hmacSignature = base64urlm.fromBase64(hmacSignature)
console.log(hmacSignature)
It gives the output as
_eiq1peyHuPx8yQwzORwoT7wcNdzv2Y0LUp_E70aIvM
The above is the correct value. Now following is the java code.
package com.drivertest.hmactest;
import android.util.Log;
import org.apache.commons.codec.binary.Hex;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HMAC {
public static String cal() {
try {
String secret = "Nilesh/ev12/";
String message = "Pritish8-s9";
byte[] secretByteArray = new byte[0];
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
secretByteArray = Base64.getEncoder().encode(secret.getBytes());
}
//byte[] secretByteArray = Base64.encodeBase64(secret.getBytes("utf-8"), true);
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secretByteArray, "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
hash = Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(message.getBytes()));
}
System.out.println("hash "+hash);
Log.d("++++",hash);
return hash;
}
catch (Exception e){
System.out.println("Error");
}
return "";
}
public static String encode(String key, String data) {
try {
String secret = "Nilesh/ev12/";
String message = "Pritish8-s9";
key=secret;
data=message;
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256_HMAC.init(secret_key);
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
String hash = android.util.Base64.encodeToString(sha256_HMAC.doFinal(message.getBytes()), android.util.Base64.DEFAULT);
Log.d("++",hash);
return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
}
It is a class where I have attempted to do it in different ways with other SO answers. Unfortunately I get a value like
8i/ce0u0GZ+JhL3yblsGhaMnFC0UKkUwJSQSXZ3536s=
or
f22fdc7b4bb4199f8984bdf26e5b0685a327142d142a45302524125d9df9dfab
So can anyone help me in writing java/kotlin code for the same and get the same value like the above in nodejs ?
PS : I have verified the java results on random sites and they seem to match, but my api is failing with this value , and will only work if it can match with that os nodejs, so it is incorrect in that sense.
Thank you :)
There are two differences between your nodejs and Java implementations.
First and the most important: in nodejs you decode your secret using base64, while in Java you encode it:
Base64.getEncoder().encode(secret.getBytes())
Replace it with:
Base64.getDecoder().decode(secret.getBytes())
Second, in nodejs you use URL variant of base64 (base64urlm) when encoding the final result. In Java you use a regular base64. Replace it with:
Base64.getUrlEncoder().encodeToString(...)

Generating signin KEY in Java for Post request

I need to send a POST request to api.example.com. I need to generate singin KEY to send a POST request. The KEY must be generated by the following formula
HEX (HMAC-SHA384({apiPath} + {nonce} + JSON({body}),{secretKey}))
I've found a sample of Javascript code how to genereate signing key
javascript
const crypto = require('crypto');
const publicKey = '';
const secretKey = '';
const apiPath = '/v3/auth/kuna_codes/issued-by-me';
const nonce = new Date().getTime();
const body = {};
const signatureString = `${apiPath}${nonce}${JSON.stringify(body)}`;
const signature = crypto
.createHmac('sha384', secretKey)
.update(signatureString)
.digest('hex');
console.log(signature);
I need a Java code that generates a signing key by the formula provided above
Something like this
package com.vorontsov.tbe.main;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
public class KunaSignatureGenerator {
private static final String ALGORITHM = "HmacSHA384";
private static final String API_PATH = "/v3/auth/kuna_codes/issued-by-me";
private static final String SECRET_KEY = "secret";
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, JsonProcessingException {
var signature = new KunaSignatureGenerator().generateSignature(new RequestParams("stringVal"));
System.out.println(signature);
}
private String generateSignature(RequestParams requestParams) throws NoSuchAlgorithmException, InvalidKeyException, JsonProcessingException {
var nonce = "" + System.currentTimeMillis();
var body = new ObjectMapper().writeValueAsString(requestParams);
final String signatureString = API_PATH + nonce + body;
SecretKeySpec signingKey = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
final Mac mac = Mac.getInstance(ALGORITHM);
mac.init(signingKey);
return HexFormat.of().formatHex(mac.doFinal(signatureString.getBytes()));
}
private static record RequestParams(#JsonProperty("stringParam") String stringParams) {
}
}

Crypto.js SHA1 vs MessageDigest java SHA1: why different results?

I need to encrypt a string. The initial version was written in Java, but it needs to be rewritten in javascript now. But i have different results.
Here is the code in java:
Java Version:
private static String getEncrypt(String input, String salt) throws NoSuchAlgorithmException {
byte[] raw_salt = Base64.getDecoder().decode(salt);
byte[] raw_data = input.getBytes(StandardCharsets.UTF_8);
byte[] test_data = new byte[raw_salt.length + raw_data.length];
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
System.arraycopy(raw_salt, 0, test_data, 0, raw_salt.length);
System.arraycopy(raw_data, 0, test_data, raw_salt.length, raw_data.length);
mdTemp.update(test_data);
byte[] bytes = mdTemp.digest();
return new String(Base64.getEncoder().encode(bytes));
}
input: 123456789
salt: pMm6kWsoWjR18sWKnoG4Az==
output: 6u/VAXS9ZKmLEbHw/OZ1AVarth4=
JS Version: (use crypto.js)
const crypto = require("crypto");
function getEncrypt(input, salt){
const sha1 = crypto.createHash('sha1');
const beforeCrypto = salt + input;
const afterCrypto = sha1.update(beforeCrypto).digest('base64');
return afterCrypto;
}
input: 123456789
salt: pMm6kWsoWjR18sWKnoG4Az==
output: ie/3j+92nxvcNT5i+3WUJbWsEAg=
The MessageDigest method in java requires me to enter input in byte[] format. While in javascript, I use ·string· type input.
They also use salt to make the encryption more safe, but it brings more different that i cannot rewrite it in javascript.
I try many ways to solve it. But I still cannot get the same result.
The following snippet also gives me the correct answer:
98O8HYCOBHMq32eZZczDTKeuNEE=
There must be some detail in your code that is different.
One thing I did was to use the UTF8 standard charset, to avoid any mishaps with the way "utf-8", vs. "UTF8" etc is specified.
Your code does not compile (missing return type on getEncrypt, for example) so there might be something else that is different.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Main {
private static String getEncrypt(String input) throws NoSuchAlgorithmException {
byte[] raw_data = input.getBytes(StandardCharsets.UTF_8);
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(raw_data);
byte[] bytes = mdTemp.digest();
return new String(Base64.getEncoder().encode(bytes));
}
public static void main(String[] args){
try {
System.out.println(getEncrypt("123456789"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}

Generate HMAC SHA Algorithm using URI and Key

I wrote a Java program which generates HMAC SHA hash code, But due to some reason I have to write the same code in NodeJs/JavaScript. I tried googling around but did not get anything. In this Java code, I am passing URI and Key as arguments, to generate the hash code, where URI contains Timestamp.
The java code is as :
public static String calcMAC(String data, byte[] key) throws Exception {
String result=null;
SecretKeySpec signKey = new SecretKeySpec(key, SecurityConstants.HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(SecurityConstants.HMAC_SHA1_ALGORITHM);
mac.init(signKey);
byte[] rawHmac;
try {
rawHmac = mac.doFinal(data.getBytes("US-ASCII"));
result = Base64.encodeBase64String(rawHmac);
} catch (Exception e) {
e.printStackTrace();
}
return result.trim();
}
public static void main(String args[]) {
String timestamp = args[0];
String key = "d134hjeefcgkahvg32ajkdbaff84ff180";
String out = null;
try {
out = calcMAC("/req?app_id=47ca34" + timestamp + "=2018-05-22T12:02:15Z",
key.getBytes());
System.out.println(URLEncoder.encode(out, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
Is it possible to achieve the same goal in NodeJs/JavaScript?
Note:: I have to call this script from Postman pre-request script.
The crypto module should do this for you, you can substitute the 'data' variable with whatever you want to hash:
const crypto = require('crypto');
const data = 'The fault dear Brutus lies not in our stars';
const key = Buffer.from('d134hjeefcgkahvg32ajkdbaff84ff180', 'utf8');
const hash = crypto.createHmac('sha1', key).update(data).digest('base64');
const uriEncodedHash = encodeURIComponent(hash);
console.log('Hash: ' + uriEncodedHash);
Hashing the data in both Java and Node.js gives me the result (URI Encoded) of:
TJJ3xj93m8bfVpGoucluMQqkB0o%3D
The same Java code would be:
public static void main(String args[]) {
String data = "The fault dear Brutus lies not in our stars";
String key = "d134hjeefcgkahvg32ajkdbaff84ff180";
String out = null;
try {
out = calcMAC(data, key.getBytes());
System.out.println(URLEncoder.encode(out, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
Again, we can put anything into 'data' we want.

Categories

Resources