开放平台-附录

规则

1 按照接口请求参数组织业务参数 业务参数分为必填项和非必填项(非必填项不参与SHA256签名).业务参数中的productCode唯一确定加密和签名密钥。
注意密钥为产品编码对应的密钥,每个商户的每个产品密钥都不相同
2 使用AES密钥对参数加密(无序) aes key需要Base64.decode 之后再aes加密,IV向量为空。
加密后的byte数组,经过Base64.encode之后,创建字符串,得到加密密文
3 使用SHA256密钥对参数签名(有序)。注意:空值不参与签名 字母顺序排序,值为JsonArray的按照index参数升序排序,值为JsonObject的迭代签名。
参数与参数之间以英文逗号隔开拼接,如果参数的值是集合,value转换成JSONArray的形式.
参数拼接好后,前后分别拼接合利宝提供的sha256的密钥.
4 国密证书私钥进行P7非分离签名。注意:强制要求 使用商户后台"证书申请查询",进行国密证书的申请与私钥导出,得到.sm2格式的私钥。使用证书对步骤2的AES加密结果进行P7非分离签名,作为最终加密密文。
5 使用步骤4的非分离签名和SHA256签名结果组织到系统参数中,以HTTP POST请求的方式传输到跨境系统 需要设置设置系统参数certFlag=true

注意:AES密钥和SHA256密钥是根据产品编码(productCode)不同而变化的,相同的产品编码则使用相同的密钥对。
在本平台,每个商户编码的不同产品编码,密钥对是不同的。

请求参数示例

        密钥:JkF1oQIeWyuWNNouKYRrOQ==
        原文:{"orderNo":"APPLYCUSTOMS062701","productCode":"APPLYCUSTOMS","payOrderNo":"WXPAYSCAN061503","detailList":[{"summary":"苹果手机","customsType":"NINGBO","merchantCommerceName":"","merchantInspectionName":"","freight":0.2,"goodsAmount":0.5,"index":1,"inspectionType":"","tax":0.1,"goodsItemAmount":7880,"orderAmount":0.8,"merchantCommerceCode":"","merchantInspectionCode":"","goodsName":"苹果XX","goodsNum":100},{"summary":"","customsType":"HANGZHOU","merchantCommerceName":"","merchantInspectionName":"","freight":0.1,"goodsAmount":0.05,"index":2,"inspectionType":"","tax":0.05,"goodsItemAmount":0.2,"orderAmount":0.2,"merchantCommerceCode":"","merchantInspectionCode":"","goodsName":"液体钙等","goodsNum":200}],"orderIp":"","callbackUrl":"","payProductCode":"WXPAYSCAN","merchantNo":"Me10000018"}
        密文:SGG/qj1wAt0Yg5MsUU+9TEyesUixOZ6lc1mIxa65CNQRYcy5piUqqf7kN+HCY9tx3gM/T1DYBEcit5h9+yPKSlMR9UrAb0qj+renPHwEl9U8iKy0PlYp7Pe0yCpW1Jk0anO/xmU8q6IJNZcb6yTdAYPNSnb3vh5B4VDBAU34N5HuB+MvcNP+aUVrBT5Flax74FfHwVwmIiD59dfE5rNoh/ihwCJJ1y7O3z5pI8UwWOEerC9Zppss7741RnIVhk/yMZtokaKMDlI9j3c+UGftIzUEP0XqVjrZI6sh978ywi9qmpcbzwyVRb/OZs3JUqHYawRmflqKtTFtK6dQkNE+SNM0z0SBHR/T7bmSUz3TRqhU4dJADkwwB3Db4IN0gWwMaJav7Mly5llkv+JByd9MQ7mMgbO+XDShiNTqFkPf6M0d6MXImy1n4+qvfslIIgkha9ZOOdgnzn6Dmppka5fNC6ZwpdRLbV96jZ2KQnI+0xFYDmIcxLBVqnbznxPdV8mPTfc1ikhOjF7bbpF7rlAiV2Ph855m4Q7p0HWtbU7R/je5q6BWHKZSTakfoNid/FQ/gPWpV3PbhfgMRGU1KpqvVTe95DSJGsD34RiX943DCLBeT5XzaEN331WK/RJmtcGk5Ve9I68A6DjRPWEkAcOp5UfuMbbihOb90cKX4d774ZXZVWhlv8kPgQoerfW4fhpkGOo0wKksPEb8qOoRgV65K4yLMlBNfejdxX2TE7cb09oCPIRNyaEv/sDXKyJgV99/gcm8fBzEy7swCSuux1OvBJLfafuybUWYMgnV+ajaoceImq4BSh3cmMtpKf3YQ/ghduBr6mgVLUqy+xARXwTDqD7rnt+h2+b4xvDAmTt/lfJ4eTRfmDSvFc85Al0nc4WgWdgPRvMeF2BleWeMi4Wugya4YB/a3gBNTk7HNcLUjknHfUh2C6uF5X/gPldO8ejdtpeldqwnhZeR/JxmI/eP8Qaf255Ub6RpY0MaDxq6hhcMvUTT3PPvd3sCyAF92NS7aWW4GJVTOiPOSgutAknpODkrD6HzEWxoP8flsnDArI1p3Da5U11fA//vVBfZ4d4H
        
        密钥:phmyLtaLdSr1soJX
        原文:phmyLtaLdSr1soJX,detailList=[{"customsType":"NINGBO","freight":0.2,"goodsAmount":0.5,"goodsItemAmount":7880,"goodsName":"苹果XX","goodsNum":100,"index":1,"orderAmount":0.8,"summary":"苹果手机","tax":0.1},{"customsType":"HANGZHOU","freight":0.1,"goodsAmount":0.05,"goodsItemAmount":0.2,"goodsName":"液体钙等","goodsNum":200,"index":2,"orderAmount":0.2,"tax":0.05}],merchantNo=Me10000018,orderNo=APPLYCUSTOMS062701,payOrderNo=WXPAYSCAN061503,payProductCode=WXPAYSCAN,productCode=APPLYCUSTOMS,phmyLtaLdSr1soJX
        密文:66d8a871ad44d7d8e3b115ade9cb1990d4e09a20f30f01e6cd0ea35f60bbdab3
        
        密钥:.sm2文件读取
        私钥密码:商户后台申请国密证书时,由操作人员自主设置
        原文:AES的加密结果
        密文:
        
        {"content":"SGG/qj1wAt0Yg5MsUU+9TEyesUixOZ6lc1mIxa65CNQRYcy5piUqqf7kN+HCY9tx3gM/T1DYBEcit5h9+yPKSlMR9UrAb0qj+renPHwEl9U8iKy0PlYp7Pe0yCpW1Jk0anO/xmU8q6IJNZcb6yTdAYPNSnb3vh5B4VDBAU34N5HuB+MvcNP+aUVrBT5Flax74FfHwVwmIiD59dfE5rNoh/ihwCJJ1y7O3z5pI8UwWOEerC9Zppss7741RnIVhk/yMZtokaKMDlI9j3c+UGftIzUEP0XqVjrZI6sh978ywi9qmpcbzwyVRb/OZs3JUqHYawRmflqKtTFtK6dQkNE+SNM0z0SBHR/T7bmSUz3TRqhU4dJADkwwB3Db4IN0gWwMaJav7Mly5llkv+JByd9MQ7mMgbO+XDShiNTqFkPf6M0d6MXImy1n4+qvfslIIgkha9ZOOdgnzn6Dmppka5fNC6ZwpdRLbV96jZ2KQnI+0xFYDmIcxLBVqnbznxPdV8mPTfc1ikhOjF7bbpF7rlAiV2Ph855m4Q7p0HWtbU7R/je5q6BWHKZSTakfoNid/FQ/gPWpV3PbhfgMRGU1KpqvVTe95DSJGsD34RiX943DCLBeT5XzaEN331WK/RJmtcGk5Ve9I68A6DjRPWEkAcOp5UfuMbbihOb90cKX4d774ZXZVWhlv8kPgQoerfW4fhpkGOo0wKksPEb8qOoRgV65K4yLMlBNfejdxX2TE7cb09oCPIRNyaEv/sDXKyJgV99/gcm8fBzEy7swCSuux1OvBJLfafuybUWYMgnV+ajaoceImq4BSh3cmMtpKf3YQ/ghduBr6mgVLUqy+xARXwTDqD7rnt+h2+b4xvDAmTt/lfJ4eTRfmDSvFc85Al0nc4WgWdgPRvMeF2BleWeMi4Wugya4YB/a3gBNTk7HNcLUjknHfUh2C6uF5X/gPldO8ejdtpeldqwnhZeR/JxmI/eP8Qaf255Ub6RpY0MaDxq6hhcMvUTT3PPvd3sCyAF92NS7aWW4GJVTOiPOSgutAknpODkrD6HzEWxoP8flsnDArI1p3Da5U11fA//vVBfZ4d4H","merchantNo":"Me10000018","orderNo":"APPLYCUSTOMS062701","productCode":"APPLYCUSTOMS","sign":"66d8a871ad44d7d8e3b115ade9cb1990d4e09a20f30f01e6cd0ea35f60bbdab3"}
        

签名算法

        AES加密算法

        import javax.crypto.Cipher;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.UnsupportedEncodingException;

        public class AES {

        private static final Logger logger = LoggerFactory.getLogger(AES.class);

        public static byte[] encrypt(byte[] data, byte[] key) {
        CheckUtils.notEmpty(data, "data");
        CheckUtils.notEmpty(key, "key");
        if (key.length != 16) {
        throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
        }
        try {
        SecretKeySpec secretKey = new SecretKeySpec(key, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
        cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
        byte[] result = cipher.doFinal(data);
        return result; // 加密
        } catch (Exception e) {
        throw new RuntimeException("encrypt fail!", e);
        }
        }

        public static byte[] decrypt(byte[] data, byte[] key) {
        CheckUtils.notEmpty(data, "data");
        CheckUtils.notEmpty(key, "key");
        if (key.length != 16) {
        throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
        }
        try {
        SecretKeySpec secretKey = new SecretKeySpec(key, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
        cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
        byte[] result = cipher.doFinal(data);
        return result; // 加密
        } catch (Exception e) {
        throw new RuntimeException("decrypt fail!", e);
        }
        }

        public static String encryptToBase64(String data, String key) {
        try {
        logger.info("AES加密原文:{}", data);
        byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING),
        Base64.decode(key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)));
        return new String(Base64.encode(valueByte));
        } catch (UnsupportedEncodingException e) {
        throw new RuntimeException("encrypt fail!", e);
        }

        }

        public static String decryptFromBase64(String data, String key) {
        try {
        byte[] originalData = Base64.decode(data.getBytes());
        byte[] valueByte = decrypt(originalData, Base64.decode(key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)));
        String AES_decrypt = new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
        logger.info("AES解密原文:{}", AES_decrypt);
        return AES_decrypt;
        } catch (UnsupportedEncodingException e) {
        throw new RuntimeException("decrypt fail!", e);
        }
        }


        }


        public class Base64 {

        static final int CHUNK_SIZE = 76;

        static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();


        static final int BASELENGTH = 255;


        static final int LOOKUPLENGTH = 64;


        static final int EIGHTBIT = 8;

        static final int SIXTEENBIT = 16;


        static final int TWENTYFOURBITGROUP = 24;


        static final int FOURBYTE = 4;


        static final int SIGN = -128;

        static final byte PAD = (byte) '=';

        private static byte[] base64Alphabet = new byte[BASELENGTH];
        private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
        static {
        for (int i = 0; i < BASELENGTH; i++) {
        base64Alphabet[i] = (byte) -1;
        }
        for (int i = 'Z'; i >= 'A'; i--) {
        base64Alphabet[i] = (byte) (i - 'A');
        }
        for (int i = 'z'; i >= 'a'; i--) {
        base64Alphabet[i] = (byte) (i - 'a' + 26);
        }
        for (int i = '9'; i >= '0'; i--) {
        base64Alphabet[i] = (byte) (i - '0' + 52);
        }

        base64Alphabet['+'] = 62;
        base64Alphabet['/'] = 63;

        for (int i = 0; i <= 25; i++) {
        lookUpBase64Alphabet[i] = (byte) ('A' + i);
        }

        for (int i = 26, j = 0; i <= 51; i++, j++) {
        lookUpBase64Alphabet[i] = (byte) ('a' + j);
        }

        for (int i = 52, j = 0; i <= 61; i++, j++) {
        lookUpBase64Alphabet[i] = (byte) ('0' + j);
        }

        lookUpBase64Alphabet[62] = (byte) '+';
        lookUpBase64Alphabet[63] = (byte) '/';
        }

        private static boolean isBase64(byte octect) {
        if (octect == PAD) {
        return true;
        } else if (base64Alphabet[octect] == -1) {
        return false;
        } else {
        return true;
        }
        }


        public static byte[] encodeBase64(byte[] binaryData) {
        return encodeBase64(binaryData, false);
        }


        public static byte[] decode(byte[] pArray) {
        return decodeBase64(pArray);
        }


        public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
        int lengthDataBits = binaryData.length * EIGHTBIT;
        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
        byte encodedData[] = null;
        int encodedDataLength = 0;
        int nbrChunks = 0;

        if (fewerThan24bits != 0) {
        encodedDataLength = (numberTriplets + 1) * 4;
        } else {
        encodedDataLength = numberTriplets * 4;
        }

        if (isChunked) {

        nbrChunks =
        (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
        encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
        }

        encodedData = new byte[encodedDataLength];

        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;

        int encodedIndex = 0;
        int dataIndex = 0;
        int i = 0;
        int nextSeparatorIndex = CHUNK_SIZE;
        int chunksSoFar = 0;

        for (i = 0; i < numberTriplets; i++) {
        dataIndex = i * 3;
        b1 = binaryData[dataIndex];
        b2 = binaryData[dataIndex + 1];
        b3 = binaryData[dataIndex + 2];


        l = (byte) (b2 & 0x0f);
        k = (byte) (b1 & 0x03);

        byte val1 =
        ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
        byte val2 =
        ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
        byte val3 =
        ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);

        encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
        encodedData[encodedIndex + 1] =
        lookUpBase64Alphabet[val2 | (k << 4)];
        encodedData[encodedIndex + 2] =
        lookUpBase64Alphabet[(l << 2) | val3];
        encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];

        encodedIndex += 4;

        if (isChunked) {
        if (encodedIndex == nextSeparatorIndex) {
        System.arraycopy(
        CHUNK_SEPARATOR,
        0,
        encodedData,
        encodedIndex,
        CHUNK_SEPARATOR.length);
        chunksSoFar++;
        nextSeparatorIndex =
        (CHUNK_SIZE * (chunksSoFar + 1)) +
        (chunksSoFar * CHUNK_SEPARATOR.length);
        encodedIndex += CHUNK_SEPARATOR.length;
        }
        }
        }

        dataIndex = i * 3;

        if (fewerThan24bits == EIGHTBIT) {
        b1 = binaryData[dataIndex];
        k = (byte) (b1 & 0x03);
        byte val1 =
        ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
        encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
        encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
        encodedData[encodedIndex + 2] = PAD;
        encodedData[encodedIndex + 3] = PAD;
        } else if (fewerThan24bits == SIXTEENBIT) {

        b1 = binaryData[dataIndex];
        b2 = binaryData[dataIndex + 1];
        l = (byte) (b2 & 0x0f);
        k = (byte) (b1 & 0x03);

        byte val1 =
        ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
        byte val2 =
        ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);

        encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
        encodedData[encodedIndex + 1] =
        lookUpBase64Alphabet[val2 | (k << 4)];
        encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
        encodedData[encodedIndex + 3] = PAD;
        }

        if (isChunked) {
        if (chunksSoFar < nbrChunks) {
        System.arraycopy(
        CHUNK_SEPARATOR,
        0,
        encodedData,
        encodedDataLength - CHUNK_SEPARATOR.length,
        CHUNK_SEPARATOR.length);
        }
        }

        return encodedData;
        }

        public static byte[] decodeBase64(byte[] base64Data) {
        base64Data = discardNonBase64(base64Data);

        if (base64Data.length == 0) {
        return new byte[0];
        }

        int numberQuadruple = base64Data.length / FOURBYTE;
        byte decodedData[] = null;
        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;


        int encodedIndex = 0;
        int dataIndex = 0;
        {
        int lastData = base64Data.length;
        while (base64Data[lastData - 1] == PAD) {
        if (--lastData == 0) {
        return new byte[0];
        }
        }
        decodedData = new byte[lastData - numberQuadruple];
        }

        for (int i = 0; i < numberQuadruple; i++) {
        dataIndex = i * 4;
        marker0 = base64Data[dataIndex + 2];
        marker1 = base64Data[dataIndex + 3];

        b1 = base64Alphabet[base64Data[dataIndex]];
        b2 = base64Alphabet[base64Data[dataIndex + 1]];

        if (marker0 != PAD && marker1 != PAD) {
        //No PAD e.g 3cQl
        b3 = base64Alphabet[marker0];
        b4 = base64Alphabet[marker1];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] =
        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
        decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
        } else if (marker0 == PAD) {
        //Two PAD e.g. 3c[Pad][Pad]
        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        } else if (marker1 == PAD) {
        //One PAD e.g. 3cQ[Pad]
        b3 = base64Alphabet[marker0];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] =
        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
        }
        encodedIndex += 3;
        }
        return decodedData;
        }

        static byte[] discardNonBase64(byte[] data) {
        byte groomedData[] = new byte[data.length];
        int bytesCopied = 0;

        for (int i = 0; i < data.length; i++) {
        if (isBase64(data[i])) {
        groomedData[bytesCopied++] = data[i];
        }
        }

        byte packedData[] = new byte[bytesCopied];

        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);

        return packedData;
        }
        public static byte[] encode(byte[] pArray) {
        return encodeBase64(pArray, false);
        }


        }


        public class ConfigureEncryptAndDecrypt {
        public static final String CHAR_ENCODING = "UTF-8";
        public static final String AES_ALGORITHM = "AES";
        public static final String SHA_256_ALGORITHM = "SHA-256";
        }

        public class ConvertUtils{
        private static final DecimalFormat simpleFormat = new DecimalFormat("####");
        public static String toHex(byte input[]){
        if(input == null) {
        return null;
        }
        StringBuffer output = new StringBuffer(input.length * 2);
        for(int i = 0; i < input.length; i++){
        int current = input[i] & 0xff;
        if(current < 16) {
        output.append("0");
        }
        output.append(Integer.toString(current, 16));
        }

        return output.toString();
        }

        private ConvertUtils(){}
        }


        SHA256 签名算法
        import java.io.UnsupportedEncodingException;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        public class Disguiser {
        public static final String ENCODE = "UTF-8";
        public static String apiDisguise(String message) {
        return apiDisguise(message, ENCODE, ConfigureEncryptAndDecrypt.SHA_256_ALGORITHM);
        }

        public static String apiDisguise(String message, String encoding, String algName) {
        message = message.trim();
        byte value[];
        try {
        value = message.getBytes(encoding);
        } catch (UnsupportedEncodingException e) {
        value = message.getBytes();
        }
        MessageDigest md = null;
        try {
        md = MessageDigest.getInstance(algName);
        } catch (NoSuchAlgorithmException e) {
        logger.error(e.getMessage(), e);
        return null;
        }
        return ConvertUtils.toHex(md.digest(value));
        }
        }

        /**
        * 国密算法SM2 P7 非分离签名
        *
        * @param sourceStr 签名原文
        * @param charset 编码 utf-8
        * @param certInfo 私钥文件内容
        * @param certPwd 私钥密码
        * @return
        */
        public static String p7SignMessage(String sourceStr, String charset, String certInfo, String certPwd) {
        try {
        logger.info("签名原文 sourceStr: " + sourceStr + ", charset: " + charset);
        PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getBytes(StandardCharsets.UTF_8), certPwd);
        X509Cert cert = CertUtil.getCertFromSM2(certInfo.getBytes(StandardCharsets.UTF_8));
        byte[] signByte = new cfca.sadk.util.Signature().p7SignMessageAttach(Mechanism.SM3_SM2,
        sourceStr.getBytes(charset), priKey, cert, SESSION);
        return new String(cfca.sadk.util.Base64.encode(signByte));
        } catch (IOException e) {
        logger.error("生成签名串过程中 io 异常", e);
        throw new IllegalStateException(e);
        } catch (Exception e) {
        logger.error("消息分离签名异常:" + e.getMessage(), e);
        throw new IllegalStateException("签名失败", e);
        }
        }


        
        AES加密算法

        class CryptAES
        {
        protected $cipher = MCRYPT_RIJNDAEL_128;
        protected $mode = MCRYPT_MODE_ECB;
        protected $pad_method = NULL;
        protected $secret_key = '';
        protected $iv = '';

        public function set_cipher($cipher)
        {
        $this->cipher = $cipher;
        }

        public function set_mode($mode)
        {
        $this->mode = $mode;
        }

        public function set_iv($iv)
        {
        $this->iv = $iv;
        }

        public function set_key($key)
        {
        $this->secret_key = $key;
        }

        public function require_pkcs5()
        {
        $this->pad_method = 'pkcs5';
        }

        protected function pad_or_unpad($str, $ext)
        {
        if (is_null($this->pad_method)) {
        return $str;
        } else {
        $func_name = __CLASS__ . '::' . $this->pad_method . '_' . $ext . 'pad';
        if (is_callable($func_name)) {
        $size = mcrypt_get_block_size($this->cipher, $this->mode);
        return call_user_func($func_name, $str, $size);
        }
        }
        return $str;
        }

        protected function pad($str)
        {
        return $this->pad_or_unpad($str, '');
        }

        protected function unpad($str)
        {
        return $this->pad_or_unpad($str, 'un');
        }

        public function encrypt($str)
        {
        $str = $this->pad($str);
        $td = mcrypt_module_open($this->cipher, '', $this->mode, '');

        if (empty($this->iv)) {
        $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        } else {
        $iv = $this->iv;
        }

        mcrypt_generic_init($td, base64_decode($this->secret_key), $iv);
        $cyper_text = mcrypt_generic($td, $str);
        $rt = base64_encode($cyper_text);
        //$rt = bin2hex($cyper_text);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);

        return $rt;
        }

        public function decrypt($str)
        {
        $td = mcrypt_module_open($this->cipher, '', $this->mode, '');

        if (empty($this->iv)) {
        $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        } else {
        $iv = $this->iv;
        }

        mcrypt_generic_init($td, base64_decode($this->secret_key), $iv);
        //$decrypted_text = mdecrypt_generic($td, self::hex2bin($str));
        $decrypted_text = mdecrypt_generic($td, base64_decode($str));
        $rt = $decrypted_text;
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);

        return $this->unpad($rt);
        }

        public static function hex2bin($hexdata)
        {
        $bindata = '';
        $length = strlen($hexdata);
        for ($i = 0; $i < $length; $i += 2) {
        $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
        }
        return $bindata;
        }

        public static function pkcs5_pad($text, $blocksize)
        {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
        }

        public static function pkcs5_unpad($text)
        {
        $pad = ord($text{strlen($text) - 1});
        if ($pad > strlen($text)) return false;
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
        return substr($text, 0, -1 * $pad);
        }
        }

        $keyStr = 'qjrn4B5NuEmh5KScNPMfBw==';
        $plainText = 'hello你好%^$}';

        $aes = new CryptAES();
        $aes->set_key($keyStr);
        $aes->require_pkcs5();
        $encText = $aes->encrypt($plainText);
        $decString = $aes->decrypt($encText);

        echo $encText, "n", $decString;


        SHA256 签名算法

        function SHA256Hex($str)
        {
        $re = hash('sha256', $str, true);
        return bin2hex($re);
        }


        $plainText = 'hello你好%^$}';

        $decString = SHA256Hex($plainText);

        echo $decString;


        
        AES加密算法
        SHA256 签名算法
        using Jzh.PayPlugin.HeLiBao.Models;
        using Newtonsoft.Json;
        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Linq;
        using System.Net;
        using System.Net.Security;
        using System.Reflection;
        using System.Security.Cryptography;
        using System.Security.Cryptography.X509Certificates;
        using System.Text;
        using System.Threading.Tasks;
        using System.Web;

        namespace Jzh.PayPlugin.HeLiBao
        {
        public class HLBPluginUtils
        {
        /// 
        /// 签名字段
        /// 
        public const string SignFiled = "sign";

        private const int Timeout = 100000;


        /// 
        /// 组装请求参数
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static HLBDto ProcessRequestDto(string merchantNo, string aesKey, string signKey, T model) where T :
        HLBRequestBaseDto
        {
        var contentStr = JsonConvert.SerializeObject(model, new JsonSerializerSettings() { NullValueHandling =
        NullValueHandling.Ignore });
        var content = EncryptByAES(contentStr, aesKey);

        var requestDic = ToDic(model);
        var sign = CreateSign(requestDic, signKey);

        var requestDto = new HLBDto()
        {
        content = content,
        sign = sign,
        merchantNo = merchantNo,
        orderNo = model.orderNo,
        productCode = model.productCode
        };
        return requestDto;
        }

        /// 
        /// 创建签名
        /// 
        /// 需要签名的字典数据集
        /// 私钥
        /// 
        public static string CreateSign(Dictionary dictionary, string key)
        {
        var strSign = CreateSignString(dictionary, key);

        //sha256加密
        byte[] SHA256Data = Encoding.UTF8.GetBytes(strSign);
        SHA256Managed Sha256 = new SHA256Managed();
        byte[] Result = Sha256.ComputeHash(SHA256Data);

        return ByteArrayToHexString(Result);
        }
        /// 
        /// 将一个byte数组转换成一个格式化的16进制字符串
        /// 
        /// byte数组
        /// 格式化的16进制字符串
        public static string ByteArrayToHexString(byte[] data)
        {
        StringBuilder sb = new StringBuilder(data.Length * 3);
        foreach (byte b in data)
        {
        //16进制数字
        sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
        }
        return sb.ToString();
        }

        /// 
        /// 生成签名字符串
        /// 
        /// 需要签名的字典数据集
        /// 私钥
        /// 
        public static string CreateSignString(Dictionary dictionary, string key)
        {
        if (dictionary.ContainsKey(SignFiled))
        {
        dictionary.Remove(SignFiled);
        }
        //把字典按Key的字母顺序排序
        dictionary = SortDictionary(dictionary);
        var strSign = key + "," + DictionaryToString(dictionary, ',', false) + "," + key;
        return strSign;
        }

        /// 
        /// AES加密算法
        /// 
        /// 明文字符串
        /// 密钥(32位)
        /// 字符串
        public static string EncryptByAES(string input, string key)
        {
        //Base64.decode
        byte[] keyBytes = Convert.FromBase64String(key);
        byte[] toEncryptArray = Encoding.UTF8.GetBytes(input);

        RijndaelManaged rm = new RijndaelManaged
        {
        Key = keyBytes,
        Mode = CipherMode.ECB,
        Padding = PaddingMode.PKCS7
        };

        ICryptoTransform cTransform = rm.CreateEncryptor();
        Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        return Convert.ToBase64String(resultArray, 0, resultArray.Length);

        //using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        //{
        // aesAlg.Key = keyBytes;
        // //aesAlg.IV = new byte[16];

        // ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
        // using (MemoryStream msEncrypt = new MemoryStream())
        // {
        // using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        // {
        // using (StreamWriter swEncrypt = new StreamWriter(csEncrypt, Encoding.UTF8))
        // {
        // swEncrypt.Write(input);
        // }
        // byte[] bytes = msEncrypt.ToArray();
        // return Convert.ToBase64String(bytes);
        // }
        // }
        //}
        }

        /// 
        /// AES解密
        /// 
        /// 密文字节数组
        /// 密钥(32位)
        /// 返回解密后的字符串
        public static string DecryptByAES(string input, string key)
        {
        byte[] inputBytes = Convert.FromBase64String(input);
        byte[] keyBytes = Convert.FromBase64String(key);

        RijndaelManaged rm = new RijndaelManaged
        {
        Key = keyBytes,
        Mode = CipherMode.ECB,
        Padding = PaddingMode.PKCS7
        };

        ICryptoTransform cTransform = rm.CreateDecryptor();
        Byte[] resultArray = cTransform.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
        return Encoding.UTF8.GetString(resultArray);

        //using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        //{
        // aesAlg.Key = keyBytes;
        // //aesAlg.IV = new byte[16];

        // ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
        // using (MemoryStream msEncrypt = new MemoryStream(inputBytes))
        // {
        // using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
        // {
        // using (StreamReader srEncrypt = new StreamReader(csEncrypt, Encoding.UTF8))
        // {
        // return srEncrypt.ReadToEnd();
        // }
        // }
        // }
        //}
        }


        private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
        {
        return true;
        }

        /// 
        /// 执行HTTP POST请求。
        /// 
        /// 请求地址
        /// 请求参数
        /// HTTP响应
        public static String DoPost(String url, string paramUrl)
        {
        var req = (HttpWebRequest)WebRequest.Create(url);
        req.ServicePoint.Expect100Continue = false;
        req.Method = "POST";
        req.KeepAlive = true;
        //req.UserAgent = "InterfaceService.JdSDK.NET V1.0 by StarPeng";
        req.Timeout = Timeout;
        req.ContentType = "application/json;charset=utf-8";
        //var paramUrl = DictionaryToString(parameters, '&', true);
        Byte[] postData = Encoding.UTF8.GetBytes(paramUrl);
        Stream reqStream = req.GetRequestStream();
        reqStream.Write(postData, 0, postData.Length);
        reqStream.Close();

        ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
        var rsp = (HttpWebResponse)req.GetResponse();
        var encoding = Encoding.GetEncoding(rsp.CharacterSet);
        return GetResponseAsString(rsp, encoding);
        }


        /// 
        /// 把响应流转换为文本。
        /// 
        /// 响应流对象
        /// 编码方式
        /// 响应文本
        private static String GetResponseAsString(HttpWebResponse rsp, Encoding encoding)
        {
        Stream stream = null;
        StreamReader reader = null;

        try
        {
        // 以字符流的方式读取HTTP响应
        stream = rsp.GetResponseStream();
        reader = new StreamReader(stream, encoding);
        return reader.ReadToEnd();
        }
        finally
        {
        // 释放资源
        if (reader != null) reader.Close();
        if (stream != null) stream.Close();
        if (rsp != null) rsp.Close();
        }
        }

        /// 
        /// 把所有参数名和参数值串在一起
        /// 
        /// 字典数据集
        /// 分隔符
        /// 是否使用UrlEncode编码
        /// 
        public static string DictionaryToString(Dictionary dictionary, char separator, bool isUrlEncode)
        {
        var query = new StringBuilder();
        var count = 0;
        var len = dictionary.Keys.Count;
        foreach (var key in dictionary.Keys)
        {
        count++;
        var value = dictionary[key];
        if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
        {
        if (isUrlEncode)
        value = HttpUtility.UrlEncode(value);
        if (count != len)
        {
        query.AppendFormat("{0}={1}{2}", key, value, separator);
        }
        else
        {
        query.AppendFormat("{0}={1}", key, value);
        }
        }
        }

        return query.ToString();
        }

        /// 
        /// 把字典按Key的字母顺序排序
        /// 
        /// 字典数据集
        /// 
        public static Dictionary SortDictionary(Dictionary dictionary)
        {
        Dictionary newDic = dictionary.OrderBy(o => o.Key, StringComparer.Ordinal).ToDictionary(o =>
        o.Key, p => p.Value);

        return newDic;
        }

        /// 
        /// 将字典数据集转换为json数据
        /// 
        /// 字典数据集
        /// 
        public static string DictionaryToJson(List> listPaymentBillArray)
        {
        if (listPaymentBillArray == null || listPaymentBillArray.Count < 1)
        {
        return "[]";
        }
        var json = new StringBuilder();
        foreach (var paymentBillArray in listPaymentBillArray)
        {
        var temp = "";
        foreach (var key in paymentBillArray.Keys)
        {
        temp += string.Format("\"{0}\":\"{1}\",", key, paymentBillArray[key]);
        }
        if (temp.Length > 0)
        {
        temp = temp.TrimEnd(new[] { ',' });
        json.Append("{" + temp + "},");
        }

        }
        if (json.Length > 0)
        {
        return "[" + json.ToString().TrimEnd(new[] { ',' }) + "]";
        }
        else
        {
        return "[]";
        }
        }


        /// 
        /// 构造表单提交HTML
        /// 
        /// 字典数据集
        /// 提交方式,是否为post提交
        /// 是否需要提交
        /// 输出 表单提交HTML文本
        public static string BuildForm(string serviceApiUrlForPay, Dictionary dictionary, bool isPost,
        bool isSubmit)
        {
        var sbHtml = new StringBuilder();
        sbHtml.Append("
");//style='display:none;' //提交方式(GET与POST二必选一) if (!isPost) { //GET方式传递 sbHtml.Append("
"); } else { //POST方式传递 sbHtml.Append(""); } foreach (KeyValuePair temp in dictionary) { if (!string.IsNullOrEmpty(temp.Key) && !string.IsNullOrEmpty(temp.Value)) sbHtml.Append(""); } //submit按钮控件请不要含有name属性 sbHtml.Append(""); if (isSubmit) { sbHtml.Append(""); } sbHtml.Append("
"); return sbHtml.ToString(); } /// 获取GET过来通知消息,并以“参数名=参数值”的形式组成数组 /// request回来的信息组成的数组 public static Dictionary GetDictionaryRequestGet(HttpRequest request) { int i = 0; var sArray = new Dictionary(); var coll = request.QueryString; var requestItem = coll.AllKeys; for (i = 0; i < requestItem.Length; i++) { sArray.Add(requestItem[i], request.QueryString[requestItem[i]]); } return sArray; } /// 把对象转成字典,用作请求(去除为空的值) public static Dictionary ToDic(object input, List ignores = null) { Dictionary result = null; if (input == null) return result; Type type = input.GetType(); if (type.IsValueType) return result; PropertyInfo[] propertyInfoArry = input.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); if (propertyInfoArry == null || propertyInfoArry.Length > 0 == false) return result; result = new Dictionary(); foreach (PropertyInfo propertyInfo in propertyInfoArry) { string curValue = ""; if (propertyInfo != null) { if (ignores == null) ignores = new List(); if (!ignores.Contains(propertyInfo.Name)) { object propertyValue = propertyInfo.GetValue(input, null); if (propertyValue != null) { curValue = propertyValue.GetType() == typeof(bool) ? propertyValue.ToString().ToLower() : propertyValue.ToString(); result.Add(propertyInfo.Name, curValue); } } } } return result; } } }