Welcome to CipherBC API
Preparing for API
1 Register APP_ID
Contact customer service to register for APP_ID,and provide relevant qualification information. Example:Company Name,domain name,IP, first person in charge and other business information needed。
2 Provide a list of main chains and contract currencies that needs to be supported.
Coin require basic information such as coin address、main chain、precision、name and other relevant information will be provided to business coordinator.
3 Debugging API test environment
API adopting RSA2048bit for two-way signature verification to ensures the credibility of communication. Merchants are required to give the public key to the platform, where platform uses it to verify request parameters sent to the platform. Merchants need to obtain the platform public key in advance, which can be used to verify platform response.
Supported Coin
Signature Regulations
<?php
$obj = new obj();
$obj->coin_list();
class obj
{
public $baseUrl = 'http://api.xxxxx.com/shopapi';
public $priKey = "-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8d6RGCeQkrobx
+4xO1jXnHwiMU48UyOaRaNW2FMQrN9DcK/T2h58FyuA7kYN0CVNFEnaZQS9S8Nur
Tl0lOVJcbwKfCSnu9nmFYDvPWqlCTqQW3F83sKNi9IWrMFFny3CX35eUHpeJahE2
R9wma2oJIFJv3ux/I3+w8fQQ6zMPIqpDx4Tcu9sCVpot+/siWJDpyiQBlbXfcxuH
YOePUGx+tJ5HLXtJOUtPcgyhFTXRmW9r17DnnsjrT5Vc50IbN//JXbvr96eexQ/e
/IXHZP6MZWdthjuv9y2TaxS+JMnpKLrXfgazoQEsKmchAsTLEyX6tHG5tHq+3MaK
BSTFCoHHAgMBAAECggEAYodR1GPbZ+nuFpfhlF2ctIWe3G3awYz+LUrXX2ef3tBB
WrmISVJBqq+TZtD+Hi254MR0SE6xxPSygu5m98zt5/EJN8d8qrckdLwIyLCnCElA
s8rl5KFt6Y/YSdTq2g6bgGqePgt4QZi4At6yk0tYlgjV0DKFBTqzyPfVFvs/KEuZ
o8le0XjgJA+Rnti35Gi5UDL1Tn9LcmCU/JcdpksFgPvU/wPmlNb8azM68ougXcrf
hBebSRWbS0F2OXRJd7+hrTq+pwTNIH+IyzsUZHQCcH0tFe2Q304oDuVuwX5VxB3S
tYG8+LoECW1nKalJbolkmdRqVxZRXG/Ho8/aRCHBcQKBgQDBmoZzUaKU2F4Sa6P+
5vCQ5RAAiAATWao/WYfL9BBVnLzbkbzget3BNdJcJZKp1Avh6F/jeB0xUOD0aFgm
rJAZjVAFu77damKWwiEPxKfdgSbBedV/KWCMASzr4m4cdYKOcmlLFsh+Du2+oZW5
3Mn35hyfbTf1OyYxrvOVrhSu+wKBgQD5NVfxWTBsugAE++d4CCy8TUL7QCqb1I1v
EQ0FNdZsmXslPbCFlJ5UCFJXAF6s2x0MDKCl7y2ixlDo31H7DI6xlOP5kjYon7wr
mB0U2bclGtlJ3FvCwDBFkfZykYZqPyey8A3D+kf4pnvZvn/0BgKfWCVSYnqEB4mp
BI5WhywOpQKBgH4Mio8YTn8UZCMgG/UBDQ15ZbLC2THABj0aoHRkoiHrW3ala1DI
DgsWbLB78gJKQbZCofOqp28NVnkqWoJLhcJtI/Wnp/vmoOvA1t+6TQTFaqM7HIqf
OPHObh3iHYi/5VGzIeS2n7CbLaUY9S++lWymUGpoJK+wZAyi+IqyfVrzAoGAPKmI
g75zNDQ+pvfuy2AqC5g/Esv200H1P7EF21vUm1DtEg6nNo0L0WbJLFZxtWAM/Q7h
2CKVmEbwEQ+zdAoFfiLBL9Iwjsj7VB1ODvJES6ryc7FJQOnpljXGqPLVpm1DK6KV
pzv4YwWbplBh8zO3VbRuOVSxgAwZrWPnhhaXWeUCgYEAwVHYXN1qGTBS1uyrldDr
OMuXyfvE7lbfJlg2EfG2A7PtTooPkh1IbRjlJ8ACbiadM0pYhnGGPk0D1WSKvRxU
SmSc6PgW2weANQ8QCemo2V/zZ0DlWFZDdCV+7jMbAFBhQHcVh0qUr+uNyntjLTjj
OPdMSnhKLoKtleu+NiMIogc=
-----END PRIVATE KEY-----";
public $pubKey = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7K9s+r82lofBFaj3KWp
o/5E1ryDPDlLGZ4Rv68DcoCxV/bv0IvYX3cleC0WvTpwbdTpGtLjS6RHj89JREhi
svv5qmk5YTfjEqd3RmtYDmOVz2Ry6E76wLh9qi1pqpneMGr3shvEzZC6gkieXRCA
MOlEYbE7TSzxxDJxmovdeElowdRZcvwwPLvh/BLmCn+H1MXfdx01c/eFpTvu8BZG
1m7AqDOqnaPcR1ru/ekJoIwotF1jCPM2E5kAbQQutNaeSXhLbkfom4bxqF2/ni6k
Cwnz3fbBYcXP3Ww3/AdI1dn0iLr6450qn72yBpDtQY8OZ7+Krff4Fa5jOBY7OHEN
FwIDAQAB
-----END PUBLIC KEY-----";
public function coin_list()
{
$data = $this->get_public_data();
$data["chain"] = "eth";
$data["coin"] = "eth";
$data['sign'] = Signature::encryption($data, $this->priKey);
$res = self::curlByPost($this->baseUrl . '/coin/list', $data);
print_r($res);
// verification
$resp = json_decode($res, true);
if (Signature::checkSignature($resp["sign"], $resp, $this->pubKey)) {
print_r("verification successful");
} else {
print_r("verification failed");
}
}
public static function curlByPost($url, $data = null, $timeout = 60)
{
$SSL = substr($url, 0, 8) == "https://" ? TRUE : FALSE;
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_URL, $url);
if ($SSL) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
}
$data = json_encode($data);
$headers = [
'Content-Type: application/json; charset=utf-8',
'Content-Length:' . strlen($data)
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output = curl_exec($ch);
return $output;
}
public function get_public_data()
{
$data['app_id'] = "xxxxxx";
$data['version'] = '1.0';
$data['key_version'] = 'admin';
$data['time'] = time();
return $data;
}
}
class Signature
{
/**
* Private key signing
*
* @param $data
* @param $private_key
*/
public static function encryption($data, $private_key)
{
if (is_array($data))
$signString = self::getSignString($data);
else
$signString = $data;
$privKeyId = openssl_pkey_get_private($private_key);
$signature = '';
openssl_sign($signString, $signature, $privKeyId, OPENSSL_ALGO_MD5);
openssl_free_key($privKeyId);
return base64_encode($signature);
}
/**
* Use the other party's public key to verify the signature and check whether the signature matches
*
* @param $sign
* @param $data
* @param $public_key
* @return bool
*/
public static function checkSignature($sign, $data, $public_key)
{
$toSign = self::getSignString($data);
$publicKeyId = openssl_pkey_get_public($public_key);
$result = openssl_verify($toSign, base64_decode($sign), $publicKeyId, OPENSSL_ALGO_MD5);
openssl_free_key($publicKeyId);
return $result === 1 ? true : false;
}
public static function getSignString($data)
{
unset($data['sign']);
ksort($data);
reset($data);
$pairs = array();
foreach ($data as $k => $v) {
if (is_array($v)) $v = self::arrayToString($v);
$pairs[] = "$k=$v";
}
return implode('&', $pairs);
}
private static function arrayToString($data)
{
$str = '';
foreach ($data as $list) {
if (is_array($list)) {
$str .= self::arrayToString($list);
} else {
$str .= $list;
}
}
return $str;
}
}
package main
import (
"crypto"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"github.com/imroc/req"
"github.com/tidwall/gjson"
"sort"
"time"
)
var (
BaseUrl = "http://api.xxxxx.com/shopapi"
signPrivateKey = -----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8d6RGCeQkrobx
+4xO1jXnHwiMU48UyOaRaNW2FMQrN9DcK/T2h58FyuA7kYN0CVNFEnaZQS9S8Nur
Tl0lOVJcbwKfCSnu9nmFYDvPWqlCTqQW3F83sKNi9IWrMFFny3CX35eUHpeJahE2
R9wma2oJIFJv3ux/I3+w8fQQ6zMPIqpDx4Tcu9sCVpot+/siWJDpyiQBlbXfcxuH
YOePUGx+tJ5HLXtJOUtPcgyhFTXRmW9r17DnnsjrT5Vc50IbN//JXbvr96eexQ/e
/IXHZP6MZWdthjuv9y2TaxS+JMnpKLrXfgazoQEsKmchAsTLEyX6tHG5tHq+3MaK
BSTFCoHHAgMBAAECggEAYodR1GPbZ+nuFpfhlF2ctIWe3G3awYz+LUrXX2ef3tBB
WrmISVJBqq+TZtD+Hi254MR0SE6xxPSygu5m98zt5/EJN8d8qrckdLwIyLCnCElA
s8rl5KFt6Y/YSdTq2g6bgGqePgt4QZi4At6yk0tYlgjV0DKFBTqzyPfVFvs/KEuZ
o8le0XjgJA+Rnti35Gi5UDL1Tn9LcmCU/JcdpksFgPvU/wPmlNb8azM68ougXcrf
hBebSRWbS0F2OXRJd7+hrTq+pwTNIH+IyzsUZHQCcH0tFe2Q304oDuVuwX5VxB3S
tYG8+LoECW1nKalJbolkmdRqVxZRXG/Ho8/aRCHBcQKBgQDBmoZzUaKU2F4Sa6P+
5vCQ5RAAiAATWao/WYfL9BBVnLzbkbzget3BNdJcJZKp1Avh6F/jeB0xUOD0aFgm
rJAZjVAFu77damKWwiEPxKfdgSbBedV/KWCMASzr4m4cdYKOcmlLFsh+Du2+oZW5
3Mn35hyfbTf1OyYxrvOVrhSu+wKBgQD5NVfxWTBsugAE++d4CCy8TUL7QCqb1I1v
EQ0FNdZsmXslPbCFlJ5UCFJXAF6s2x0MDKCl7y2ixlDo31H7DI6xlOP5kjYon7wr
mB0U2bclGtlJ3FvCwDBFkfZykYZqPyey8A3D+kf4pnvZvn/0BgKfWCVSYnqEB4mp
BI5WhywOpQKBgH4Mio8YTn8UZCMgG/UBDQ15ZbLC2THABj0aoHRkoiHrW3ala1DI
DgsWbLB78gJKQbZCofOqp28NVnkqWoJLhcJtI/Wnp/vmoOvA1t+6TQTFaqM7HIqf
OPHObh3iHYi/5VGzIeS2n7CbLaUY9S++lWymUGpoJK+wZAyi+IqyfVrzAoGAPKmI
g75zNDQ+pvfuy2AqC5g/Esv200H1P7EF21vUm1DtEg6nNo0L0WbJLFZxtWAM/Q7h
2CKVmEbwEQ+zdAoFfiLBL9Iwjsj7VB1ODvJES6ryc7FJQOnpljXGqPLVpm1DK6KV
pzv4YwWbplBh8zO3VbRuOVSxgAwZrWPnhhaXWeUCgYEAwVHYXN1qGTBS1uyrldDr
OMuXyfvE7lbfJlg2EfG2A7PtTooPkh1IbRjlJ8ACbiadM0pYhnGGPk0D1WSKvRxU
SmSc6PgW2weANQ8QCemo2V/zZ0DlWFZDdCV+7jMbAFBhQHcVh0qUr+uNyntjLTjj
OPdMSnhKLoKtleu+NiMIogc=
-----END PRIVATE KEY-----
signPublicKey = -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7K9s+r82lofBFaj3KWp
o/5E1ryDPDlLGZ4Rv68DcoCxV/bv0IvYX3cleC0WvTpwbdTpGtLjS6RHj89JREhi
svv5qmk5YTfjEqd3RmtYDmOVz2Ry6E76wLh9qi1pqpneMGr3shvEzZC6gkieXRCA
MOlEYbE7TSzxxDJxmovdeElowdRZcvwwPLvh/BLmCn+H1MXfdx01c/eFpTvu8BZG
1m7AqDOqnaPcR1ru/ekJoIwotF1jCPM2E5kAbQQutNaeSXhLbkfom4bxqF2/ni6k
Cwnz3fbBYcXP3Ww3/AdI1dn0iLr6450qn72yBpDtQY8OZ7+Krff4Fa5jOBY7OHEN
FwIDAQAB
-----END PUBLIC KEY-----
)
func main() {
pub := make(map[string]string)
// Open Parameter
pub["app_id"] = "xxxxxxxxx"
pub["version"] = "1.0"
pub["key_version"] = "admin"
pub["time"] = fmt.Sprintf("%d", time.Now().Unix())
// Business Coin Parameter
pub["chain"] = "eth"
pub["coin"] = "eth"
pub["sign"] = getSign(pub)
url := fmt.Sprintf("%s/coin/list", BaseUrl)
r := req.New()
r.EnableInsecureTLS(true)
post, err := r.Post(url, req.BodyJSON(&pub))
if err != nil {
panic(err)
}
fmt.Println("return", post.String())
// verification
checkSign(post.String())
}
// Getting signature string
func getSign(data map[string]string) string {
err, signStr := ApiSign(data, signPrivateKey)
if err != nil {
panic(err)
}
fmt.Println("signature string:", signStr)
return signStr
}
// verifying signature
func checkSign(resp string) {
var res map[string]interface{}
err := json.Unmarshal([]byte(resp), &res)
if err != nil {
panic(err)
}
sign := res["sign"].(string)
data := ParseJsonToClassMap(resp)
err = ApiVerifySign(data, signPublicKey, sign)
if err != nil {
fmt.Println("verification failed")
return
}
fmt.Println("verification successful")
}
/**
* Local private key signature
* @param data parameter
* @param priKey local private key
* @return error
* @return string
/
func ApiSign(data map[string]string, priKey string) (error, string) {
hashed := getSignString(data)
// rsa
block, _ := pem.Decode([]byte(priKey))
if block == nil {
return errors.New("private key error"), ""
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return err, ""
}
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey.(rsa.PrivateKey), crypto.MD5, hashed)
if err != nil {
return err, ""
}
ciphertext := base64.StdEncoding.EncodeToString(signature)
return nil, ciphertext
}
/**
* Using other party's public key to verify the signature, and determine whether the signature matches
* @param data parameter
* @param signed signature body
* @param pubKey public key
* @return error
/
func ApiVerifySign(data map[string]string, pubKey string, signed string) error {
hashed := getSignString(data)
block, _ := pem.Decode([]byte(pubKey))
if block == nil {
return errors.New("public key error")
}
// Parse public key
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return err
}
pub := pubInterface.(rsa.PublicKey)
decodeString, err := base64.StdEncoding.DecodeString(signed)
if err != nil {
return err
}
err = rsa.VerifyPKCS1v15(pub, crypto.MD5, hashed, decodeString)
if err != nil {
return err
}
return nil
}
// Splice parameter
func getSignString(data map[string]string) []byte {
delete(data, "sign")
var keys []string
for k := range data {
keys = append(keys, k)
}
sort.Strings(keys)
var signStr string
for _, k := range keys {
if signStr != "" {
signStr = signStr + "&" + k + "=" + data[k]
} else {
signStr = k + "=" + data[k]
}
}
hashMd5 := md5.Sum([]byte(signStr))
hashed := hashMd5[:]
return hashed
}
func ParseJsonToClassMap(jsonStr string) (m map[string]string) {
m = make(map[string]string)
result := gjson.Parse(jsonStr)
result.ForEach(func(key, value gjson.Result) bool {
if value.Type == gjson.JSON && gjson.Valid(value.String()) {
m[key.String()] = JsonToString(value.String())
} else {
m[key.String()] = value.String()
}
return true
})
return
}
func JsonToString(jsonStr string) string {
str := ""
r := gjson.Parse(jsonStr)
r.ForEach(func(key, value gjson.Result) bool {
if value.Type == gjson.JSON && gjson.Valid(value.String()) {
str += JsonToString(value.String())
} else {
str += value.String()
}
return true
})
return str
}
var crypto = require('crypto');
var priKey = -----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8d6RGCeQkrobx
+4xO1jXnHwiMU48UyOaRaNW2FMQrN9DcK/T2h58FyuA7kYN0CVNFEnaZQS9S8Nur
Tl0lOVJcbwKfCSnu9nmFYDvPWqlCTqQW3F83sKNi9IWrMFFny3CX35eUHpeJahE2
R9wma2oJIFJv3ux/I3+w8fQQ6zMPIqpDx4Tcu9sCVpot+/siWJDpyiQBlbXfcxuH
YOePUGx+tJ5HLXtJOUtPcgyhFTXRmW9r17DnnsjrT5Vc50IbN//JXbvr96eexQ/e
/IXHZP6MZWdthjuv9y2TaxS+JMnpKLrXfgazoQEsKmchAsTLEyX6tHG5tHq+3MaK
BSTFCoHHAgMBAAECggEAYodR1GPbZ+nuFpfhlF2ctIWe3G3awYz+LUrXX2ef3tBB
WrmISVJBqq+TZtD+Hi254MR0SE6xxPSygu5m98zt5/EJN8d8qrckdLwIyLCnCElA
s8rl5KFt6Y/YSdTq2g6bgGqePgt4QZi4At6yk0tYlgjV0DKFBTqzyPfVFvs/KEuZ
o8le0XjgJA+Rnti35Gi5UDL1Tn9LcmCU/JcdpksFgPvU/wPmlNb8azM68ougXcrf
hBebSRWbS0F2OXRJd7+hrTq+pwTNIH+IyzsUZHQCcH0tFe2Q304oDuVuwX5VxB3S
tYG8+LoECW1nKalJbolkmdRqVxZRXG/Ho8/aRCHBcQKBgQDBmoZzUaKU2F4Sa6P+
5vCQ5RAAiAATWao/WYfL9BBVnLzbkbzget3BNdJcJZKp1Avh6F/jeB0xUOD0aFgm
rJAZjVAFu77damKWwiEPxKfdgSbBedV/KWCMASzr4m4cdYKOcmlLFsh+Du2+oZW5
3Mn35hyfbTf1OyYxrvOVrhSu+wKBgQD5NVfxWTBsugAE++d4CCy8TUL7QCqb1I1v
EQ0FNdZsmXslPbCFlJ5UCFJXAF6s2x0MDKCl7y2ixlDo31H7DI6xlOP5kjYon7wr
mB0U2bclGtlJ3FvCwDBFkfZykYZqPyey8A3D+kf4pnvZvn/0BgKfWCVSYnqEB4mp
BI5WhywOpQKBgH4Mio8YTn8UZCMgG/UBDQ15ZbLC2THABj0aoHRkoiHrW3ala1DI
DgsWbLB78gJKQbZCofOqp28NVnkqWoJLhcJtI/Wnp/vmoOvA1t+6TQTFaqM7HIqf
OPHObh3iHYi/5VGzIeS2n7CbLaUY9S++lWymUGpoJK+wZAyi+IqyfVrzAoGAPKmI
g75zNDQ+pvfuy2AqC5g/Esv200H1P7EF21vUm1DtEg6nNo0L0WbJLFZxtWAM/Q7h
2CKVmEbwEQ+zdAoFfiLBL9Iwjsj7VB1ODvJES6ryc7FJQOnpljXGqPLVpm1DK6KV
pzv4YwWbplBh8zO3VbRuOVSxgAwZrWPnhhaXWeUCgYEAwVHYXN1qGTBS1uyrldDr
OMuXyfvE7lbfJlg2EfG2A7PtTooPkh1IbRjlJ8ACbiadM0pYhnGGPk0D1WSKvRxU
SmSc6PgW2weANQ8QCemo2V/zZ0DlWFZDdCV+7jMbAFBhQHcVh0qUr+uNyntjLTjj
OPdMSnhKLoKtleu+NiMIogc=
-----END PRIVATE KEY-----
var pubKey = -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7K9s+r82lofBFaj3KWp
o/5E1ryDPDlLGZ4Rv68DcoCxV/bv0IvYX3cleC0WvTpwbdTpGtLjS6RHj89JREhi
svv5qmk5YTfjEqd3RmtYDmOVz2Ry6E76wLh9qi1pqpneMGr3shvEzZC6gkieXRCA
MOlEYbE7TSzxxDJxmovdeElowdRZcvwwPLvh/BLmCn+H1MXfdx01c/eFpTvu8BZG
1m7AqDOqnaPcR1ru/ekJoIwotF1jCPM2E5kAbQQutNaeSXhLbkfom4bxqF2/ni6k
Cwnz3fbBYcXP3Ww3/AdI1dn0iLr6450qn72yBpDtQY8OZ7+Krff4Fa5jOBY7OHEN
FwIDAQAB
-----END PUBLIC KEY-----
/**
* Private key signing
*/
function encryption(data) {
let signString = getSignString(data)
let sign = crypto.createSign('md5WithRSAEncryption');
sign.update(signString, 'utf8');
sign.end();
let signature = sign.sign(priKey, 'base64');
return signature
}
/**
* Use the other party's public key to verify the signature and check whether the signature matches
* @param sign
* @param data
* @return bool
*/
function checkSignature(sign, data) {
let signString = getSignString(data)
let verify = crypto.createVerify('md5WithRSAEncryption');
verify.update(signString, 'utf8');
verify.end();
return verify.verify(pubKey, sign, 'base64');
}
// Splice parameter
function getSignString(data) {
delete(data["sign"])
var keys = Object.keys(data)
keys = keys.sort()
let signStr = ""
for(let k in keys) {
if(typeof(data[keys[k]]) == "object") {
signStr = keys[k] + "=" + arrayToString(data[keys[k]])
}else {
if (signStr != "") {
signStr = signStr + "&" + keys[k] + "=" + data[keys[k]]
} else {
signStr = keys[k] + "=" + data[keys[k]]
}
}
}
return signStr
}
function arrayToString(data) {
let str = '';
for(let list in data) {
if (typeof (data[list]) == "object") {
str += arrayToString(data[list]);
} else {
str += data[list];
}
}
return str;
}
//send http post
function httpByPost(data) {
let HTTP = require('http')
let POST_OPTIONS = {
port: 8888,
host: "http://api.xxxxx.com/shopapi",
path: "/coin/list",
method: 'POST',
headers: {
"Content-Type": "application/json"
}
};
const REQUEST = HTTP.request(POST_OPTIONS, requestOnResponse)
REQUEST.setTimeout(6000)
REQUEST.write(JSON.stringify(data), 'utf8')
REQUEST.end()
}
function requestOnResponse(incomingMessage){
let data = []
incomingMessage.on('data', chunk => {
data.push(...chunk)
})
incomingMessage.on('end', () => {
let _date = JSON.parse( new TextDecoder().decode(new Uint8Array(data)))
let res = checkSignature(_date["sign"], _date)
console.log(res)
})
}
/**testing*****/
let data = {
'app_id' : 'xxxxxxxxx',
'version' : '1.0',
'key_version' : 'admin',
'time' : Math.floor(Date.now()/1000),
'chain' :'eth',
'coin' : 'eth',
}
data['sign'] = encryption(data)
httpByPost(data);
package com.junior.common;
import com.google.gson.*;
import java.io.;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.stream.Collectors;
public class rsaSigner {
public static void main(String[] args) {
String baseUrl = "http://api.xxxxx.com/shopapi";
String priKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtdgvcfozY7fe/x6UlXR/Fff8+wX++CiG05YVRafDe90O8y5KJAMZ156TgajEUXSRyKZqASz7iVoMAcu5FqfK1KuOOdjTAg0LFe/twIZvKmAqHcCSS2pjUXk0fTpZEgQ5lmDCK12Mg07YXvr+BeHjF+kBpwRJMPMG/+DGBDYNFNeRSJqWyopt6yyF528soAykHIUQ7TTultJV5IPCrvjRQQrGdJ/QtCdE0vZg+tA09nFN7AqgraU99kIvzyS0sqysj+pP1SJRmTRSGtlVAO3mILfkXOekZl1KD60G7CF+7BIaaW83FPD55tZhAGhnbLMYyiq7OiIyU61LTRcNSaJ0tAgMBAAECggEBAIGgGKcSzxCBbMYdXLV6TPbZ/HeaRGrwyVWUu7cmc0E8CJu6iWvmb2jGzbiCwuCT5luF6ZZ8JKchvU4FlTfsE5r9TQY2IZ/Ht4s65qBaDUEts5iY3kv8HX9+ZSXDJZpV0ztqqsPmx4ZNj+NYwWXwcFiKdb5R8OmV8bgSsnPddD6wFhwewglUOBPRAjropHE4sqk1Vam6q1MlFq5kiekMn/CItyFYFFzST/ruqAtVTp7YIvazAMrfCKH+pvXWk6pYNHZmL/JZey0GglRyJ1nItiqL33X8Lji056FcbAclidq022h/KPLEJpZk6irWqYdqYZeF6YMLHtdZFDkTZ/hXY+UCgYEA5i794UTQ1Kd6K+iui/S8K5H8nGdiXb3+uZ5j5VKYfbRmzXhAdVYisYe8czeTECxhH+MeHgM4tLR4ifx9q60ylvZxl9Fav1p6EaE36YcvGVmpm7Gm3q6QDDBa4gB+X9u7CxCS4LpotV917YS1QvOIF9lnRonSj1zNzFtbXkywrN8CgYEAwOpx/mR40ZvgZxNCq9DsIdrSZYAFFKpw0xy0OtF1PpuwMaREoRabnlVw6Bb3Hofg50XA3JxemzAX6fB92ee+NfXfeL4UtC4vG1K9JyiXHtHq8p9Uw2W03ehIEeNa3xdoRRnLmD4VUpyfL19ViE2+7TM1Gd3TogjDT+AMOCSuq3MCgYBsurPH7gaq/LVT+mRAzgj4l8v4YUlwuGeTbIMJdvt7HXUWB4CDLH3U2CYnUpAQKrZyJok6ahEmIr1xiKggKP7lmmHL8eNo0icpHrtXfzi7Q8Q/PCpzs4dtioXTjaIkS5nNvzVyG/uL+RyuZmpsxrZ5dYM4KbAhchfwORMutxEZhwKBgQC5zpVw4jCEItBmNuTWO+nTScGvxTgfiXIVw+XLaQa2AJoZlhAL34yPWdffko79tv3lgweY9HsimZXO2rU8dbp8mo5c6ydhy8HPXUeWOcAkDSdv/ApWENW9jgYsRIC3swHY3Fl+Dv3Wjce8huQI3mjwaYvRmBhIToxfmHnscVhTBQKBgB9uciEHfz8hMI7ePrPo2PkyK/RbJDm3HnuWE2lFBp7MOUMlh53MXwv4dIRaYMbRrFuN1UIKEFxuKhWwTHSzZJufk/UENrha/81fpj2BoFsAKkEunKeETvaIh6fZSjec16h2+zxbzwkdS6b+yIYkSlaoAxmDYrMtae0C8G4e0YS9";
String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2RZZISModRzproL3eTxRtvVK/nToDbeLmAVdmaVtHgNCwjXMeNCZvHySZKO7+t7XySzBd2PhhQjvAkDd8HRKB5Y0uvP6TFwvhxOMkMydTeBFcUobQWNeAI7ttRms1txSNrmyrZr0WpWfk79VIsO+yI/jFN/Pj2gxD+vh5UUKU1LD2IQq/VxgtEpf8Vs6w7SvafPwYkpdPGCKUFkRvN1FrY2+tq0j1MRVJbB2rIunWDnT8cDUh2sWLXf4yNaJVOaaTFB0zbAk/qcoicGQyW7bVG7dVW8WywKmOAPeQCCQa+SJ4BhfI1k8eEAwFVRuxz/f9Ecfog9hZJ+RgELkpS8qhQIDAQAB";
rsaSigner signer = new rsaSigner();
Map<String, Object> params = new HashMap<>();
params.put("app_id", "hxgmau9imt86qky9");
params.put("version", "1.0");
params.put("key_version", "admin");
params.put("time", Long.toString(System.currentTimeMillis() / 1000));
params.put("coin", "trx");
try {
params.put("sign", signer.genSign(params, priKey));
Gson gson = new GsonBuilder().create();
System.out.println("params = " + gson.toJson(params));
String res = doPost(baseUrl + "/address/getBatch", gson.toJson(params));
if (null == res) {
throw new RuntimeException("http error");
}
System.out.println("response = " + res);
JsonParser jp = new JsonParser();
JsonObject resEle = jp.parse(res).getAsJsonObject();
boolean retSignOK = signer.verifySign(resEle, resEle.get("sign").getAsString(), pubKey);
if (!retSignOK) {
throw new RuntimeException("verify response sign fail");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("OK");
}
public String genSign(Map<String, Object> data, String priKey) throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
Map<String, String> stringMap = toStringMap(new GsonBuilder().create().toJson(data));
return this.sign(this.genSignString(stringMap), priKey);
}
public String genSignString(Map<String, String> params) {
if (null == params || params.size() == 0) {
return "";
}
List<String> sortedKeys = new ArrayList<>(params.keySet());
sortedKeys.remove("sign");
sortedKeys.sort(null);
String sb = sortedKeys.stream().map(s -> s + "=" + params.get(s)).collect(Collectors.joining("&"));
System.out.println("genSignString = " + sb);
return sb;
}
public String sign(String str, String priKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.getDecoder().decode(priKey.getBytes(StandardCharsets.UTF_8));
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
PrivateKey pk = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(pk);
signature.update(str.getBytes(StandardCharsets.UTF_8));
return new String(Base64.getEncoder().encode(signature.sign()), StandardCharsets.UTF_8);
}
public boolean verifySign(JsonElement data, String sign, String pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
Map<String, String> resMap = toStringMap(data);
String signString = this.genSignString(resMap);
byte[] keyBytes = Base64.getDecoder().decode(pubKey.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(key);
signature.update(signString.getBytes(StandardCharsets.UTF_8));
return signature.verify(Base64.getDecoder().decode(sign.getBytes(StandardCharsets.UTF_8)));
}
public static Map<String, String> toStringMap(String str) {
JsonParser jp = new JsonParser();
JsonElement jEle = jp.parse(str);
return toStringMap(jEle);
}
public static Map<String, String> toStringMap(JsonElement jEle) {
Map<String, String> resMap = new HashMap<>();
if (null != jEle && jEle.isJsonObject()) {
for (Map.Entry<String, JsonElement> entry : jEle.getAsJsonObject().entrySet()) {
resMap.put(entry.getKey(), valueAsString(entry.getValue()));
}
}
return resMap;
}
public static String valueAsString(JsonElement element) {
if (element.isJsonNull()) {
return "";
} else if (element.isJsonPrimitive()) {
return element.getAsJsonPrimitive().getAsString();
} else if (element.isJsonObject()) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, JsonElement> entry : element.getAsJsonObject().entrySet()) {
sb.append(valueAsString(entry.getValue()));
}
return sb.toString();
} else if (element.isJsonArray()) {
StringBuilder sb = new StringBuilder();
for (JsonElement vv : element.getAsJsonArray()) {
sb.append(valueAsString(vv));
}
return sb.toString();
}
return "";
}
public static String doPost(String httpUrl, String param) {
//send http post request
}
}
Create private key
openssl genrsa -out rsa_private_key.pem 2048
Create public key from private key
openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
Signature Algorithm Details
- 1. Get the request parameters and format them to get a new parameter formatted string:
- 2. Sign the first step data with the RSA private key and save the signature result to a variable:
Generate the signature string, for the following parameter array:
user_id = 1
coin = eth
address = 0x038B8E7406dED2Be112B6c7E4681Df5316957cad
amount = 10.001
trade_id = 20220131012030274786
Sort each key in the array in order from a to z. If the same first letter is encountered, look at the second letter, and so on. After sorting, connect all array values with "&" characters, such as $dataString:
address=0x038B8E7406dED2Be112B6c7E4681Df5316957cad&amount=10.001&coin=eth&trade_id=20220131012030274786&user_id=1
This string is the transfromed string.
Public information description
The following information is common to all interfaces, and the description of each interface is not repeated
Production Environment
Please contact customer service to obtain the API address
test environment
Please contact customer service to obtain the API address
Common paremeter in Request
| Parameter Name | Required | Type | Constraint | Case | Description |
|---|---|---|---|---|---|
| app_id | Yes | string | Generated by the system | A6vZLiqyRYxu | Merchant ID assigned by the system when docking |
| version | yes | string | fixed | 1.0 | interface version number - fixed according to the agreement at the time of access |
| sign | yes | string | length 344 bits | Dr7MnhPYh… | RSA-2048 bit key mode |
| time | yes | string | length 10 digits | 1578829811 | timestamp - ignoring timezone timestamp in seconds |
| key_version | no | string | dynamic enumeration type | default: admin | key version number: admin / read / arbitrary - can be authorized for any 1-n interfaces |
Public Information Notice
| Name | Type | Case | Description |
|---|---|---|---|
| time1 | timeStamp | 1578829811 | UTC time unified time without time zone |
| Time 2 | DateTime | 2020-01-09 15:48:42 | The global system defaults to Beijing time if this format appears |
| Request Method | POST | — | All requests are POST |
| Encoding | UTF-8 | — | Other encodings may produce garbled characters and errors |
| Communication Protocol | HTTPS | https | Currently only HTTPS is supported |
| Signature Type | RSA-2048-bit | Dr7MnhPYh… | Click to view rule details |
| Request and Return | JSON | — | All request and response parameters are in JSON format |
| Note on the amount parameter | string | — | The number of digits after the decimal point exceeds 8, and the number of digits before the decimal point exceeds 12, which cannot be passed, and the last invalid 0 must be deleted. |