Home > front end >  How to get same base64 hmac in java as in nodejs code?
How to get same base64 hmac in java as in nodejs code?

Time:04-30

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 :)

CodePudding user response:

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