Search code examples
javascriptjavadigital-signaturehmaccryptojs

How to make a Base64 HmacSHA256 signature of bytes payload in JavaScript equivalent to Java?


In Java to make a signature of some data we are using Mac instance which allows to sign any byte array. How to make a function in JavaScript which produces the same signature for the same byte array?

An example of Java implementation (method sign signs message with HmacSHA256 and than converts signature into url-safe base64 string):

public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {

    byte[] secret = new byte[5];
    secret[0] = 0x1e;
    secret[1] = 0x03;
    secret[2] = 0x01;
    secret[3] = 0x02;
    secret[4] = 0x03;

    byte[] message = new byte[5];
    message[0] = 0x01;
    message[1] = 0x03;
    message[2] = 0x02;
    message[3] = 0x1e;
    message[4] = 0x03;

    System.out.println(sign(secret, message));
}

private static String sign(byte[] secret, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {

    Mac sha256Hmac = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKey = new SecretKeySpec(secret, "HmacSHA256");
    sha256Hmac.init(secretKey);

    byte[] signature = sha256Hmac.doFinal(message);
    return Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
}

The example above produces q-l6FioFNkAqMIIxX5rs3AF-VnGIzpApCSSDHmnmjF8 signature string. I am trying to create the equivalent of sign method in JavaScript to get the same signature.

function main(){
    var secret = [5];
    secret[0] = 0x1e;
    secret[1] = 0x03;
    secret[2] = 0x01;
    secret[3] = 0x02;
    secret[4] = 0x03;

    var message = [5];
    message[0] = 0x01;
    message[1] = 0x03;
    message[2] = 0x02;
    message[3] = 0x1e;
    message[4] = 0x03;

    console.log(sign(secret, message));
}

function sign(secret, message){

    // ?

}

I couldn't find a way to sign bytes with CryptoJS.


Solution

  • The solution appeared to be not complicated. Before using CryptoJS we have to correctly convert bytes array into a String. After returning base64 string we should escape it to url friendly string.

    function sign(secret, message){
    
        var secretString = String.fromCharCode.apply(String, secret);
        var messageString = String.fromCharCode.apply(String, message);
    
        var hash = CryptoJS.HmacSHA256(messageString, secretString);
        return CryptoJS.enc.Base64.stringify(hash).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
    }