Skip to content

Signature Code Examples

Java

java
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
public class RSASignatureUtil {

    /**
     * Sign with SHA256withRSA
     * @param data The string to sign
     * @param privateKeyBase64 PKCS#8 private key (Base64 encoded, without PEM header/footer)
     * @return Base64-encoded signature result
     */
    public static String sign(String data, String privateKeyBase64) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);

        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(key);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * Construct signature string
     * @param bodyData Request body JSON string (pass null if no body)
     * @param timestamp Unix timestamp (seconds)
     * @param nonce Random string
     * @param requestParams URL query parameters
     * @return Concatenated signature string
     */
    public static String constructSignatureString(
            String bodyData, String timestamp, String nonce,
            Map<String, String> requestParams) {

        StringBuilder sortedParams = new StringBuilder();
        new TreeMap<>(requestParams).forEach((key, value) ->
                sortedParams.append(key).append("=").append(value).append("&"));
        if (sortedParams.length() > 0) {
            sortedParams.setLength(sortedParams.length() - 1);
        }
        return String.format("%s%s%s%s",
                sortedParams, timestamp, nonce,
                bodyData != null ? bodyData : "");
    }
}

Usage example:

java
Map<String, String> params = Map.of("param1", "value1", "param2", "value2");
String signatureString = RSASignatureUtil.constructSignatureString(
    "{\"merchantUserNo\":\"U1000001\",\"email\":\"[email protected]\"}",
    "1743478725", "a1b2c3", params);
String signature = RSASignatureUtil.sign(signatureString, PRIVATE_KEY);

Python

TIP

Requires dependency: pip install pycryptodome

python
import base64
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256


def construct_signature_string(body_data, timestamp, nonce, request_params):
    """Construct signature string"""
    sorted_params = "&".join(
        f"{k}={v}" for k, v in sorted(request_params.items())
    )
    return f"{sorted_params}{timestamp}{nonce}{body_data or ''}"


def sign(data, private_key_pem):
    """Sign with SHA256withRSA"""
    key = RSA.import_key(private_key_pem)
    h = SHA256.new(data.encode("utf-8"))
    signature = pkcs1_15.new(key).sign(h)
    return base64.b64encode(signature).decode("utf-8")


# Usage example
PRIVATE_KEY = """-----BEGIN PRIVATE KEY-----
<YOUR_PRIVATE_KEY>
-----END PRIVATE KEY-----"""

request_params = {"param1": "value1", "param2": "value2"}
body = '{"merchantUserNo":"U1000001","email":"[email protected]"}'
signature_string = construct_signature_string(body, "1743478725", "a1b2c3", request_params)
result = sign(signature_string, PRIVATE_KEY)
print("Signature:", result)

Go

go
package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"log"
	"sort"
	"strings"
)

// constructSignatureString constructs the signature string
func constructSignatureString(bodyData, timestamp, nonce string, params map[string]string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	pairs := make([]string, 0, len(keys))
	for _, k := range keys {
		pairs = append(pairs, fmt.Sprintf("%s=%s", k, params[k]))
	}
	sortedParams := strings.Join(pairs, "&")
	return sortedParams + timestamp + nonce + bodyData
}

// sign signs data with SHA256withRSA
func sign(data, pemKey string) (string, error) {
	block, _ := pem.Decode([]byte(pemKey))
	if block == nil {
		return "", fmt.Errorf("failed to decode PEM block")
	}

	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse private key: %w", err)
	}

	rsaKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return "", fmt.Errorf("private key is not RSA")
	}

	hashed := sha256.Sum256([]byte(data))
	sig, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, hashed[:])
	if err != nil {
		return "", fmt.Errorf("failed to sign: %w", err)
	}

	return base64.StdEncoding.EncodeToString(sig), nil
}

func main() {
	params := map[string]string{"param1": "value1", "param2": "value2"}
	body := `{"merchantUserNo":"U1000001","email":"[email protected]"}`
	sigStr := constructSignatureString(body, "1743478725", "a1b2c3", params)

	signature, err := sign(sigStr, privateKeyPEM)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Signature:", signature)
}

All game copyrights, trademarks, and service marks belong to their respective owners.