Encryption, decryption, and signature verification
When integrating with OSL Pay, various operations involving encryption, decryption, signing, and signature verification may occur. The scenarios below cover all cases to help you integrate quickly. Upon integrating with OSL Pay, you will receive 2 keys: 1. A private key for interacting with OSL Pay’s OpenAPI. 2. A public key for verifying signatures from our Webhooks or for encrypting data in special scenarios
Signature generation when calling OpenAPI
When making requests to OSL Pay’s OpenAPI, you need to include 3 parameters in the request header:
Parameter | Description | Type | Required | Example |
---|---|---|---|---|
appId | The unique app ID assigned to your merchant account | string | Yes | me114503923793922 |
timestamp | Request timestamp | string | Yes | 1755215371269 |
signature | Request header signature | string | Yes | eHr5nFDrxBBBYkzGUi5YpCF7b3i0sny63PM4cjiskww4MTRJ2gxzIR1eWRrMdMFNe/JHpTiyD3aMBM+Xz3R3PO3CzzA5aT/1p+7z9IC0c/Gthlk9I2HNmOU9Raj4uMFGEo0sgqz5mFUH/MUkRz3M56WWd98Ie8DiDcEeAFi9Ix8= |
- Construct the signature string
The signature string (the data to sign) is built as: appId=[appId from header]×tamp=[timestamp from header]
For example, if appId
= me114503923793922
and timestamp
= 1755215371269
, the string to sign is:
appId=me114503923793922×tamp=1755215371269
- Construct the signature string
Use your private key to encrypt the signature string and generate the final signature.
Refer to the following code example:
RSAUtils.sign(String sourceData, String privateKey)
This will produce the signature
value to include in the request header.
Data encryption and signature verification
OpenAPI request encryption
For certain sensitive fields, you must encrypt data when calling the following OpenAPI endpoints:
-
Change bound email: /api/v1/user/modify-bind-email
-
Account creation: /api/v1/user/kyc/share
Encryption key: Use the Webhook public key. Encryption method: Refer to the code example: RSAUtils.encryptByPublicKey(content, publicKey)
Webhook decryption
If you are integrating as a Web3 merchant, the email
field in account information will be pushed via Webhook. You must decrypt this field using your private key: RSAUtils.decryptByPrivateKey(encryptStr, privateKey)
Webhook signature verification
After receiving a Webhook push, you must verify its signature. The signature string example: "appId=me114702259781634×tamp=1756802303227"
Use the Webhook public key issued by OSL Pay to verify the signature:
RSAUtils.verify(String sourceData, String publicKey, String sign)
Signature generation for SDK integration
When integrating via the Web SDK, a signature
parameter is required in the redirect URL. The signature string depends on your merchant type:
Web3 merchants:
Signature string (%s as placeholder):
With blockchain address:
appId=%s&merchantUser=%s&address=%s
Without blockchain address: appId=%s&merchantUser=%s
Standard merchants:
Signature string:
appId=%s&address=%s
Signature method (reference code): RSAUtils.sign(String sourceData, String privateKey)
Sample code
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.tuple.Pair;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
public class RSAUtils {
/**
* generate public and private key
*
* @return
* @throws Exception
*/
public static Pair<String, String> getKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
KeyPair keyPair = generator.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
return Pair.of(publicKeyString, privateKeyString);
}
/**
* transfrom private key string to PrivateKey
*
* @param privateKey
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
/**
* transfrom public key string to PublicKey
*
* @param publicKey
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
/**
* sign
*
* @param sourceData
* @param privateKey
* @return
* @throws Exception
*/
public static String sign(String sourceData, PrivateKey privateKey) throws Exception {
byte[] keyBytes = privateKey.getEncoded();
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(sourceData.getBytes());
return new String(Base64.encodeBase64(signature.sign()));
}
/**
* verify signature
*
* @param sourceData
* @param publicKey
* @param sign
* @return
* @throws Exception
*/
public static boolean verify(String sourceData, PublicKey publicKey, String sign) throws Exception {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(key);
signature.update(sourceData.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
}
/**
* sign
*
* @param sourceData
* @param privateKey
* @return
* @throws Exception
*/
public static String sign(String sourceData, String privateKey) throws Exception {
return sign(sourceData, getPrivateKey(privateKey));
}
public static boolean verify(String sourceData, String publicKey, String sign) throws Exception {
return verify(sourceData, getPublicKey(publicKey), sign);
}
private static String decrypt(String cryptograph, Key key) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(2, key);
byte[] bytes = Base64.decodeBase64(cryptograph.getBytes());
int inputLen = bytes.length;
int offLen = 0;
int i = 0;
ByteArrayOutputStream byteArrayOutputStream;
for(byteArrayOutputStream = new ByteArrayOutputStream(); inputLen - offLen > 0; offLen = 128 * i) {
byte[] cache;
if (inputLen - offLen > 128) {
cache = cipher.doFinal(bytes, offLen, 128);
} else {
cache = cipher.doFinal(bytes, offLen, inputLen - offLen);
}
byteArrayOutputStream.write(cache);
++i;
}
byteArrayOutputStream.close();
byte[] byteArray = byteArrayOutputStream.toByteArray();
return new String(byteArray);
}
public static String encryptByPublicKey(String source, String publicKey) throws Exception {
Key key = getPublicKey(publicKey);
return encrypt(source, key);
}
private static String encrypt(String source, Key key) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(1, key);
byte[] b = source.getBytes();
int MAX_ENCRYPT_BLOCK = 117;
int offSet = 0;
byte[] resultBytes = new byte[0];
byte[] cache = new byte[0];
int inputLength = b.length;
while(inputLength - offSet > 0) {
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(b, offSet, MAX_ENCRYPT_BLOCK);
offSet += MAX_ENCRYPT_BLOCK;
} else {
cache = cipher.doFinal(b, offSet, inputLength - offSet);
offSet = inputLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
}
return new String(Base64.encodeBase64(resultBytes), "UTF-8");
}
public static String decryptByPrivateKey(String cryptograph, String privateKey) throws Exception {
Key key = getPrivateKey(privateKey);
return decrypt(cryptograph, key);
}
public static void main(String[] args) throws Exception {
//加解密示例
String content = "hello & @ zz.com";
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQRcqgOAS0QzOkCPLseExAXb7LPQdA6z59sXufmHcBLnkgt19WVXB5/N2LvrfOTsePKKBqJheKQbTEMlLEqGiv5YlGdNZxcnxgy0J8Az1Ja15DOeGLz9AHcPzNydBRzysbZ+RDFg1nzI8BbKvNrvDNAE7uLeEQT65UU/bphte1TwIDAQAB";
String privateKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANBFyqA4BLRDM6QI8ux4TEBdvss9B0DrPn2xe5+YdwEueSC3X1ZVcHn83Yu+t85Ox48ooGomF4pBtMQyUsSoaK/liUZ01nFyfGDLQnwDPUlrXkM54YvP0Adw/M3J0FHPKxtn5EMWDWfMjwFsq82u8M0ATu4t4RBPrlRT9umG17VPAgMBAAECgYEAuL4/p3EpUxENPKMngHSRBsNhG3yt83r4Opx1kTK31Rd5IHkoGze8hqiziGMLPfiJccZSzPWvCdeQ1jvhSUotWMffhJ3mSP4Lp/a2pVNt2EwbdnHCou2qFOXUXB3iwF+TcxEfnSOFo7Xb0Ra1QeJznUyCALGf8i5D1YQIC4ZG7KECQQDr/L61oEMMlYHcJGYGKWCqyXNGXCeRJDSivJpktJvfYf5gvaeH44B9j/yx+sncO99S+kI5PHNNYAmO6Pe209Y/AkEA4e9dWKeIZ1op94xEI5QaGaxrB+Il4fyQDys+apQF3L6rXbTzlsheltp0b3vPsGL1iqH5KJBteX8FGXdn/jv88QJBAIM4pWslVGM4917MNpcShxgwsJLdR3sjoMklCs0YyNvsB6EPlIqxbI9lc2QcSgbWnBXt5skg3hSWZHo8/RGCtc0CQDFdKikHma7zLT4wAGdBoZ5AZLY5PAuvMg6OZTnGE63SXwC/W4VzS+9r1YrSI2Oni3x5vUWWoMSt8wBIbYlHtBECQQCii8u+HGZqwej+a6/koc46ih1ZA4tS1x5UWLE4nGfTEdVgcoEmM0ZTw2FzPURfXWeOrQMyfa2y5ituwrDyBqqf";
String encryptStr = RSAUtils.encryptByPublicKey(content,publicKey);
System.out.println(encryptStr);
System.out.println(RSAUtils.decryptByPrivateKey(encryptStr,privateKey));
//加签,验签示例
String signContent = "appId=me114702259781634×tamp=1756802303227";
String sign = RSAUtils.sign(signContent,privateKey);
System.out.println(sign);
System.out.println(RSAUtils.verify(signContent,publicKey,sign));
}
}
API request headers
All OpenAPI requests must include the following 3 parameters in the request header to identify the merchant and enable signature verification.
-
Request headers
Parameter Description Type Required Example appId The unique app ID assigned to your merchant account string Yes me114503923793922 timestamp Request timestamp string Yes 1755215371269 signature Request header signature string Yes eHr5nFDrxBBBYkzGUi5YpCF7b3i0sny63PM4cjiskww4MTRJ2gxzIR1eWRrMdMFNe/JHpTiyD3aMBM+Xz3R3PO3CzzA5aT/1p+7z9IC0c/Gthlk9I2HNmOU9Raj4uMFGEo0sgqz5mFUH/MUkRz3M56WWd98Ie8DiDcEeAFi9Ix8
Updated 3 days ago