Security
How to make safe issuing card requests.
Request Signature
1、Request header
Content-Type*
string
application/json; chartset=UTF-8
2、Request body
{
"app_id": "2017051914172236111",
"partner_id": "000000000000001",
"sub_partner_id": "",
"timestamp": "2011-09-23 04:24:03",
"version": "1.0",
"random_key": "OvUp0Z7zhLfYdBR1lOK", //need to be encrypted
"request_id": "jJT3+PR9CSowcR2QOK5OzMjVmTSjGoectzYbO8lENHnboElE",
"sign_type": "RSA",
"biz_data": {} //need to be encrypted
}
3、Signature
randomKey:
This is encrypted by Pagsmile public key.
Step 1 : Randomly generate a 16-byte AES KEY;
Step 2 : Encrypt AES KEY with Pagsmile RSA public key;
Find the Pagsmile RSA public key in the config of the app under the App Management section of the Pagsmile Test Dashboard.
*The RSA key pairs are 2048 bits in length.
biz_Data:
Encrypt using the AES KEY generated in Step 1.
Response Signature
1、Response body
{
"code": "10000",
"msg": "Success",
"data": { }, //need to be decrypted
"total":1
"random_key":"OvUp0Z7zhLfYdBR1lOK/F1fE0jKGiwsIea9XD9urkav",//need to be decrypted
"sign_type": "RSA",
"app_id": "2017051914172236111"
}
2、Signature
random_key:
It needs to be decrypted by Partner private key
data:
It needs to be decrypted by randomly generated AES KEY
Example
public class CryptoUtil {
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static IvParameterSpec generateIvValue() {
return new IvParameterSpec(generateRandom16Chars().getBytes());
}
private static String generateRandom16Chars() {
byte[] array = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(array);
return bytesToHex(array);
}
private static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
}
public class CryptoExample {
//encrypt random_key
public static String encryptSecret(PublicKey publicKey, String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // PKCS1 or OAEP padding "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" can be used
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedSign = cipher.doFinal(secret.getBytes());
return Base64.getEncoder().encodeToString(encryptedSign);
}
//encrypt biz_data
public static String encryptContentMode(String content, String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
IvParameterSpec iv = CryptoUtil.generateIvValue();
SecretKey secretKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher dataCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dataCipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] encryptedDataBytes = dataCipher.doFinal(content.getBytes());
return new String(iv.getIV()) + Base64.getEncoder().encodeToString(encryptedDataBytes);
}
//decrypt random_key
public static String decryptSecretMode(PrivateKey privateKey, String sign) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] encryptedDataBytes = Base64.getDecoder().decode(sign);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // PKCS1 or OAEP padding "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" can be used
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] secretByte = cipher.doFinal(encryptedDataBytes);
return new String(secretByte);
}
//decrypt data
public static String decryptContentMode(String encryptedContent, String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String ivString = encryptedContent.substring(0, 16);
String correctEncryptedData = encryptedContent.substring(16);
byte[] encryptedDataBytes = Base64.getDecoder().decode(correctEncryptedData);
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes());
SecretKey secretKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher dataCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dataCipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedDataBytes = dataCipher.doFinal(encryptedDataBytes);
return new String(decryptedDataBytes);
}
}
Encryption algorithm in PHP-encrypt with RSA Public Key
<?php
// public key
$publicKey = 'public key';
function encryptSecret(string $secret, string $publicKey): string
{
openssl_public_encrypt($secret, $encryptedData, $publicKey, OPENSSL_PKCS1_PADDING);
return base64_encode($encryptedData);
}
function encryptContent(string $content, string $secret): string
{
$iv = bin2hex(random_bytes(8));
$encryptedData = openssl_encrypt($content, 'aes-256-cbc', $secret, OPENSSL_RAW_DATA, $iv);
return $iv.base64_encode($encryptedData);
}
// usage
$secret = 'RandomString32CharactersLength12'; // 32 characters; must be randomly generated for every response
$random_key = encryptSecret($secret, $publicKey); // add to 'random_key'
$biz_data = '{"responseCode":"00","billingAmount":"11186","holderAmount":"11186","availableBalance":100008814,"settledBalance":100020000}';
$encryptedContent = encryptContent($content, $secret); // set this to be the response body
// Optional outputs that show the result
echo $sign.PHP_EOL;
echo $encryptedContent.PHP_EOL;
Decryption algorithm in PHP
<?php
// Client private key
$privateKey = 'insert your private key here';
$encryptedContent = 'raw response body'; // Get from the webhook body
function decryptSecret(string $sign, string $privateKey): string
{
// OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING
openssl_private_decrypt(base64_decode($sign), $secret, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
return $secret;
}
function decryptContent(string $encryptedContent, string $secret): string
{
$iv = substr($encryptedContent, 0, 16);
$encryptedData = base64_decode(substr($encryptedContent, 16));
return openssl_decrypt($encryptedData, 'aes-256-cbc', $secret, OPENSSL_RAW_DATA, $iv);
}
// usage
$decryptedSecret = decryptSecret($sign, $privateKey);
$decryptedContent = decryptContent($encryptedContent, $decryptedSecret);
// Optional output that shows the result
echo $decryptedContent.PHP_EOL;
Encryption algorithm in C# (MODE 2 - encrypt with RSA public key)
// ISAC public key
string publicKey = "insert ISAC public key here";
static string EncryptSecret(string secret, string publicKey)
{
// OaepEncoding or Pkcs1Encoding
var encryptEngine = new OaepEncoding(new RsaEngine());
using (var reader = new StringReader(publicKey))
{
encryptEngine.Init(true, (AsymmetricKeyParameter)new PemReader(reader).ReadObject());
}
var data = Encoding.UTF8.GetBytes(secret);
var encryptedData = encryptEngine.ProcessBlock(data, 0, data.Length);
return Convert.ToBase64String(encryptedData);
}
static string EncryptContent(string content, string secret)
{
var iv = Hex.Encode(new SecureRandom().GenerateSeed(8));
var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7PADDING");
var keyParam = new KeyParameter(Encoding.UTF8.GetBytes(secret));
var keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);
cipher.Init(true, keyParamWithIv);
var encryptedData = cipher.DoFinal(Encoding.UTF8.GetBytes(content));
return Encoding.UTF8.GetString(iv) + Convert.ToBase64String(encryptedData);
}
string secret = "RandomString32CharactersLength12"; // 32 characters; must be randomly generated for every response
string sign = EncryptSecret(secret, publicKey); //
string bizData = "{"responseCode":"00","billingAmount":"11186","holderAmount":"11186","availableBalance":100008814,"settledBalance":100020000}";
string encryptedContent = EncryptContent(content, secret); // set this to be the response body
// Optional outputs that show the result
Console.WriteLine(sign);
Console.WriteLine(encryptedContent);
Decryption algorithm in C#
// Client private key
string privateKey = "Client private key";
string sign = "response"; // Get from response 'random_Key'
string encryptedContent = "raw response body"; // Get from Get from response 'data'
static string DecryptSecret(string sign, string privateKey)
{
// OaepEncoding or Pkcs1Encoding
var decryptEngine = new OaepEncoding(new RsaEngine());
using (var reader = new StringReader(privateKey))
{
decryptEngine.Init(false, (AsymmetricKeyParameter)new PemReader(reader).ReadObject());
}
var data = Convert.FromBase64String(sign);
var decryptedData = decryptEngine.ProcessBlock(data, 0, data.Length);
return Encoding.UTF8.GetString(decryptedData);
}
static string DecryptContent(string encryptedContent, string secret)
{
var iv = Encoding.UTF8.GetBytes(encryptedContent.Substring(0, 16));
var encryptedData = Convert.FromBase64String(encryptedContent.Substring(16));
var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7PADDING");
var keyParam = new KeyParameter(Encoding.UTF8.GetBytes(secret));
var keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);
cipher.Init(false, keyParamWithIv);
var data = cipher.DoFinal(encryptedData);
return Encoding.UTF8.GetString(data);
}
// usage
string decryptedSecret = DecryptSecret(sign, privateKey);
string decryptedContent = DecryptContent(encryptedContent, decryptedSecret);
// Optional output that shows the result
Console.WriteLine(decryptedContent);
Encryption algorithm in Node.js (MODE 2 - encrypt with RSA public key)
// Import necessary modules
const crypto = require('crypto');
// ISAC public key
const publicKey = `insert public key here`;
function encryptSecret(secret, publicKey) {
const encryptedData = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(secret));
return encryptedData.toString('base64');
}
function encryptContent(content, secret) {
const iv = crypto.randomBytes(8).toString('hex');
const cipher = crypto.createCipheriv('aes-256-cbc', secret, iv);
let encryptedData = cipher.update(content, 'utf8', 'base64');
encryptedData += cipher.final('base64');
return iv + encryptedData;
}
// usage
const secret = 'RandomString32CharactersLength12'; // 32 characters; must be randomly generated for every response to a webhook
const sign = encryptSecret(secret, publicKey); // add to 'random_key'
const content = `{"responseCode":"00","billingAmount":"11186","holderAmount":"11186","availableBalance":100008814,"settledBalance":100020000}`;
const encryptedContent = encryptContent(content, secret); // add to 'biz_data'
console.log(sign);
console.log(encryptedContent);
Decryption algorithm in Node.js
// Import necessary modules
const crypto = require('crypto');
// private key
const publicKey = `insert your private key here`;
const sign = ``; // Get from response as 'random_key'
const encryptedContent = `raw response body`; // Get from response as 'data'
function decryptSecret(sign, privateKey) {
// RSA_PKCS1_OAEP_PADDING or RSA_PKCS1_PADDING
const decryptedData = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(sign, 'base64'));
return decryptedData.toString();
}
function decryptContent(encryptedContent, secret) {
const iv = encryptedContent.slice(0, 16);
const encryptedData = Buffer.from(encryptedContent.slice(16), 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', secret, iv);
decipher.setAutoPadding(false);
let decryptedData = decipher.update(encryptedData);
decryptedData = Buffer.concat([decryptedData, decipher.final()]);
return decryptedData.toString();
}
// usage
decryptedSecret = decryptSecret(sign, privateKey);
decryptedContent = decryptContent(encryptedContent, decryptedSecret);
console.log(decryptedContent);
Last updated