Sign & Data encryption

鉴权签名&数据加密

鉴权签名

  1. 将代理商 ID 放入到 Header 头中。
  2. 将请求参数以 a 至 z 排序后,然后使用&x=y 的逻辑拼接
  3. 请求方法 + URL + 时间 + requestid + 请求参数| 分割,拼成字符串。
  4. 使用 hmac.sha256 对拼接好的字符串进行签名。
  5. 对签名之后的数据进行 base64

📘

测试网站: https://www.devglan.com/online-tools/hmac-sha256-online

参数类型是否必须示例值备注
URLString/api/v1/test
METHODStringPOST
timestampString1701024967796毫秒级即可。验证 60 秒请求超时
requestidStringa0a47bf4-0507-4f04-894b-047d15bfeeff自定义,不可重复
paramsStringkey1=value1&key2=value2

Demo

URL: /api/v1/test
方法: POST
时间:1701024967796
requestid: a0a47bf4-0507-4f04-894b-047d15bfeeff
参数: key1=value1&key2=value2

// 请求方法 + URL + 时间 + requestid + 请求参数
POST|/api/v1/test|1701024967796|a0a47bf4-0507-4f04-894b-047d15bfeeff|key1=value1&key2=value2

假如 Sign 值为 a3e43e0a51ad9bf25b3fe7dbc40b88e25f1921a1a0f23a912c1c13ac7219c6ca, Base64之后为 f+B4V5buwFnDLNmMlniNPhswh15G1lwFogGjX6Yrf2I=, 将 o+Q+ClGtm/JbP+fbxAuI4l8ZIaGg8jqRLBwTrHIZxso= 放入到 X-URA-SIGN 头中

Demo Code

import hmac
import base64
from hashlib import sha256


def sign_data_sha256(data, secret):
    data = data.encode('utf8')
    secret = secret.encode('utf8')
    signature = base64.b64encode(hmac.new(secret, data, digestmod=sha256).digest())
    return signature.decode()
      

if __name__ == '__main__':
    url = "/api/v1/test"
    method = "POST"
    requestid = "a0a47bf4-0507-4f04-894b-047d15bfeeff"
    params = {"key1": "value1", "key2": "value2"}
    time = "1701024967796"
    unsign_data = "|".join([method, url, time, requestid, "&".join(["{k}={v}".format(k=k, v=params[k]) for k in sorted(params)])])
    
    api_secret = "0a227672c20f48989b5fcdb4c00038ff"
    sign_data = sign_data_sha256(unsign_data, api_secret)
    print("sign data is : ", sign_data)
    # print to o+Q+ClGtm/JbP+fbxAuI4l8ZIaGg8jqRLBwTrHIZxso=
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class HMACSHA256Signer {

    public static String signDataSHA256(String data, String secret) throws Exception {
        Mac sha256HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
        sha256HMAC.init(secretKey);

        byte[] hash = sha256HMAC.doFinal(data.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(hash);
    }

    public static void main(String[] args) throws Exception {
        String url = "/api/v1/test";
        String method = "POST";
        String requestid = "a0a47bf4-0507-4f04-894b-047d15bfeeff";
        Map<String, String> params = new TreeMap<>();
        params.put("key1", "value1");
        params.put("key2", "value2");
        String time = "1701024967796";

        String unsignData = String.join("|",
                method,
                url,
                time,
                requestid,
                params.entrySet().stream()
                        .map(entry -> entry.getKey() + "=" + entry.getValue())
                        .collect(Collectors.joining("&"))
        );

        String apiSecret = "0a227672c20f48989b5fcdb4c00038ff";
        String signData = signDataSHA256(unsignData, apiSecret);
        System.out.println("sign data is : " + signData);
    }
}

数据加密

加密基本信息

过程: 加密分为两对公私钥,一对是平台密钥`,一对是商户密钥。平台使用商户公钥加密商户使用私钥解密,同样,商户使用平台公钥加密,平台自己使用私钥解密。
算法: JWE RSA-OAEP-256
encrypt: A128GCM

RSA 生成规则: RSA 2048bit, 可以使用 JWK 直接生成,或者自己生成 RSA 2048 的公私钥

📘

测试网站: https://dinochiesa.github.io/jwt/

加密参数

{
  "alg": "RSA-OAEP-256",
  "enc": "A128GCM",
  "kid": kid,
  "typ": "JOSE",
  "zip": "DEF"
}

KID 如何计算,将公钥的 PEM 格式进行 Sha256,得到指纹。

参数作用

参数说明算法备注
alg密钥管理算法RSA-OAEP-256
enc加密算法A128GCM
typ加密类型JOSE
zip压缩类型DEF增快传输速度,降低传输内容大小
kid使用的密钥对sha256(public_pem).hexdigest()对公钥进行 hash 指纹签名,告诉对方是使用对方的哪个公钥进行签名的该套数据。 该参数为应对,当一个 Key 出现问题,但又存在多个公钥时,寻找到合适的公/私钥对加解密。

请求

业务私钥:用于解密响应平台的数据,确保该数据只能在业务被解密。

平台公钥:用于加密请求或响应平台的数据,该数据只能在业务方被解密。

响应

业务公钥:用于加密请求或响应平台的数据,该数据只能在业务方被解密。

平台私钥:用于解密请求平台的数据,确保该数据只能在平台被解密。


Demo

字段内容
请求报文{"payload": "加密后的字符串" }
Header{ "alg": "RSA-OAEP-256", "enc": "A128GCM", "kid": "4B7AC601795594F8EB3C50F07E5FE4569E9B04D95A2C83F2B680BE0AA89A9E0B", "typ": "JWE", "zip": "DEF" }
原始报文{ "id": "123", "hello": "world", "time": 1723976484, "tag": "UPay Encryption and decryption" }
加密报文eyJhbGciOiAiUlNBLU9BRVAtMjU2IiwgImVuYyI6ICJBMTI4R0NNIiwgImtpZCI6ICI0QjdBQzYwMTc5NTU5NEY4RUIzQzUwRjA3RTVGRTQ1NjlFOUIwNEQ5NUEyQzgzRjJCNjgwQkUwQUE4OUE5RTBCIiwgInR5cCI6ICJKV0UiLCAiemlwIjogIkRFRiJ9.vBFtYk_yN8MG_iYGA-xpU_eBnswO3e9jXimmWuuIOh54yO9umpEttC-9Ns3beJ8QKx8U37ZilRIBQZMLnBAGPvAl7v3hGx6yzSKBp3sCpbV5Ueiv4Q7CudLDbaOPk0VkGJXzSe4UidKWDs_Qzk_q7GNVHwZNYveiY28wi4hLsRc5Jzb1CQ-oLo-NnI66zs-QttPSarA-9aP_TSOSiPStNFGgRK_fP-RBwNBmx6ZrKS6WjPqc8QjaykrEgxL8FgaSgi-5C9sss8uty6kJWb6R9zecbpMpQUHvRRUefVFQqI7hcruvv2m8Yol5HHWRRGDYBNS3QFi3NPbodQT3VjxWig.Xi_RKtZ6KMmRStA9.8zXIO3mzF1XfHhUkfxqG5W4aprGR68gB_0QyyHU2nQkjBzIPHDmVWryB8-Ie_zySCOT7B-4ZqEb-n3T1untzMGD4eBaJ_87Apa8AdWBm.Sgyj_8opMH_Gj_C-EWvzhg
公钥-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvcnAzEJgN3tcyed3SayO o+2go3LrYxuNRWiYFwkDPMmcpgP4cyeasY7XjWr7dUPlwjmUf5DfG0UeasQlVu16 c/Ab8Qkj4ypopJ4hXwZUb45KVynHjcNsZVAC4fDOFBWCKSn2MJs28N4D5tZnvY9Z BQIQLMkyc0tUGl6VbSb+EOP5mZuK5lZ9yBAZioQl49rXTHQpvzz1AISmperUpsU9 oYJch5/sWS2+rkinI6cC3LZkGndSl5ruUQ1FAyLhd71JX++zVuP6EayByGnwKSg8 7RqnaSKSzPnVq4bClWE+hNlVrkuSlh1VtBXIK14qVpyNXyW01I075kbLm7zDr9LW /wIDAQAB -----END PUBLIC KEY-----
私钥-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9ycDMQmA3e1zJ 53dJrI6j7aCjcutjG41FaJgXCQM8yZymA/hzJ5qxjteNavt1Q+XCOZR/kN8bRR5q xCVW7Xpz8BvxCSPjKmikniFfBlRvjkpXKceNw2xlUALh8M4UFYIpKfYwmzbw3gPm 1me9j1kFAhAsyTJzS1QaXpVtJv4Q4/mZm4rmVn3IEBmKhCXj2tdMdCm/PPUAhKal 6tSmxT2hglyHn+xZLb6uSKcjpwLctmQad1KXmu5RDUUDIuF3vUlf77NW4/oRrIHI afApKDztGqdpIpLM+dWrhsKVYT6E2VWuS5KWHVW0FcgrXipWnI1fJbTUjTvmRsub vMOv0tb/AgMBAAECggEABT4L7yMhkOwaCKe191IRZmBbe2obnaFhXcVIWxhQIxSM mQIH2M0f3g2awvxQ6tgIrNkLUuEwnrUM5IptQyvwBV1EQ9/ql7DXeeiZ2X24GlwV /7dd49ATRUR76a14USBCvxo6kdglEsDAGA/43CxtVB8HopJlummsp3C0LSv18sBx h8/TtUa0ttG8UodTtBdAbIwolb55lu92XV0mGHpOc9ZsgLlyn2C7o+jlujNqOhi3 YxKCkhL2jzl88qZ4mmRPGPYQQwTo1Xzd4brixwZ1Fyag2kXziE6+v9PX8s+yFsfW 3E/0ye16tfa0/qAx/4oGr9Ox4e7wXLLiN2QpR8UkEQKBgQD4W1r9y3/uKPDne/UR srt0oZQOG9OqouVNgIwzZYw8WG6fu9h+RuihRpLLLqBwCDCiPo17xyHkPufonntJ 4FkfzvL9RV2/d7FERIOPy9umsoFhv1cqD94zlf96hNj5EK1VZpHGhhZ1MCuiRr8W J/tPiCMMSOxVus3SRPw9M/IebwKBgQDDoPjR1WJb1i23uCQm9uvKXl3AgJ9ltfHA JuRR2SO5XxOkZ5HxG+QSAkd9Og4kQAN6MQ9HMh1sR8xa75ck9G5YgmAGBtzRr68L RpcWvT1MNN81hkOrq3pvkrLkepSyz174EIV3m3aTOXaqSe0TOtPN/mU57NxWgyFz QyrubvcYcQKBgQCi2VxfwHlhU+0rDIfUlAsA3hYz7iEr6WZMHHdSGEsNIrte+BBs NNjDL4B/xFIlQ+mH6VZijF93x5vPV2PmPDqUdeG1Gy/upXBSIE7YEkc0FiZqTsm7 5e3Ai6Ga19Nh9YKC1h/OOgwtyWlDHfqcb5kFPHIm0wZM7JGbR+rRwucG9wKBgFc7 JRgz5Rr8QwCD/KLfQb7IG1fmQq1Q1w6OiQqyH0reSVzqzdnMTQN2vrfpNjsVzDcl PyHJB/OrhEUcRrI0yCtxXy3wy3A4vX3yqRZ7paSggtNUHkKjQQIVUO2udzXQYBGP FGSEDviRDAfc1PjZJ27YO/z7UDjHWhgSYEdXdZkRAoGAPpHataBL9NOHZG4zSmCZ jw9yXHTe9xuZl2mkYFgMVzcoGvMjS4PFWDncpiDaVef9rvkY1SlmkMjejOb4KxyI eUkVcbUjvHQ786s4Ul+kdPJehx5B9Av0QhE1D1TVXPUxkx5Xa7usYW3HR0CmeN/1 IFK8O6Hrcn4W90kIgq86hng= -----END PRIVATE KEY-----

Demo Code

from jwcrypto import jwk, jwe
import json
from hashlib import sha256


class JWECrypto(object):
    header = {
        'alg': 'RSA-OAEP-256',
        'enc': 'A128GCM',
        'kid': None,
        "typ": "JWE",
        "zip": "DEF"
    }

    def __init__(self, public_key=None, private_key=None):
        if private_key and public_key:
            self.private_key = jwk.JWK.from_pem(private_key.encode('utf-8'))
            self.public_key = jwk.JWK.from_pem(public_key.encode('utf-8'))
        elif private_key and public_key is None:
            self.private_key = jwk.JWK.from_pem(private_key.encode('utf-8'))
            self.public_key = self.private_key.public()
        elif public_key and private_key is None:
            self.private_key = None
            self.public_key = jwk.JWK.from_pem(public_key.encode('utf-8'))
        elif public_key is None and private_key is None:
            # 生产上不要用,这里只是为了 Demo 而已,生产上会千万数据异常。因为 RSA Key 不可控。
            self.private_key = jwk.JWK(generate='RSA')
            self.public_key = self.private_key.export_public()
            self.public_key = self.private_key.public()

    def encrypt(self, payload: dict):
        header = self.header.copy()
        kid = sha256(self.public_key.export_to_pem()).hexdigest().upper()
        header['kid'] = kid

        jwetoken = jwe.JWE(
            json.dumps(payload),
            json.dumps(header)
                           )
        jwetoken.add_recipient(self.public_key)

        encrypt_token = jwetoken.serialize(True)

        return encrypt_token

    def decrypt(self, encrypt_token: str):
        jwetoken = jwe.JWE()
        jwetoken.deserialize(encrypt_token, key=self.private_key)
        decrypted_payload = jwetoken.payload
        payload_json = json.loads(decrypted_payload)
        return payload_json


if __name__ == '__main__':
    private_key = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9ycDMQmA3e1zJ
53dJrI6j7aCjcutjG41FaJgXCQM8yZymA/hzJ5qxjteNavt1Q+XCOZR/kN8bRR5q
xCVW7Xpz8BvxCSPjKmikniFfBlRvjkpXKceNw2xlUALh8M4UFYIpKfYwmzbw3gPm
1me9j1kFAhAsyTJzS1QaXpVtJv4Q4/mZm4rmVn3IEBmKhCXj2tdMdCm/PPUAhKal
6tSmxT2hglyHn+xZLb6uSKcjpwLctmQad1KXmu5RDUUDIuF3vUlf77NW4/oRrIHI
afApKDztGqdpIpLM+dWrhsKVYT6E2VWuS5KWHVW0FcgrXipWnI1fJbTUjTvmRsub
vMOv0tb/AgMBAAECggEABT4L7yMhkOwaCKe191IRZmBbe2obnaFhXcVIWxhQIxSM
mQIH2M0f3g2awvxQ6tgIrNkLUuEwnrUM5IptQyvwBV1EQ9/ql7DXeeiZ2X24GlwV
/7dd49ATRUR76a14USBCvxo6kdglEsDAGA/43CxtVB8HopJlummsp3C0LSv18sBx
h8/TtUa0ttG8UodTtBdAbIwolb55lu92XV0mGHpOc9ZsgLlyn2C7o+jlujNqOhi3
YxKCkhL2jzl88qZ4mmRPGPYQQwTo1Xzd4brixwZ1Fyag2kXziE6+v9PX8s+yFsfW
3E/0ye16tfa0/qAx/4oGr9Ox4e7wXLLiN2QpR8UkEQKBgQD4W1r9y3/uKPDne/UR
srt0oZQOG9OqouVNgIwzZYw8WG6fu9h+RuihRpLLLqBwCDCiPo17xyHkPufonntJ
4FkfzvL9RV2/d7FERIOPy9umsoFhv1cqD94zlf96hNj5EK1VZpHGhhZ1MCuiRr8W
J/tPiCMMSOxVus3SRPw9M/IebwKBgQDDoPjR1WJb1i23uCQm9uvKXl3AgJ9ltfHA
JuRR2SO5XxOkZ5HxG+QSAkd9Og4kQAN6MQ9HMh1sR8xa75ck9G5YgmAGBtzRr68L
RpcWvT1MNN81hkOrq3pvkrLkepSyz174EIV3m3aTOXaqSe0TOtPN/mU57NxWgyFz
QyrubvcYcQKBgQCi2VxfwHlhU+0rDIfUlAsA3hYz7iEr6WZMHHdSGEsNIrte+BBs
NNjDL4B/xFIlQ+mH6VZijF93x5vPV2PmPDqUdeG1Gy/upXBSIE7YEkc0FiZqTsm7
5e3Ai6Ga19Nh9YKC1h/OOgwtyWlDHfqcb5kFPHIm0wZM7JGbR+rRwucG9wKBgFc7
JRgz5Rr8QwCD/KLfQb7IG1fmQq1Q1w6OiQqyH0reSVzqzdnMTQN2vrfpNjsVzDcl
PyHJB/OrhEUcRrI0yCtxXy3wy3A4vX3yqRZ7paSggtNUHkKjQQIVUO2udzXQYBGP
FGSEDviRDAfc1PjZJ27YO/z7UDjHWhgSYEdXdZkRAoGAPpHataBL9NOHZG4zSmCZ
jw9yXHTe9xuZl2mkYFgMVzcoGvMjS4PFWDncpiDaVef9rvkY1SlmkMjejOb4KxyI
eUkVcbUjvHQ786s4Ul+kdPJehx5B9Av0QhE1D1TVXPUxkx5Xa7usYW3HR0CmeN/1
IFK8O6Hrcn4W90kIgq86hng=
-----END PRIVATE KEY-----
        """
    public_key = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvcnAzEJgN3tcyed3SayO
o+2go3LrYxuNRWiYFwkDPMmcpgP4cyeasY7XjWr7dUPlwjmUf5DfG0UeasQlVu16
c/Ab8Qkj4ypopJ4hXwZUb45KVynHjcNsZVAC4fDOFBWCKSn2MJs28N4D5tZnvY9Z
BQIQLMkyc0tUGl6VbSb+EOP5mZuK5lZ9yBAZioQl49rXTHQpvzz1AISmperUpsU9
oYJch5/sWS2+rkinI6cC3LZkGndSl5ruUQ1FAyLhd71JX++zVuP6EayByGnwKSg8
7RqnaSKSzPnVq4bClWE+hNlVrkuSlh1VtBXIK14qVpyNXyW01I075kbLm7zDr9LW
/wIDAQAB
-----END PUBLIC KEY-----
        """
    payload = {
        "id": "123",
        "hello": "world",
        "time": 1723976484,
        "tag": "UPay Encryption and decryption"
    }
    # No Key
    jwe_crypto = JWECrypto()
    print(jwe_crypto.private_key)
    print(jwe_crypto.public_key)

    encrypt_token = jwe_crypto.encrypt(payload)
    print(encrypt_token)
    decrypted_payload = jwe_crypto.decrypt(encrypt_token)
    print(decrypted_payload)

    # Just public Key, only Sign
    jwe_crypto = JWECrypto(public_key=public_key)
    encrypt_token = jwe_crypto.encrypt(payload)
    print(encrypt_token)

    # Just private key, only design
    jwe_crypto = JWECrypto(private_key=private_key)
    # design last encrypt token
    decrypted_payload = jwe_crypto.decrypt(encrypt_token)
    print(decrypted_payload)

    # have both of key of rsa
    jwe_crypto = JWECrypto(public_key=public_key, private_key=private_key)
    encrypt_token = jwe_crypto.encrypt(payload)
    print(encrypt_token)
    decrypted_payload = jwe_crypto.decrypt(encrypt_token)
    print(decrypted_payload)
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class JWECrypto {

    private static final JWEAlgorithm JWE_ALG = JWEAlgorithm.RSA_OAEP_256;
    private static final EncryptionMethod ENC_METHOD = EncryptionMethod.A128GCM;

    private RSAPublicKey publicKey;
    private RSAPrivateKey privateKey;

    public JWECrypto(String publicKeyPEM, String privateKeyPEM) throws Exception {
        if (publicKeyPEM != null) {
            this.publicKey = loadPublicKey(publicKeyPEM);
        }
        if (privateKeyPEM != null) {
            this.privateKey = loadPrivateKey(privateKeyPEM);
        }
    }

    public String encrypt(String payload) throws Exception {
        String kid = generateKid(publicKey);

        JWEHeader header = new JWEHeader.Builder(JWE_ALG, ENC_METHOD)
                .type(JOSEObjectType.JWE)
                .keyID(kid)
                .compressionAlgorithm(CompressionAlgorithm.DEF)
                .build();

        JWEObject jweObject = new JWEObject(header, new Payload(payload));
        RSAEncrypter encrypter = new RSAEncrypter(publicKey);
        jweObject.encrypt(encrypter);

        return jweObject.serialize();
    }

    public String decrypt(String jweString) throws Exception {
        JWEObject jweObject = JWEObject.parse(jweString);
        RSADecrypter decrypter = new RSADecrypter(privateKey);
        jweObject.decrypt(decrypter);

        return jweObject.getPayload().toString();
    }

    private RSAPublicKey loadPublicKey(String publicKeyPEM) throws Exception {
        String publicKeyPEMFormatted = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");

        byte[] encoded = Base64.getDecoder().decode(publicKeyPEMFormatted);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    private RSAPrivateKey loadPrivateKey(String privateKeyPEM) throws Exception {
        String privateKeyPEMFormatted = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s", "");

        byte[] encoded = Base64.getDecoder().decode(privateKeyPEMFormatted);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    }

    private String generateKid(RSAPublicKey publicKey) throws NoSuchAlgorithmException {
        byte[] publicKeyBytes = publicKey.getEncoded();
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(publicKeyBytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(hash).toUpperCase();
    }

    public static void main(String[] args) throws Exception {
        String privateKeyPEM = """
        -----BEGIN PRIVATE KEY-----
        MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9ycDMQmA3e1zJ
        53dJrI6j7aCjcutjG41FaJgXCQM8yZymA/hzJ5qxjteNavt1Q+XCOZR/kN8bRR5q
        xCVW7Xpz8BvxCSPjKmikniFfBlRvjkpXKceNw2xlUALh8M4UFYIpKfYwmzbw3gPm
        1me9j1kFAhAsyTJzS1QaXpVtJv4Q4/mZm4rmVn3IEBmKhCXj2tdMdCm/PPUAhKal
        6tSmxT2hglyHn+xZLb6uSKcjpwLctmQad1KXmu5RDUUDIuF3vUlf77NW4/oRrIHI
        afApKDztGqdpIpLM+dWrhsKVYT6E2VWuS5KWHVW0FcgrXipWnI1fJbTUjTvmRsub
        vMOv0tb/AgMBAAECggEABT4L7yMhkOwaCKe191IRZmBbe2obnaFhXcVIWxhQIxSM
        mQIH2M0f3g2awvxQ6tgIrNkLUuEwnrUM5IptQyvwBV1EQ9/ql7DXeeiZ2X24GlwV
        /7dd49ATRUR76a14USBCvxo6kdglEsDAGA/43CxtVB8HopJlummsp3C0LSv18sBx
        h8/TtUa0ttG8UodTtBdAbIwolb55lu92XV0mGHpOc9ZsgLlyn2C7o+jlujNqOhi3
        YxKCkhL2jzl88qZ4mmRPGPYQQwTo1Xzd4brixwZ1Fyag2kXziE6+v9PX8s+yFsfW
        3E/0ye16tfa0/qAx/4oGr9Ox4e7wXLLiN2QpR8UkEQKBgQD4W1r9y3/uKPDne/UR
        srt0oZQOG9OqouVNgIwzZYw8WG6fu9h+RuihRpLLLqBwCDCiPo17xyHkPufonntJ
        4FkfzvL9RV2/d7FERIOPy9umsoFhv1cqD94zlf96hNj5EK1VZpHGhhZ1MCuiRr8W
        J/tPiCMMSOxVus3SRPw9M/IebwKBgQDDoPjR1WJb1i23uCQm9uvKXl3AgJ9ltfHA
        JuRR2SO5XxOkZ5HxG+QSAkd9Og4kQAN6MQ9HMh1sR8xa75ck9G5YgmAGBtzRr68L
        RpcWvT1MNN81hkOrq3pvkrLkepSyz174EIV3m3aTOXaqSe0TOtPN/mU57NxWgyFz
        QyrubvcYcQKBgQCi2VxfwHlhU+0rDIfUlAsA3hYz7iEr6WZMHHdSGEsNIrte+BBs
        NNjDL4B/xFIlQ+mH6VZijF93x5vPV2PmPDqUdeG1Gy/upXBSIE7YEkc0FiZqTsm7
        5e3Ai6Ga19Nh9YKC1h/OOgwtyWlDHfqcb5kFPHIm0wZM7JGbR+rRwucG9wKBgFc7
        JRgz5Rr8QwCD/KLfQb7IG1fmQq1Q1w6OiQqyH0reSVzqzdnMTQN2vrfpNjsVzDcl
        PyHJB/OrhEUcRrI0yCtxXy3wy3A4vX3yqRZ7paSggtNUHkKjQQIVUO2udzXQYBGP
        FGSEDviRDAfc1PjZJ27YO/z7UDjHWhgSYEdXdZkRAoGAPpHataBL9NOHZG4zSmCZ
        jw9yXHTe9xuZl2mkYFgMVzcoGvMjS4PFWDncpiDaVef9rvkY1SlmkMjejOb4KxyI
        eUkVcbUjvHQ786s4Ul+kdPJehx5B9Av0QhE1D1TVXPUxkx5Xa7usYW3HR0CmeN/1
        IFK8O6Hrcn4W90kIgq86hng=
        -----END PRIVATE KEY-----
        """;

        String publicKeyPEM = """
        -----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvcnAzEJgN3tcyed3SayO
        o+2go3LrYxuNRWiYFwkDPMmcpgP4cyeasY7XjWr7dUPlwjmUf5DfG0UeasQlVu16
        c/Ab8Qkj4ypopJ4hXwZUb45KVynHjcNsZVAC4fDOFBWCKSn2MJs28N4D5tZnvY9Z
        BQIQLMkyc0tUGl6VbSb+EOP5mZuK5lZ9yBAZioQl49rXTHQpvzz1AISmperUpsU9
        oYJch5/sWS2+rkinI6cC3LZkGndSl5ruUQ1FAyLhd71JX++zVuP6EayByGnwKSg8
        7RqnaSKSzPnVq4bClWE+hNlVrkuSlh1VtBXIK14qVpyNXyW01I075kbLm7zDr9LW
        /wIDAQAB
        -----END PUBLIC KEY-----
        """;

        String payload = "{\"id\":\"123\",\"hello\":\"world\",\"time\":1723976484,\"tag\":\"UPay Encryption and decryption\"}";

        // No Key
        JWECrypto jweCrypto = new JWECrypto(null, null);
        System.out.println("Private Key: " + jweCrypto.privateKey);
        System.out.println("Public Key: " + jweCrypto.publicKey);

        String encryptToken = jweCrypto.encrypt(payload);
        System.out.println("Encrypted Token: " + encryptToken);
        String decryptedPayload = jweCrypto.decrypt(encryptToken);
        System.out.println("Decrypted Payload: " + decryptedPayload);

        // Just public Key, only Sign
        jweCrypto = new JWECrypto(publicKeyPEM, null);
        encryptToken = jweCrypto.encrypt(payload);
        System.out.println("Encrypted Token with Public Key: " + encryptToken);

        // Just private key, only design
        jweCrypto = new JWECrypto(null, privateKeyPEM);
        // design last encrypt token
        decryptedPayload = jweCrypto.decrypt(encryptToken);
        System.out.println("Decrypted Payload with Private Key: " + decryptedPayload);

        // have both of key of rsa
        jweCrypto = new JWECrypto(publicKeyPEM, privateKeyPEM);
        encryptToken = jweCrypto.encrypt(payload);
        System.out.println("Encrypted Token with Both Keys: " + encryptToken);
        decryptedPayload = jweCrypto.decrypt(encryptToken);
        System.out.println("Decrypted Payload with Both Keys: " + decryptedPayload);
    }
}

Demo Key

KeyValue
CUSTOMERID1111111111
APIKEY51d6dc09-e641-4162-a9e9-43fd0f3cf903
SECRETKEY0a227672c20f48989b5fcdb4c00038ff
PublicKey-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvcnAzEJgN3tcyed3SayO o+2go3LrYxuNRWiYFwkDPMmcpgP4cyeasY7XjWr7dUPlwjmUf5DfG0UeasQlVu16 c/Ab8Qkj4ypopJ4hXwZUb45KVynHjcNsZVAC4fDOFBWCKSn2MJs28N4D5tZnvY9Z BQIQLMkyc0tUGl6VbSb+EOP5mZuK5lZ9yBAZioQl49rXTHQpvzz1AISmperUpsU9 oYJch5/sWS2+rkinI6cC3LZkGndSl5ruUQ1FAyLhd71JX++zVuP6EayByGnwKSg8 7RqnaSKSzPnVq4bClWE+hNlVrkuSlh1VtBXIK14qVpyNXyW01I075kbLm7zDr9LW /wIDAQAB -----END PUBLIC KEY-----
PrivateKey-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9ycDMQmA3e1zJ 53dJrI6j7aCjcutjG41FaJgXCQM8yZymA/hzJ5qxjteNavt1Q+XCOZR/kN8bRR5q xCVW7Xpz8BvxCSPjKmikniFfBlRvjkpXKceNw2xlUALh8M4UFYIpKfYwmzbw3gPm 1me9j1kFAhAsyTJzS1QaXpVtJv4Q4/mZm4rmVn3IEBmKhCXj2tdMdCm/PPUAhKal 6tSmxT2hglyHn+xZLb6uSKcjpwLctmQad1KXmu5RDUUDIuF3vUlf77NW4/oRrIHI afApKDztGqdpIpLM+dWrhsKVYT6E2VWuS5KWHVW0FcgrXipWnI1fJbTUjTvmRsub vMOv0tb/AgMBAAECggEABT4L7yMhkOwaCKe191IRZmBbe2obnaFhXcVIWxhQIxSM mQIH2M0f3g2awvxQ6tgIrNkLUuEwnrUM5IptQyvwBV1EQ9/ql7DXeeiZ2X24GlwV /7dd49ATRUR76a14USBCvxo6kdglEsDAGA/43CxtVB8HopJlummsp3C0LSv18sBx h8/TtUa0ttG8UodTtBdAbIwolb55lu92XV0mGHpOc9ZsgLlyn2C7o+jlujNqOhi3 YxKCkhL2jzl88qZ4mmRPGPYQQwTo1Xzd4brixwZ1Fyag2kXziE6+v9PX8s+yFsfW 3E/0ye16tfa0/qAx/4oGr9Ox4e7wXLLiN2QpR8UkEQKBgQD4W1r9y3/uKPDne/UR srt0oZQOG9OqouVNgIwzZYw8WG6fu9h+RuihRpLLLqBwCDCiPo17xyHkPufonntJ 4FkfzvL9RV2/d7FERIOPy9umsoFhv1cqD94zlf96hNj5EK1VZpHGhhZ1MCuiRr8W J/tPiCMMSOxVus3SRPw9M/IebwKBgQDDoPjR1WJb1i23uCQm9uvKXl3AgJ9ltfHA JuRR2SO5XxOkZ5HxG+QSAkd9Og4kQAN6MQ9HMh1sR8xa75ck9G5YgmAGBtzRr68L RpcWvT1MNN81hkOrq3pvkrLkepSyz174EIV3m3aTOXaqSe0TOtPN/mU57NxWgyFz QyrubvcYcQKBgQCi2VxfwHlhU+0rDIfUlAsA3hYz7iEr6WZMHHdSGEsNIrte+BBs NNjDL4B/xFIlQ+mH6VZijF93x5vPV2PmPDqUdeG1Gy/upXBSIE7YEkc0FiZqTsm7 5e3Ai6Ga19Nh9YKC1h/OOgwtyWlDHfqcb5kFPHIm0wZM7JGbR+rRwucG9wKBgFc7 JRgz5Rr8QwCD/KLfQb7IG1fmQq1Q1w6OiQqyH0reSVzqzdnMTQN2vrfpNjsVzDcl PyHJB/OrhEUcRrI0yCtxXy3wy3A4vX3yqRZ7paSggtNUHkKjQQIVUO2udzXQYBGP FGSEDviRDAfc1PjZJ27YO/z7UDjHWhgSYEdXdZkRAoGAPpHataBL9NOHZG4zSmCZ jw9yXHTe9xuZl2mkYFgMVzcoGvMjS4PFWDncpiDaVef9rvkY1SlmkMjejOb4KxyI eUkVcbUjvHQ786s4Ul+kdPJehx5B9Av0QhE1D1TVXPUxkx5Xa7usYW3HR0CmeN/1 IFK8O6Hrcn4W90kIgq86hng= -----END PRIVATE KEY-----