3 附录

附 1:加解密方式

请求参数data与响应字段data的值都是经过rsa加密后再通过 base64urlsafe 加密的结果

注意事项

1)base64传统编码中会出现+, /两个会被url直接转义的符号,因此如果希望通过url传输这些编码字符串,我们需要先做传统base64编码,随后将+和/分别替换为- _两个字符,在接收端则做相反的动作解码

  1. Java Demo : https://github.com/HiCoinCom/WaaSDemo

3)rsa加密与解密使用分段加密

请求参数加密示例:
// 原始请求参数
String originReqData = '{"charset":"utf-8","symbol":"eth","sign":"","time":"1586420916306","app_id":"baaceb1e506e1b5d7d1f0a3b1622583b","version":"2.0"}'

// encryptByPrivate方法封装在下列公共类RSAHelper.java中
String encryptReqData = RSAHelper.encryptByPrivate(originReqData, "第三方自己的私钥")

//http post
String httpBuildParams = "app_id=baaceb1e506e1b5d7d1f0a3b1622583b&data=" + encryptReqData
响应数据解密示例:
      // 响应的原始数据
String originResp= '{"data":"jwtkGrhh2EVJS8xe93MpUYd-SQ-TyK0Bx5sXjE4hygFNg4wmctiahtIYXRpR2j8yDaEF5YzVstnUKbOH2p44FSMjXMQU4qFrhD00WOfW7v4LNALyiQXRb_5sakR0Zf573lGfLRTPlzLtTho3gqu3hMwuAv5e3r2dpb6_jxh1Z9BjkzSsNRX_bjLcHLUOPhMvo6rTUKSa9LQ6QnT8RX0eqzOZPlnCw3TeX_zcWWjxp6fcpKcdODxoI86gHwWRpSd-2qbEbFcaT12CJd9nPXA0KnLPNNHWz8sxQGiAg7Jg_-cN_yBHL9cS15zecTemYGqpOXRkojM1JwLsjM-7txf_dw"}'

      // 解密响应数据
      String encryptRespData = JSON.parse(originResp)['data']
      // decryptByPublic 方法封装在下列公共类 RSAHelper.java中
String decryptRespData = RSAHelper.decryptByPublic( encryptRespData, "托管平台提供的公钥" )
公共类RSAHelper.java:
 
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

import javax.crypto.Cipher;

public class RSAHelper {
        /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /** *//**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 234;

    /** *//**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 256;


    private static final String CHARSET ="UTF-8";



    /**
     * 公钥解密
     *
     * @param encryptedData 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
                    throws Exception {
            byte[] keyBytes =  decryptBASE64(publicKey);
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key publicK = keyFactory.generatePublic(x509KeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, publicK);
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                    if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                            cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                    } else {
                            cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                    }
                    out.write(cache, 0, cache.length);
                    i++;
                    offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
    }

    /**
     *  公钥分段解密
     * @param encryptedData 加密的base64数据
     * @param publicKey rsa 公钥
     * @return
     */
    public static String decryptByPublicKey(String encryptedData, String publicKey){
            if(encryptedData==null || encryptedData.isEmpty() || publicKey==null || publicKey.isEmpty()) {
                return "";
            }

            try {
                encryptedData = encryptedData.replace("\r", "").replace("\n", "");
                byte[] data = decryptByPublicKey(decryptBASE64(encryptedData), publicKey);
                if(data == null || data.length < 1){
                        return  "";
                }
                return new String(data);
            }catch (Exception ex){
                    ex.printStackTrace();
            }
            return "";
    }

    /**
     * 私钥加密
     *
     * @param data 源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
                    throws Exception {
            byte[] keyBytes =  decryptBASE64(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateK);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                    if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                            cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                    } else {
                            cache = cipher.doFinal(data, offSet, inputLen - offSet);
                    }
                    out.write(cache, 0, cache.length);
                    i++;
                    offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            return encryptedData;
    }

    /**
     *  私钥分段加密数据
     * @param data 待加密数据
     * @param privateKey  私钥
     * @return
     */
    public static String encryptByPrivateKey(String data, String privateKey){
            if(data==null || privateKey==null || data.isEmpty()|| privateKey.isEmpty()) {
                return "";
            }

            try {
                    byte[] encryptedData = encryptByPrivateKey(data.getBytes(CHARSET), privateKey);
                    if(encryptedData == null || encryptedData.length < 1){
                            return  "";
                    }

        byte[] dataBytes = encryptBASE64(encryptedData).getBytes(CHARSET);
        return new String(dataBytes).replace("\r", "").replace("\n", "");
            }catch (Exception ex){
                    ex.printStackTrace();
            }
            return "";
    }

    /**
     * BASE64Encoder 加密
     *
     * @param data
     *            要加密的数据
     * @return 加密后的字符串
     */
    public static String encryptBASE64(byte[] data) {
        //JDK 1.8以下环境,使用下列2行代码
        // BASE64Encoder encoder = new BASE64Encoder();
        // String encode = encoder.encode(data);
        // 从JKD 9开始rt.jar包已废除,从JDK 1.8开始使用java.util.Base64.Encoder
        Encoder encoder = Base64.getEncoder();
        String encode = encoder.encodeToString(data);
        //不管使用什么环境,下面的+/替换成-_都需要完成。
        String safeBase64Str = encode.replace('+', '-');
        safeBase64Str = safeBase64Str.replace('/', '_');
        safeBase64Str = safeBase64Str.replaceAll("=", "");
        return safeBase64Str;
    }
    /**
     * BASE64Decoder 解密
     *
     * @param data
     *            要解密的字符串
     * @return 解密后的byte[]
     * @throws Exception
     */
    public static byte[] decryptBASE64(String data) throws Exception {
        //JDK 1.8以下环境,使用下列2行代码
        // BASE64Decoder decoder = new BASE64Decoder();
        // byte[] buffer = decoder.decodeBuffer(data);
        // 从JKD 9开始rt.jar包已废除,从JDK 1.8开始使用java.util.Base64.Decoder
        Decoder decoder = Base64.getDecoder();

        //不管使用什么环境,下面的-_替换成+/都需要完成。
        String base64Str = data.replace('-', '+');
        base64Str = base64Str.replace('_', '/');
        int mod4 = base64Str.length()%4;
        if(mod4 > 0){
            base64Str = base64Str + "====".substring(mod4);
        }

        byte[] buffer = decoder.decode(base64Str);
        return buffer;
    }
}
PHP签名Demo:
<?php
        class RSA
        {

                //第三方私钥
            public $pri_key = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD6YNILWOJZjS6FQQ9ZL9CEKcWZTTldrDLsxP2dQME7hSUTDQ5AosBUZk18Uq212SC2+L0UA9G6WPoCNzHCB8TP25jC+EwIkHMN4EEPRs+bEHUgX3Bq3oR2SCHjEiqleTFW2kO/oS6Vg9bhTST5MFaEnA0fc2Bh3+4iRus+5mVc6ux0lG55f1qmvUNM4hhP7qVpzc3X0xFA0Slu8dyel1dbOUQlJbUkrt5NzXXqmRoP5UVHUCXPZzH1kbxdbGA58TonXceh6DHQRa6pIBNaQ6BfnqhMvGVvuIqKPrdWq8yigvTw2zqBfwCwY3/3FZoI5ICQ8oS3GRHYP/rXzncqkKTzAgMBAAECggEAdag77EMnkueKXeo12TZj6Udr6N9mPsOl5qenelcsttiZlHtFIFCays6MSQjdQqA3BGSdDaPB0azwR0xCoKhf70GFZtGhgUDIIFQqnpArDPZN5BmVTVMlsiOxcPBfhAUQj3zf61RF/NLIjnVfE46IiaZ/cDEasMO3NvpWn+dK6L86zklgwHfC5IXTFnFRVA3bWkAQ3gswhLzjs50HNoNV96fsnbt1n7NSWhyz9B8hGMV+qYz1NGmb+VsaAune+oIv28krcaqf+Doah37rCmzEgVeZZ1/flPFOXpaq1eGJDgbLu6FbbgqfabCBlhmuzuwNbDl/2T/U9U6JoQWGR7t++QKBgQD8XSzBqpWwz8ebfsPipvnhIugbHgBnwLaRc3/xieuNuiDMsYPY1isBWSeYqjwV5uTad9s9dRxb6OOMH+KChkUxkYhEvoujUulGSuO4MxJlWl99WWEsbLzefubBD0zyHo5daHbPPXO8UPMu/SfiYxT2D5wsW2/swUqHWS3AmDS9RQKBgQD9/FJC/++DLyhU60Q9vrVY50zQTyPLtPnuIxbsPXB1Exo1wKe+LC02k9Cub9f5EFViTEniWRasB7ecnDxJT/ISU+hJjMUKFuaHueb7dO6wiIqyfpJeQM/4fKalBQI+nCEh3aceNKP44mk/lv2x22+P47EAKh7yqBdEVUv5GlHw1wKBgQCbAqReJOijXU0vLtMlYgj0h9tn5Kq9D/tUJky9UUkVmfFRqevhgdOSlW+j71TO4y9JHfvVqRyNO+ShCmi4Yb8Yrlq0VxIwdNoCqjdryjsPdE5ZEVCF2Bi+1dXpWfuacLhjman4q7duQY7OGwOno9KZPYdhG50JIMUlk9pthVBHvQKBgCXUC+iAuAqg3m/vboWHvvjT0mQANYOkm8j1HvfmmrZFNxUkcZdoev9y+pTQgalN3nm6hRKaVD8hEx7XQj9lEdfa+XDi74H2MTWr4ZQ4MUjHvWiiY2h4XMFUx3kyisgKdwDVQ4vDKVzrU+OtuHFiDnau4fD1VRCtKnH6Bku+uM+XAoGAB7V/OFlk7gaX7gne7p+DypXICn1oGE46aFLsDciOyePNovYg6bfdiUB9evwFSijiHq7eldZIQSRIdUalL1qfv2zDwFmEGpSd/RZYOOv21c3eISjln6W7ZGtumtLHx2nGpC072i5vNee0aAPEdvO0h3y4gvzad5L4KwIwyHifKic=';

                //钱包给的公钥
            public $pub_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAua4XMw/W9BxyZhirTlNau5Y/tdAHkPsbZo58Cdz1ByeRX8RwOibpREDZLTwhMTZGroqWEAZ+efQhx0gez++03Sw6IsPWPDpzpM90ezn2gBqPog7jxQA+M0E32gMHWB5ygplPwQkGz/qGYeJ5qhp2OZ8O+jFqOJNi7ob1hE2QsPT118HIhUzTL77urD61BovI+jg9Rx6PGAqlFLLmfXToqDulLkYVKhhQlL7ii6iuzIXgl46mbmvH2RXJRq083sa9b9J1z/WzXxNaHNpq5USl3ifTTyD/IiOKnblA7f4KJmr9rcMFbAP1mNxz95at6hPBvqGypPqqixxPBrdkOIPUVwIDAQAB';

                public function __construct() {

                if (!is_null($this->pub_key)) {
                    $this->pub_key = openssl_pkey_get_public($this->formatterPublicKey($this->pub_key));
                }

                if (!is_null($this->pri_key)) {
                    $this->pri_key = openssl_pkey_get_private($this->formatterPrivateKey($this->pri_key));
                }

            }


                 /**
             * 格式化公钥
             * @param $publicKey string 公钥
             * @return string
             */
            public function formatterPublicKey($publicKey)
            {
                if (false !== strpos($publicKey, '-----BEGIN PUBLIC KEY-----')) return $publicKey;

                $str = chunk_split($publicKey, 64, PHP_EOL);//在每一个64字符后加一个\n
                $publicKey = "-----BEGIN PUBLIC KEY-----".PHP_EOL.$str."-----END PUBLIC KEY-----";

                return $publicKey;
            }

            /**
             * 格式化私钥
             * @param $privateKey string 公钥
             * @return string
             */
            public function formatterPrivateKey($privateKey)
            {
                if (false !==strpos($privateKey, '-----BEGIN RSA PRIVATE KEY-----')) return $privateKey;

                $str = chunk_split($privateKey, 64, PHP_EOL);//在每一个64字符后加一个\n
                $privateKey = "-----BEGIN RSA PRIVATE KEY-----".PHP_EOL.$str."-----END RSA PRIVATE KEY-----";

                return $privateKey;
            }

                /**
             * URL base64解码
             * '-' -> '+'
             * '_' -> '/'
             * 字符串长度%4的余数,补'='
             * @param unknown $string
             */
          function urlsafe_b64decode($string) {
                $data = str_replace(array('-','_'),array('+','/'),$string);
                $mod4 = strlen($data) % 4;
                if ($mod4) {
                    $data .= substr('====', $mod4);
                }
                return base64_decode($data);
            }

            /**
             * URL base64编码
             * '+' -> '-'
             * '/' -> '_'
             * '=' -> ''
             * @param unknown $string
             */
            function urlsafe_b64encode($string) {
                $data = base64_encode($string);
                $data = str_replace(array('+','/','='),array('-','_',''),$data);
                return $data;
            }


            /**
             *  私钥加密(分段加密)
             *  emptyStr    需要加密字符串
             */
            public function encrypt($str) {
                $crypted = array();
        //        $data = json_encode($str);
                $data = $str;
                $dataArray = str_split($data, 234);
                foreach($dataArray as $subData){
                    $subCrypted = null;
                    openssl_private_encrypt($subData, $subCrypted, $this->pri_key);
                    $crypted[] = $subCrypted;
                }
                $crypted = implode('',$crypted);
                return $this->urlsafe_b64encode($crypted);
            }

            /**
             *  公钥解密(分段解密)
             *  @encrypstr  加密字符串
             */
            public function decrypt($encryptstr) {
                // echo $encryptstr;exit;
                $encryptstr = $this->urlsafe_b64decode($encryptstr);
                $decrypted = array();
                $dataArray = str_split($encryptstr, 256);

                foreach($dataArray as $subData){
                    $subDecrypted = null;
                    openssl_public_decrypt($subData, $subDecrypted, $this->pub_key);
                    $decrypted[] = $subDecrypted;
                }
                $decrypted = implode('',$decrypted);
                // openssl_public_decrypt(base64_decode($encryptstr),$decryptstr,$this->pub_key);
                return $decrypted;
            }

        }
        $rsa = new RSA();
        $params = '{"charset":"utf-8","country":"+86","sign":"","mobile":"","time":"1589013592078","app_id":"baaceb1e506e1b5d7d1f0a3b1622583b","version":"2.0","email":"test123@qq.com"}';

        //加密参数
        $encryptParamsByPriv = $rsa->encrypt($params);

        //请求接口
        $resp = file_get_contents('http://awstestopenapi.hicoin.one/api/v2/user/info?app_id=baaceb1e506e1b5d7d1f0a3b1622583b&data='.$encryptParamsByPriv);

        $resp = json_decode($resp, true)['data'];
        //解密接口返回
        echo "get user info api:", $rsa->decrypt($resp);

附 2:接口错误码表

code msg
0 成功
100001 系统错误
100004 请求参数不合法(提现二次确认接口回调失败)
100005 签名校验失败
100007 非法IP
100015 商户ID无效
100016 商户信息过期
110004 用户被冻结不可提现
110023 手机号已注册
110037 提现地址存在风险
110055 提现地址错误
110065 请求用户用户不存在(获取用户余额、提现或转账时用到)
110078 提现或转账金额小于最小转出金额
110087 提现或转账金额大于最大转出金额
110088 请勿重复提交请求
110089 注册手机号不正确
110101 用户注册失败
110161 超过提现最大支持精度
120202 币种不支持
120206 提现二次确认失败
120402 提现或转账余额不足
120403 提现手续费余额不足
120404 提现或转账金额太小, 小于等于手续费
900006 用户存在风险,禁止提现
3040006 不能给自己转账