API Signature Generate

# Unified signature generation rules

Only the parameter value participates in the signature, the Key (parameter name) is only sorted and does not participate in the signature calculation

  1. Sort the Key (parameter name) of all non-empty parameters according to ASCII
  2. According to the order of Key (parameter name), take the parameter values one by one for parallel splicing, and use the RSA algorithm to calculate the string to calculate the signature string

# Example description

  • Example Parameters
Parameters Example
merchantCode S820211021094748000001
method BCA
orderNum T1642592278863
payMoney 10000
productDetail Test Pay
notifyUrl your notify url
dateTime 20221202125813
name JackMa
expiryPeriod 1440
email [email protected]
phone 082122965511
  • Splicing strings
  1. First sort the Key (parameter name) according to ASCII
  2. Then sort the values according to Key (parameter name) and concatenate them into a string.

strA = 20221202125813test@email.com1440S820211021094748000001BCAJackMayour notify urlT164259227886310000082122965511Test Pay

  • Calculate Signature
  1. Use the key pair you configured in TopPay Merchant Backend
  2. Use your PrivateKey to perform encryption calculation RSA(StrA) to obtain the final signature string

Sign = IMLn23c4orM+7pZhHoRmbjrol4X33jeAqFxbZuQ+pnznBIGhb6 ...

# Code Example

import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * TopPay RSA signature tool class
 * 
 * Author: TopPay
 */
public class TopPayRequestUtil {

  /**
   * VERIFY SIGNATURE
   * 
   * @param params
   * @return
   */
  public static boolean verifySign(JsonObject params, String publickey) throws InvalidKeySpecException, NoSuchAlgorithmException {
    String platSign = params.remove("platSign").getAsString(); // sign
    List<String> paramNameList = new ArrayList<>(params.keySet());
    Collections.sort(paramNameList);

    StringBuilder stringBuilder = new StringBuilder();
    for (String name : paramNameList) {
      stringBuilder.append(params.get(name).getAsString());
    }
    System.out.println("keys:" + stringBuilder);

    String decryptSign = publicDecrypt(platSign, getPublicKey(publickey));
    System.out.println("decryptSign:" + decryptSign);
    
    return stringBuilder.toString().equals(decryptSign);
  }

  /**
   * PRIVATE KEY ENCRYPTION
   * 
   * @param data
   * @param privateKey
   * @return
   */
  public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
    try {
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.ENCRYPT_MODE, privateKey);
      return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
    } catch (Exception e) {
      throw new RuntimeException("ENCRYPTED STRING[" + data + "]Encountered an exception", e);
    }
  }

  /**
   * PUBLIC KEY DECRYPTION
   * 
   * @param data
   * @param publicKey
   * @return
   */
  public static String publicDecrypt(String data, RSAPublicKey publicKey) {
    try {
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.DECRYPT_MODE, publicKey);
      return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()),"UTF-8");
    } catch (Exception e) {
      throw new RuntimeException("When attempting to encrypt the string [" + data + "], an exception occurred.", e);
    }
  }

  /**
   * GET PRIVATE KEY
   * 
   * @param privateKey KEY STRING (BASE 64 ENCODED)
   */
  public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    // Obtain the private key object through the Key command encoded by PKCS 8
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
    RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
    return key;
  }

  /**
   * GET PUBLIC KEY
   * 
   * @param publicKey KEY STRING (BASE 64 ENCODED)
   */
  public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    // Obtain the public key object through the Key command encoded by X509
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
    RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
    return key;
  }

  private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
    int maxBlock = 0;
    if (opmode == Cipher.DECRYPT_MODE) {
      maxBlock = keySize / 8;
    } else {
      maxBlock = keySize / 8 -11;
    }
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] buff;
    int i = 0;
    try {
      while (datas.length > offSet) {
        if (datas.length - offSet > maxBlock) {
          buff = cipher.doFinal(datas, offSet, maxBlock);
        } else {
          buff = cipher.doFinal(datas, offSet, datas.length - offSet);
        }
        out.write(buff, 0, buff.length);
        i++;
        offSet = i * maxBlock;
      }
    } catch (Exception e) {
      throw new RuntimeException("An exception occurred while encrypting or decrypting data with a threshold of [" + maxBlock + "].", e);
    }
    byte[] resultDatas = out.toByteArray();
    IOUtils.closeQuietly(out);
    return resultDatas;
  }

  public static String doPost(String url, String json) throws IOException {
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost(url);
    StringEntity s = new StringEntity(json);
    s.setContentEncoding("UTF-8");
    s.setContentType("application/json");// SENDING JSON DATA NEEDS TO SET CONTENT TYPE
    post.setEntity(s);
    HttpResponse res = client.execute(post);
    if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
      return EntityUtils.toString(res.getEntity());// RETURN JSON FORMAT
    }
    return null;
  }
}

<?php
header('Content-Type: text/html; charset=utf-8');
class Rsa{
  public function __construct($platPublicKey, $mchPrivateKey){
    // Read public and private keys from file
    $this->publicKey = openssl_pkey_get_public($platPublicKey);
    if (!$this->publicKey) {
      die('Load public key error');
    }
    $this->privateKey = openssl_pkey_get_private($mchPrivateKey);
    if (!$this->privateKey) {
      die('Load private key error');
    }
  }

  // Private key encrypted data
  public function pivate_key_encrypt($data){
    $crypto = '';
    foreach (str_split($data, 117) as $chunk) {
      openssl_private_encrypt($chunk, $encryptData, $this->privateKey);
      $crypto .= $encryptData;
    }
    return base64_encode($crypto); // The signed data is base64 encoded for transmission
  }

  // Public key decrypts signed data
  public function public_key_decrypt($encryptedData){
    $encryptedData = base64_decode($encryptedData); // Decode the received signature
    if ($encryptedData === false) {
      echo "Failed to base64 decode the data.\n";
      return false;
    }
    if (openssl_public_decrypt($encryptedData, $decrypted, $this->publicKey)) {
      return $decrypted; // Decryption is successful and the decrypted data is returned.
    } else {
      // Print all OpenSSL error messages
      while ($msg = openssl_error_string()) {
        echo "OpenSSL Error: $msg\n";
      }
      return false; // Decryption failed
    }
  }
}

// The following code is debugging code
// Please note: Use the merchant’s private key to request data encryption, and use the platform’s public key to decrypt the notification.
// Merchant private key
$mchPrivateKey = '-----BEGIN PRIVATE KEY-----'."\n".'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAK8ZBceS0SgKtD8Scxx5jhBVzXkofK9bV4/JiZKpkkL327iBx1vNhi66fDuSqx0gpZKM9Xqr/hxzzrrlk4W5QfxbyIUx06a4Y7RBFQi+J/T2GHJbJozsik8yVTb6jJ0BZaOBzwkWYNm3V1Zsy5o5L1HD3n3BQ6hCltArjIPfsGPTAgMBAAECgYBtrMS7/zBXXm0MXKgRm+nqPXRYgY2n4RT1kY1EekaM/+d2qIOQ8ykrN8/9GJ9pvTs4kmZokja256sD0i0XQ7UaLXYp31jTU4HlNpB8ixiCQG0gvldZRlLlGEeCeNrCQXT88dqC9mmvR7FIvS1sUnDonMZXEtJC8HOL4L80P52P0QJBAN3aovKYaCtmbtmYc76U+CqxyijRCxqV1Cb6u+YFY423FrFG2hXhdVu8UmgV+VvgT5lh0VQyKzVqRIdwk1hwzW8CQQDKDB8alSSBgcaEhlQkuTUjz4TwnYa5UhDuwzIs9FC2QLbBMa5alUiJDVlqVc33WzAHmGBJPRtMnnsGlgPeQiXdAkEAw9GVfjeTyqrxMNLlZtSqb1TaMUjCWkbhKT6q1w7unkl6mCMHn8SPB9ejjQfIP5Yv7Bxw3bYieVVBA6MUCz4VtQJAKju8aK8SMWvyFhOKFR8Np42iKTWUSrqHxZDRtSngkSgXy2xaGJ8pyVQcA6kp43GRKZVFUynNwgdCig7jghrrlQJBANfIE+D+PcFLSC8pRmX0sPhTLbuSFORgRzvpovW/FGXonNRp/yPWgIdXzpJKb40IFbbPEPjZtHKRJwSWWBDEfuk='."\n".'-----END PRIVATE KEY-----';
// Platform public key
$platPublicKey = '-----BEGIN PUBLIC KEY-----'."\n".'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvGQXHktEoCrQ/EnMceY4QVc15KHyvW1ePyYmSqZJC99u4gcdbzYYuunw7kqsdIKWSjPV6q/4cc8665ZOFuUH8W8iFMdOmuGO0QRUIvif09hhyWyaM7IpPMlU2+oydAWWjgc8JFmDZt1dWbMuaOS9Rw959wUOoQpbQK4yD37Bj0wIDAQAB'."\n".'-----END PUBLIC KEY-----';
// Call the encryption and decryption tool class
$encryptionTool = new Rsa($platPublicKey, $mchPrivateKey);
$merchantCode = 'S820230414142102000012';
$orderType = '0';
$orderNum = 'TEST202404121503';
$money = '22.00';
$name = 'สวัสดี สวัสดี';
$name_ch = 'test';
$dateTime = date("YmdHis",time());
$params = array(
  'merchantCode' => $merchantCode,
  'orderType' => $orderType,
  'orderNum' => $orderNum,
  'money' => $money,
  'dateTime' => $dateTime,
  'name' => $name,
  'name_ch' => $name_ch,
);
// Data sorting and splicing
ksort($params);
$params_str = '';
foreach ($params as $key => $val) {
  $params_str = $params_str . $val;
}
$signature = $encryptionTool->pivate_key_encrypt($params_str);
$decryptedData = $encryptionTool->public_key_decrypt($signature);
echo "Data: " . $params_str . "<br>";
echo "Signature: " . $signature . "<br>";
echo "DecryptedData: " . $decryptedData . "<br>";
?>
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace demo.utils
{
  public class RSAForJava
  {

    public RSAForJava() 
    {

    }
    /// <summary>
    /// KEY structure
    /// </summary>
    public struct RSAKEY
    {
      /// <summary>
      /// Public key
      /// </summary>
      public string PublicKey
      {
        get;
        set;
      }
      /// <summary>
      /// Private key
      /// </summary>
      public string PrivateKey
      {
        get;
        set;
      }
    }

    public RSAKEY GetKey() {
      // Constructor for RSA key pairs
      RsaKeyPairGenerator keyGenerator = new RsaKeyPairGenerator();

      // RSA key constructor parameters
      RsaKeyGenerationParameters param = new RsaKeyGenerationParameters(
        Org.BouncyCastle.Math.BigInteger.ValueOf(3),
        new Org.BouncyCastle.Security.SecureRandom(),
        1024, // Key length
        25);
      // Initialize key constructor with parameters
      keyGenerator.Init(param);
      // Generate key pair
      AsymmetricCipherKeyPair keyPair = keyGenerator.GenerateKeyPair();
      // Get the public key and secret key
      AsymmetricKeyParameter publicKey = keyPair.Public;
      AsymmetricKeyParameter privateKey = keyPair.Private;

      SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
      PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);

      Asn1Object asn1ObjectPublic = subjectPublicKeyInfo.ToAsn1Object();

      byte[] publicInfoByte = asn1ObjectPublic.GetEncoded("UTF-8");
      Asn1Object asn1ObjectPrivate = privateKeyInfo.ToAsn1Object();
      byte[] privateInfoByte = asn1ObjectPrivate.GetEncoded("UTF-8");

      RSAKEY item = new RSAKEY()
      {
        PublicKey = Convert.ToBase64String(publicInfoByte),
        PrivateKey = Convert.ToBase64String(privateInfoByte)
      };
      return item;
    }
    private AsymmetricKeyParameter GetPublicKeyParameter(string s)
    {
      s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
      byte[] publicInfoByte = Convert.FromBase64String(s);
      Asn1Object pubKeyObj = Asn1Object.FromByteArray(publicInfoByte); // Here you can also read from the stream and import from local
      AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(publicInfoByte);
      return pubKey;
    }
    private AsymmetricKeyParameter GetPrivateKeyParameter(string s)
    {
      s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
      byte[] privateInfoByte = Convert.FromBase64String(s);
      // Asn1Object priKeyObj = Asn1Object.FromByteArray(privateInfoByte); // Here you can also read from the stream and import from local
      // PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
      AsymmetricKeyParameter priKey = PrivateKeyFactory.CreateKey(privateInfoByte);
      return priKey;
    }

    private static StreamReader GetStreamReader(string content)
    {
      byte[] bytes = Encoding.UTF8.GetBytes(content);
      var memory = new MemoryStream(bytes);
      var reader = new StreamReader(memory);
      return reader;
    }

    public string EncryptByPrivateKey(string s, string key)
    {
      // Asymmetric encryption algorithm, used for encryption and decryption
      IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
      // encryption
      try
      {
        engine.Init(true, GetPrivateKeyParameter(key));
        byte[] byteData = System.Text.Encoding.UTF8.GetBytes(s);

        byte[] result = new byte[] { };
        for (int i = 0, j = byteData.Length / 117 + (byteData.Length % 117 == 0 ? 0 : 1); i < j; i++)
        {
          byte[] getData = byteData.Skip(i * 117).Take(117).ToArray();
          byte[] ResultData = engine.ProcessBlock(getData, 0, getData.Length);
          result = result.Concat(ResultData).ToArray();
        }

        // var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
        return Convert.ToBase64String(result);

        // Console.WriteLine("Ciphertext (base64 encoded):" + Convert.ToBase64String(testData) + Environment.NewLine);
      }
      catch (Exception ex)
      {
        return ex.Message;
      }
    }
    public string DecryptByPublicKey(string s, string key)
    {
      s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
      // Asymmetric encryption algorithm, used for encryption and decryption
      IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
      // Decrypt
      try
      {
        engine.Init(false, GetPublicKeyParameter(key));
        byte[] byteData = Convert.FromBase64String(s);

        string all = "";

        for (int i = 0, j = byteData.Length / 128 + (byteData.Length % 128 == 0 ? 0 : 1); i < j; i++)
        {
          byte[] getData = byteData.Skip(i * 128).Take(128).ToArray();
          byte[] ResultData = engine.ProcessBlock(getData, 0, getData.Length);
          all += Encoding.UTF8.GetString(ResultData);
        }
        return all;
        // var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
        // return System.Text.Encoding.UTF8.GetString(ResultData);
      }
      catch (Exception ex)
      {
        return ex.Message;
      }
    }

    /// <summary>
    /// Verify signature
    /// </summary>
    /// <param name="?"></param>
    /// <returns></returns>
    public bool verifySign(Dictionary<string, object> dict, string publickey)
    {
      string platSign = dict["platSign"].ToString().Trim(); // sign
      List<string> paramNameList = new List<string>();
      foreach (string key in dict.Keys)
      {
        if (!"platSign".Equals(key))
        {
          paramNameList.Add(key);
        }
      }
      paramNameList.Sort();
      StringBuilder stringBuilder = new StringBuilder();
      for (int i = 0; i < paramNameList.Count; i++)
      {
        string name = paramNameList[i];
        stringBuilder.Append(dict[name]);
      }
      
      String decryptSign = "";
      try
      {
        decryptSign = DecryptByPublicKey(platSign, publickey);
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }

      if (!stringBuilder.ToString().Equals(decryptSign, StringComparison.OrdinalIgnoreCase))
      {
        return false;
      }
      return true;
    }
  }
}

package rsa_ext

import (
  "bytes"
  "crypto"
  "crypto/rand"
  "crypto/rsa"
  "crypto/sha512"
  "crypto/x509"
  "encoding/base64"
  "encoding/pem"
  "errors"
  "hash"
  "io"
  "io/ioutil"
  "math/big"
  "os"
)

var (
  ErrNoPubKey        = errors.New("no pub key input")
  ErrDuplicatePubKey = errors.New("duplicate pub key specify")
  ErrNoPriKey        = errors.New("no pri key input")
  ErrDuplicatePriKey = errors.New("duplicate pri key specify")
  ErrNoPem           = errors.New("no pem")
  ErrDataToLarge     = errors.New("message too long for RSA public key size")
  ErrDataLen         = errors.New("data length error")
  ErrDataBroken      = errors.New("data broken, first byte is not zero")
  ErrKeyPairDismatch = errors.New("data is not encrypted by the private key")
  ErrDecryption      = errors.New("decryption error")
)

type Cryptor struct {
  pub     *rsa.PublicKey
  pri     *rsa.PrivateKey
	PubPath string // Public key address
	PriPath string // Private key address
	Pub64   string // The public key body does not include header and tail
	Pri64   string // The private key body does not include header and tail
}

func NewCryptor() *Cryptor {
  return &Cryptor{}
}

func (r *Cryptor) loadPubKey() {
  if r.pub != nil {
    return
  }
  if r.PubPath == "" && r.Pub64 == "" {
    panic(ErrNoPubKey)
  }
  if r.PubPath != "" && r.Pub64 != "" {
    panic(ErrDuplicatePubKey)
  }
  var keyByte []byte
  if r.PubPath != "" {
    keyByte = ReadFile(r.PubPath)
    block, _ := pem.Decode([]byte(keyByte))
    if block == nil {
      panic(ErrNoPem)
    }
    keyByte = block.Bytes
  }
  if r.Pub64 != "" {
    var err error
    keyByte, err = base64.StdEncoding.DecodeString(r.Pub64)
    CheckErr(err)
  }
  publicKeyInterface, err := x509.ParsePKIXPublicKey(keyByte)
  if err != nil {
    publicKeyInterface, err = x509.ParsePKCS1PublicKey(keyByte)
    CheckErr(err)
  }
  r.pub = publicKeyInterface.(*rsa.PublicKey)
}

func (r *Cryptor) loadPriKey() {
  if r.pri != nil {
    return
  }
  if r.PriPath == "" && r.Pri64 == "" {
    panic(ErrNoPriKey)
  }
  if r.PriPath != "" && r.Pri64 != "" {
    panic(ErrDuplicatePriKey)
  }
  var keyByte []byte
  if r.PriPath != "" {
    keyByte = ReadFile(r.PriPath)
    block, _ := pem.Decode([]byte(keyByte))
    if block == nil {
      panic(ErrNoPem)
    }
    keyByte = block.Bytes
  }
  if r.Pri64 != "" {
    var err error
    keyByte, err = base64.StdEncoding.DecodeString(r.Pri64)
    CheckErr(err)
  }
  privateKey, err := x509.ParsePKCS8PrivateKey(keyByte)
  if err != nil {
    privateKey, err = x509.ParsePKCS1PrivateKey(keyByte)
    CheckErr(err)
  }
  r.pri = privateKey.(*rsa.PrivateKey)
}

// Public key encryption
func (r *Cryptor) Encrypt(plainText []byte) []byte {
  r.loadPubKey()
  cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, r.pub, plainText)
  CheckErr(err)
  return cipherText
}

// Private key decryption
func (r *Cryptor) Decrypt(cipherText []byte) []byte {
  r.loadPriKey()
  plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, r.pri, cipherText)
  return plainText
}

// Signature default SHA512
func (r *Cryptor) Sign(data []byte) []byte {
  return r.SignWithHash(data, sha512.New(), crypto.SHA512)
}

// Signature specifies the Hash algorithm. The hash and hashType should be kept consistent.
func (r *Cryptor) SignWithHash(data []byte, h hash.Hash, hashType crypto.Hash) []byte {
  r.loadPriKey()
  h.Write([]byte(data))
  signature, err := rsa.SignPKCS1v15(rand.Reader, r.pri, hashType, h.Sum(nil))
  CheckErr(err)
  return signature
}

// Signature verification default SHA512
func (r *Cryptor) Verify(data []byte, sign []byte) error {
  return r.VerifyWithHash(data, sign, sha512.New(), crypto.SHA512)
}

// Verify the signature. Specify the Hash algorithm. The hash and hashType must be consistent.
func (r *Cryptor) VerifyWithHash(data []byte, sign []byte, h hash.Hash, hashType crypto.Hash) error {
  r.loadPubKey()
  h.Write(data)
  return rsa.VerifyPKCS1v15(r.pub, hashType, h.Sum(nil), sign)
}

func ReadFile(path string) []byte {
  file, err := os.Open(path)
  CheckErr(err)
  defer file.Close()
  info, err := file.Stat()
  CheckErr(err)
  buf := make([]byte, info.Size())
  _, err = file.Read(buf)
  CheckErr(err)
  return buf
}

// Private key encryption
func (r *Cryptor) PriEncrypt(input []byte) []byte {
  r.loadPriKey()
  output := bytes.NewBuffer(nil)
  err := priKeyIO(r.pri, bytes.NewReader(input), output, true)
  CheckErr(err)
  out, err := ioutil.ReadAll(output)
  CheckErr(err)
  return out
}

// Public key decryption
func (r *Cryptor) PubDecrypt(input []byte) []byte {
  r.loadPubKey()
  output := bytes.NewBuffer(nil)
  err := pubKeyIO(r.pub, bytes.NewReader(input), output, false)
  CheckErr(err)
  out, err := ioutil.ReadAll(output)
  CheckErr(err)
  return out
}

// Private key to encrypt or decrypt Reader
func priKeyIO(pri *rsa.PrivateKey, r io.Reader, w io.Writer, isEncrypt bool) (err error) {
  k := (pri.N.BitLen() + 7) / 8
  if isEncrypt {
    k = k - 11
  }
  buf := make([]byte, k)
  var b []byte
  size := 0
  for {
    size, err = r.Read(buf)
    if err != nil {
      if err == io.EOF {
        return nil
      }
      return err
    }
    if size < k {
      b = buf[:size]
    } else {
      b = buf
    }
    if isEncrypt {
      b, err = priKeyEncrypt(rand.Reader, pri, b)
    } else {
      b, err = rsa.DecryptPKCS1v15(rand.Reader, pri, b)
    }
    if err != nil {
      return err
    }
    if _, err = w.Write(b); err != nil {
      return err
    }
  }
}

// Public key encryption or decryption Reader
func pubKeyIO(pub *rsa.PublicKey, in io.Reader, out io.Writer, isEncrytp bool) (err error) {
  k := (pub.N.BitLen() + 7) / 8
  if isEncrytp {
    k = k - 11
  }
  buf := make([]byte, k)
  var b []byte
  size := 0
  for {
    size, err = in.Read(buf)
    if err != nil {
      if err == io.EOF {
        return nil
      }
      return err
    }
    if size < k {
      b = buf[:size]
    } else {
      b = buf
    }
    if isEncrytp {
      b, err = rsa.EncryptPKCS1v15(rand.Reader, pub, b)
    } else {
      b, err = pubKeyDecrypt(pub, b)
    }
    if err != nil {
      return err
    }
    if _, err = out.Write(b); err != nil {
      return err
    }
  }
}

// Public key decryption
func pubKeyDecrypt(pub *rsa.PublicKey, data []byte) ([]byte, error) {
  k := (pub.N.BitLen() + 7) / 8
  if k != len(data) {
    return nil, ErrDataLen
  }
  m := new(big.Int).SetBytes(data)
  if m.Cmp(pub.N) > 0 {
    return nil, ErrDataToLarge
  }
  m.Exp(m, big.NewInt(int64(pub.E)), pub.N)
  d := leftPad(m.Bytes(), k)
  if d[0] != 0 {
    return nil, ErrDataBroken
  }
  if d[1] != 0 && d[1] != 1 {
    return nil, ErrKeyPairDismatch
  }
  var i = 2
  for ; i < len(d); i++ {
    if d[i] == 0 {
      break
    }
  }
  i++
  if i == len(d) {
    return nil, nil
  }
  return d[i:], nil
}

// Copied from crypto/rsa
func leftPad(input []byte, size int) (out []byte) {
  n := len(input)
  if n > size {
    n = size
  }
  out = make([]byte, size)
  copy(out[len(out)-n:], input)
  return
}

// Private key encryption
func priKeyEncrypt(rand io.Reader, priv *rsa.PrivateKey, hashed []byte) ([]byte, error) {
  tLen := len(hashed)
  k := (priv.N.BitLen() + 7) / 8
  if k < tLen+11 {
    return nil, ErrDataLen
  }
  em := make([]byte, k)
  em[1] = 1
  for i := 2; i < k-tLen-1; i++ {
    em[i] = 0xff
  }
  copy(em[k-tLen:k], hashed)
  m := new(big.Int).SetBytes(em)
  c, err := decrypt(rand, priv, m)
  if err != nil {
    return nil, err
  }
  copyWithLeftPad(em, c.Bytes())
  return em, nil
}

// Copied from crypto/rsa
var bigZero = big.NewInt(0)
var bigOne = big.NewInt(1)

func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) {
  if c.Cmp(priv.N) > 0 {
    err = ErrDecryption
    return
  }
  var ir *big.Int
  if random != nil {
    var r *big.Int

    for {
      r, err = rand.Int(random, priv.N)
      if err != nil {
        return
      }
      if r.Cmp(bigZero) == 0 {
        r = bigOne
      }
      var ok bool
      ir, ok = modInverse(r, priv.N)
      if ok {
        break
      }
    }
    bigE := big.NewInt(int64(priv.E))
    rpowe := new(big.Int).Exp(r, bigE, priv.N)
    cCopy := new(big.Int).Set(c)
    cCopy.Mul(cCopy, rpowe)
    cCopy.Mod(cCopy, priv.N)
    c = cCopy
  }
  if priv.Precomputed.Dp == nil {
    m = new(big.Int).Exp(c, priv.D, priv.N)
  } else {
    m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0])
    m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1])
    m.Sub(m, m2)
    if m.Sign() < 0 {
      m.Add(m, priv.Primes[0])
    }
    m.Mul(m, priv.Precomputed.Qinv)
    m.Mod(m, priv.Primes[0])
    m.Mul(m, priv.Primes[1])
    m.Add(m, m2)

    for i, values := range priv.Precomputed.CRTValues {
      prime := priv.Primes[2+i]
      m2.Exp(c, values.Exp, prime)
      m2.Sub(m2, m)
      m2.Mul(m2, values.Coeff)
      m2.Mod(m2, prime)
      if m2.Sign() < 0 {
        m2.Add(m2, prime)
      }
      m2.Mul(m2, values.R)
      m.Add(m, m2)
    }
  }
  if ir != nil {
    m.Mul(m, ir)
    m.Mod(m, priv.N)
  }

  return
}

// Copied from crypto/rsa
func modInverse(a, n *big.Int) (ia *big.Int, ok bool) {
  g := new(big.Int)
  x := new(big.Int)
  y := new(big.Int)
  g.GCD(x, y, a, n)
  if g.Cmp(bigOne) != 0 {
    return
  }
  if x.Cmp(bigOne) < 0 {
    x.Add(x, n)
  }
  return x, true
}

// Copied from crypto/rsa
func copyWithLeftPad(dest, src []byte) {
  numPaddingBytes := len(dest) - len(src)
  for i := 0; i < numPaddingBytes; i++ {
    dest[i] = 0
  }
  copy(dest[numPaddingBytes:], src)
}

func CheckErr(err error) {
  if err != nil {
    panic(err)
  }
}

from M2Crypto import RSA

# Generate RSA key pair
rsa_key = RSA.gen_key(2048, 65537)

# Data to be encrypted
data_to_encrypt = b'Hello, RSA!'

# Encrypt data using private key
encrypted_data = rsa_key.private_encrypt(data_to_encrypt, RSA.pkcs1_padding)

# Decrypt data using public key
try:
  decrypted_data = rsa_key.public_decrypt(encrypted_data, RSA.pkcs1_padding)
  print("Decrypted data:", decrypted_data)
except RSA.RSAError as e:
  print("RSAError:", e)

const crypto = require('crypto');

const nodeRSA = require('node-rsa');

// This public and private key is for example only. When connecting business, please use the real key pair.
const privateKey = `-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKGFQO+Gu+a1Vc08jsYL7v2dKIitgAeFPSNDOyNBUnbH3IFFfk0lNGHZQ45pvicFMARshBDzR7jJynl/F+JYkQBoqFQIRVsEHMWkq3+3QapCjknNq+H2Vnd41swxaWy3lNTgN532ia04LBghWxCJo4/ZMBsUX8qCSTer1lvSsFkXAgMBAAECgYA7hPUWztly/KNvi78477DTuBP1uamSG+EZm26xYSLYLnHtGQSfvPP+kqc4VXMCwgCdwLcx39QwHfwEpXbRq71SiUHiiwaZncNIWLFiUzy23DDc6GlP5VPp/bCMWrIukE0zp4a4Il7Uq7smDAM1oZFv44PMJfX52iFdmO1DSKX0YQJBANOwqgt6AjVuBZErpe5bZdZAxXFR3utc2hcQza4XmK1bxrKK3IfK7qslyGc+OrW5HzCzBQM3M5tGAPmFDmalMg8CQQDDVEV29muQ9R1cLcAVMLPNeBkMwukLO0J/iJ4JN+hS8bcOdpdQ8pyaNqbUMpHkYKP0PhS2dcieAVRlVAj8mVB5AkBNH+9EESae2r/SfSmOJuR6Y8zLlX13GLQodSnKhLRObMJ+qBLism+0ESbrXoO7U3/mmUJ5QnJd3upZ/j0Z7pPHAkBQOXtyWdFiLxpI8s1yppciq9H2CNO4djVgthbaiGjWMErUGOcbKxogyJOKkd6T4xFTzm+pZQTpyHgGShaK0NlZAkBMst7KZNxDMwRTImCCi03GU42uKfpzK9pK3cjyU2eYftNXAfEmfB49uxXW839m9vYQvJBltYUbbsLsMdJBkrNT
-----END PRIVATE KEY-----`;

const publicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChhUDvhrvmtVXNPI7GC+79nSiIrYAHhT0jQzsjQVJ2x9yBRX5NJTRh2UOOab4nBTAEbIQQ80e4ycp5fxfiWJEAaKhUCEVbBBzFpKt/t0GqQo5Jzavh9lZ3eNbMMWlst5TU4Ded9omtOCwYIVsQiaOP2TAbFF/Kgkk3q9Zb0rBZFwIDAQAB
-----END PUBLIC KEY-----`;


// Encrypt the message using the private key (usually this is used to create a signature)
function encryptWithPrivateKey(data) {
  // Convert data to Buffer
  const buffer = Buffer.from(data);
  // Maximum size of encrypted blocks
  const maxBlockSize = 117; 
  let offset = 0;
  let encryptedBuffer = Buffer.alloc(0);
  // Block encryption
    while (offset < buffer.length) {
    // Get the current block
    const block = buffer.slice(offset, Math.min(offset + maxBlockSize, buffer.length));
    // 使用私钥进行加密
    const encryptedBlock = crypto.privateEncrypt(
      {
        key: privateKey,
        padding: crypto.constants.RSA_PKCS1_PADDING,
      },
      block
    );
    // Add the encrypted block to the result Buffer
    encryptedBuffer = Buffer.concat([encryptedBuffer, encryptedBlock]);
    // increase offset
    offset += block.length;
  }
  // Convert to Base64 encoding
  return encryptedBuffer.toString('base64');
}



// Decrypt data using public key
function decryptWithPublicKey(signature) {
  const decryptedData = crypto.publicDecrypt(
    {
      key: publicKey,
      padding: crypto.constants.RSA_PKCS1_PADDING,
    },
    Buffer.from(signature, 'base64') // Decode Base64 encoded encrypted data
  );
  return decryptedData.toString();
}

// Export encryption functions for use in other files
module.exports = {
  encryptWithPrivateKey,
  decryptWithPublicKey,
}