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

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(...)

Related

How do i get RSACryptoServiceProvider to verify a message using public key and signature

I generated a private and public key in javascript like this.
import crypto from "crypto";
/*export const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
modulusLength: 2048,
});*/
const pair = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
export const privateKey = pair.privateKey.export({
type: "pkcs1",
format: "pem",
});
export const publicKey = pair.publicKey.export({
type: "pkcs1",
format: "pem",
});
Then i use the private key to create a signature for a jsonfile like this, and the public key to verify it before i return the signature.
//Lav signatur
const signate = crypto.createSign("SHA384");
signate.update(Buffer.from(licenseRelationship, "utf-8"));
const signature = signate.sign(privateKey, "hex");
const verifier = crypto.createVerify("SHA384");
// verificer signature, besked
verifier.update(Buffer.from(licenseRelationship, "utf-8"));
const verificationResult = verifier.verify(publicKey, signature, "hex");
This works perfectly, and then i return the json and the signature as a http response.
I recieve it in c# code and store the two components so im can use them later on request.
Upon request i fetch the two components and want to use the signature to check if the json has been tampered with.
I also has the public key in this code.
I do it like this.
string licenseRelationshipJson = licenseRelationshipDAO.getLicenseRelationshipWithoutSignatureAsJson(licenseRelationship);
byte[] signature = Encoding.UTF8.GetBytes(licenseRelationship.signature);
byte[] licenseRelationshipJsonAsArray = Encoding.UTF8.GetBytes(licenseRelationshipJson);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
result = rsa.VerifyData(licenseRelationshipJsonAsArray, signature,
HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
if (result)
{
log.write("Message verified ", null);
} else
{
log.write("Message not Verified ", null);
}
All debug code and exception handling removed.
I'm a crypto virgin, and am trying to understand this. But i must have misunderstood something serious.
I have the public key as a string (not base64 encoded)
Ive checked the json, and it is the exact same bytes when signed in Javascript as when being verified in c#
The public key is not used in this process. That has to be wrong i think ?
How do i get the public key into the RWACryptoServiceProvider ?
Im sure im using RWACryptoServiceProvider wrong.
EDIT....:
Ive tried this instead, but still to no avail.
string licenseRelationshipJson = licenseRelationshipDAO.getLicenseRelationshipWithoutSignatureAsJson(licenseRelationship);
byte[] signature = Encoding.UTF8.GetBytes(licenseRelationship.signature);
byte[] licenseRelationshipJsonAsArray = Encoding.UTF8.GetBytes(licenseRelationshipJson);
byte[] asBytes = Encoding.ASCII.GetBytes(DataStorage.Instance.PUBLIC_KEY);
char[] publicKeyAsArray = Encoding.ASCII.GetChars(asBytes);
ReadOnlySpan<char> publicKeyChars = publicKeyAsArray;
RSA rsa = RSA.Create();
try
{
rsa.ImportFromPem(publicKeyChars);
result = rsa.VerifyData(licenseRelationshipJsonAsArray, signature, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
} catch (CryptographicException cex)
{
log.write("Something went wrong with the crypto verification process", cex);
}
.
.
.
Thankyou for your time.

Using JavaScipt for calculating HMAC-SHA256 signature

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.

HMAC256 from C# to Javascript returns different results

I want to create a HMAC256 key from a string with a HMAC key based on my C# project in Javascript. however, each project has different results and can't seem to find a way to make the results identical.
C# PROJECT
private string CalculateHMAC(string hmacKey, string signingstring) {
byte[] key = PackH(hmacKey) //returns 32 bit array;
byte[] data = Encoding.UTF8.GetBytes(signingstring);
try {
using(HMACSHA256 hmac = new HMACSHA256(key)) {
// Compute the hmac on input data bytes
byte[] rawHmac = hmac.ComputeHash(data);
// Base64-encode the hmac
return Convert.ToBase64String(rawHmac);
}
} catch (Exception e) {
throw new Exception("Failed to generate HMAC : " + e.Message);
}
}
JAVASCRIPT CODE
var hash = CryptoJS.HmacSHA256(byteString, hmacKeyinString);
var msg = hash.toString(CryptoJS.enc.Base64);
Thank you in advance.
Using CryptoJS in my javascript project
fixed with this line of code
var wordsKey = CryptoJS.enc.Hex.parse('yourkey');
var hash = CryptoJS.HmacSHA256(convertString, wordsKey);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);

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