Generate digital signature with nodejs for Alipay API

812 views Asked by At

I am implenting ALIPAY API, to my project. In order to make some request I need to generate a signature with RSA private.pem - public.pem. The documentions is only showing JAVA exemple, but I need to implement it in NodeJS. So I try using crypto.sign but cannot get this to work.

Here is the JAVA code :

const g = (content, privateKey, charset) => {

    try {

        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));

        PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);

        Signature signature = Signature.getInstance("SHA256withRSA");

        signature.initSign(priKey);

        signature.update(content.getBytes(charset));

        byte[] signed = signature.sign();

        return Base64.getEncoder().encodeToString(signed);

    } catch (Exception e) {

        throw new RuntimeException("the key's format maybe not right");

    }

}

Can any one help to convert this into NodeJS code.

HERE IS MY WORKING SOLUTIONS:

/**
     * @param {*} // let parameters = new Map(); parameters.set("service", "alipay.overseas.secmerchant.maintain.queryStatus");
     */
    signRSA: async (parameters) => {


        let unnecessaryParams = Array.from(parameters.entries())
            .filter(a1 => a1[1] === undefined || a1[1].length === 0 || a1[1] === "null")
            .map(a1 => a1[0]);
        unnecessaryParams.forEach(a1 => parameters.delete(a1));

        let charset = ["_input_charset", "charset"].filter(a1 => parameters.has(a1)).map(a1 => parameters.get(a1))[0];
        charset = charset === undefined ? "UTF-8" : charset;

        //MD5,RSA,RSA2
        let signContent = Array.from(parameters.entries())
            .filter(a1 => {
                return a1[0] !== "sign" && a1[0] !== "sign_type";
            })
            .sort((a1, a2) => a1[0].localeCompare(a2[0]))
            .map(a1 => a1[0] + "=" + a1[1]).join("&");

        let key = fs.readFileSync('./services/alipay/rsa_private_key.pem');

        // let RSA_sign = r.sign_it(signContent, key, "RSA2")
        let RSA_sign = crypto.createSign('RSA-SHA256')

        RSA_sign.update(signContent)

        let s1 = RSA_sign.sign(key, "base64")
        parameters.set("sign", s1);

        return Array.from(parameters.entries()).map(a1 => a1[0] + "=" + encodeURIComponent(a1[1])).join("&");
    }

1

There are 1 answers

2
Terry Lennox On

I've created a Node.js version of this code, it should sign content using the provided private key.

I've also created a verifySignature function to check this, and created the equivalent code in Java (using your code as a template), just to ensure we're getting the same result in both languages.

Node.js

const crypto = require("crypto");

// Sample keys I've generated
const privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
const publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";

const content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";

const signature = signContent(content, base64KeyToPEM(privateKey, "PRIVATE"), "utf8");

console.log("Signature:", signature);
console.log("Verify signature:", verifySignature(content, base64KeyToPEM(publicKey, "PUBLIC"), signature, "utf8"));

function signContent(content, privateKey, encoding) {
    const sign = crypto.createSign("SHA256");
    sign.write(content, encoding);
    sign.end();
    return sign.sign(privateKey, "base64");
}

function verifySignature(content, publicKey, signature, encoding) {
    const verify = crypto.createVerify("SHA256");    
    verify.write(content, encoding);
    verify.end();
    return verify.verify(publicKey, Buffer.from(signature, "base64"));
}

function base64KeyToPEM(base64Key, keyType) {
    return [`-----BEGIN ${keyType} KEY-----`, ...splitStringIntoChunks(base64Key, 64), `-----END ${keyType} KEY-----`].join("\n");
}

function splitStringIntoChunks(input, chunkSize) {
    const chunkCount = Math.ceil(input.length / chunkSize)
    return Array.from( { length: chunkCount } ).map((v, chunkIndex) => input.substr(chunkIndex * chunkSize, chunkSize));
}

Java

import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;

public class DigitalSignature {


    public static void main(String[] args)
    {
        try {

            // Sample keys I've generated
            final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
            final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";

            final String content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";

            String signature = signContent(content, privateKey, "UTF-8");

            System.out.println("Signature: " + signature);
            System.out.println("verifySignature: " + verifySignature(signature, content, getPublicKeyFromBase64(publicKey), "UTF-8"));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    private static PublicKey getPublicKeyFromBase64(String publicKeyBase64) throws Exception {
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64)));
    }

    public static String signContent(String content, String privateKey, String charset) throws Exception
    {

            try {

                PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));

                PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);

                Signature signature = Signature.getInstance("SHA256withRSA");

                signature.initSign(priKey);

                signature.update(content.getBytes(charset));

                byte[] signed = signature.sign();

                return Base64.getEncoder().encodeToString(signed);

            } catch (Exception e) {
                throw new RuntimeException("signContent: Exception occurred: " + e.getMessage());
            }
        }

        public static Boolean verifySignature(String signatureBase64, String content, PublicKey publicKey, String charset) throws Exception {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(publicKey);
            signature.update(content.getBytes(charset));
            return signature.verify(Base64.getDecoder().decode(signatureBase64));
        }

}