API Reference

簽名

簽名規則:

1:簽名中出現&,為連接符,非運算符,簽名規則:body體中signature不參與簽名,但header中的timestamp參與body簽名

2:其餘以參數名的字典升序排序(ascii碼升序),且值不為空,並拼接"key=value"格式,多參數之間以&鏈接,得到A

  • 值為空,不參與簽名
  • 只有數字類型以及字符串類型,參與簽名
  • 特別注意:請求參數體中需要增加unitId,用來確定目前客戶識別資訊

3:timestamp=時間戳,得到B,然後B&A得到C

4:將C使用MD5加密並全部字母變為大寫,得到最終值D,即為signature值

5:body中需要增加signature,然後對body中數據,轉為json後,進行encode轉碼,得到E

6:用公司公鑰將E進行加密,得到F,其請求key值為data

  • F長度以100長度分割,進行分段加密,加密後的值,需以用","進行拼接

7:最終請求參數結構如下:

//Request header
{
    "timestamp": 11111131331,
    "trace": "xxxxxx"
}
 
//Request body
{
    "data": F
}

簽名示例:

1:a=1&b=2&c=3×tamp=11111131331,即為A

2:timestamp=11111131331,即為B

3: B&A 即 timestamp=11111131331&a=1&b=2&c=3×tamp=11111131331 為最終C取值

4:將C進行MD5加密並全部字母變為大寫,得到最終簽名密鑰

5:對所有body參數,轉為json後,encode轉碼,得到E

6:RSA加密

  • 用公司公鑰將E加密,得到F
  • 若E長度>100,則RSA(E[0-100])=E1,RSA[E[剩余長度]]=E2,最終F=E1,E2
//Request header
{
   "timestamp": 11111131331,
   "trace": "xxxxxx"
}

//Request body
{
   "a": 1,
   "b": 2,
   "c": "3",
   "signature": "44b3a042-dd5d-4796-92e1-651927b6ada9"
}

JAVA版加密方式如下,请参考encryptByPublicKey

JAVA版解密方式如下,请参考decrypt4LongTextByPublicKey

package com.cats.common.core.util;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cats.common.core.base.enums.CommonLogType;
import com.cats.common.core.log.LogUtil;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
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.*;

@Slf4j
public class RSAUtil {
    public static final String RSA_ALGORITHM = "RSA";
    public static final String UTF8_STR = "UTF-8";
    public static final Integer KEY_SIZE = 1024;
    public static final String TIME_STAMP = "timestamp";
    public static final String JOIN_CHAR_1 = "=";
    public static final String JOIN_CHAR_2 = "&";
    public static final String JOIN_CHAR_3 = ",";

    /**
     * 随机生成密钥对
     *
     * @throws NoSuchAlgorithmException
     */
    public static List<String> genKeyPair() {
        List<String> result = new ArrayList<>();
        try {
            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            // 初始化密钥对生成器,密钥大小为96-1024位
            keyPairGen.initialize(KEY_SIZE, new SecureRandom());
            // 生成一个密钥对,保存在keyPair中
            KeyPair keyPair = keyPairGen.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())));
            // 将公钥和私钥保存到
            result.add(publicKeyString);
            result.add(privateKeyString);
        } catch (NoSuchAlgorithmException e) {
            log.error("RSAUtil genKeyPair ", e);
        }
        return result;
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        String outStr = null;
        try {
            //base64编码的公钥
            byte[] decoded = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
            //RSA加密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(UTF8_STR)));
        } catch (Exception e) {
            log.error("RSAUtil encrypt ", e);
        }
        return outStr;
    }

    /**
     * RSA加密(私钥)
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encryptByPrivateKey(String str, String privateKey) throws Exception {
        String outStr = null;
        try {
            //base64编码的公钥
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA加密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(UTF8_STR)));
        } catch (Exception e) {
            log.error("RSAUtil encryptByPrivateKey ", e);
        }
        return outStr;
    }

    /**
     * RSA解密(公钥)
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decryptByPublicKey(String str, String publicKey) {
        String outStr = null;
        try {
            //64位解码加密后的字符串
            byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
            //base64编码的私钥
            byte[] decoded = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (Exception e) {
            log.error("RSAUtil decryptByPublicKey ", e);
        }
        return outStr;
    }

    /**
     * RSA私钥解密(支持超长文本)
     *
     * @param str       加密字符串
     * @param publicKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt4LongTextByPublicKey(String str, String publicKey) {
        try {
            if (StringUtils.isEmpty(str)) {
                return null;
            }
            StringBuilder sb = new StringBuilder();
            String[] tempStr = str.split(JOIN_CHAR_3);
            for (String s : tempStr) {
                if (StringUtils.isEmpty(s)) {
                    return s;
                }
                String temp = decryptByPublicKey(s, publicKey);
                if (StringUtils.isEmpty(temp)) {
                    return temp;
                }
                sb.append(temp);
            }
            return URLDecoder.decode(sb.toString(), "UTF-8");
        } catch (Exception e) {
            LogUtil.error(CommonLogType.CODE_ERROR, "decrypt4LongTextByPublicKey解密出现异常", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 数据加密+签名(私钥)
     *
     * @param params     params
     * @param timestamp  timestamp
     * @param privateKey privateKey
     * @return String
     * @throws Exception
     */
    public static String encryptByPrivateKey(Map<String, Object> params, String timestamp, String privateKey, Map<String, Object> noSignatureMap) throws Exception {
        StringBuilder sb = new StringBuilder();
        Map<String, Object> sortParams = new TreeMap<>(params);
        sortParams.forEach((key, value) -> {
            if (U.isNotBlank(value)) {
                sb.append(key).append("=").append(value).append("&");
            }
        });
        String sortedBody = sb.substring(0, sb.length() - 1),
                salt = "timestamp=" + timestamp,
                signatureBeforeMD5 = salt + "&" + sortedBody;
        String signature = DigestUtils.md5Hex(signatureBeforeMD5).toUpperCase();
        sortParams.put("signature", signature);
        if (Objects.nonNull(noSignatureMap)) {
            sortParams.putAll(noSignatureMap);
        }
        String toJson = JSONUtil.toJsonStr(sortParams);
        log.info("sortedBody={},salt={},signatureBeforeMD5={},signature={},toJson={}", sortedBody, salt, signatureBeforeMD5, signature, toJson);
        Iterable<String> chunks = Splitter.fixedLength(100).split(URLEncoder.encode(toJson, "UTF-8"));
        Iterator<String> iterator = chunks.iterator();
        StringBuilder sbEncryption = new StringBuilder();
        while (iterator.hasNext()) {
            String encrypt = encryptByPrivateKey(iterator.next(), privateKey);
            sbEncryption.append(encrypt).append(",");
        }
        Map<String, String> bodyData = Maps.newHashMap();
        bodyData.put("data", sbEncryption.substring(0, sbEncryption.length() - 1));
        return JSONUtil.toJsonStr(bodyData);
    }


    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str, String privateKey) {
        String outStr = null;
        try {
            //64位解码加密后的字符串
            byte[] inputByte = Base64.decodeBase64(str.getBytes(UTF8_STR));
            //base64编码的私钥
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (Exception e) {
            log.error("RSAUtil decrypt ", e);
        }
        return outStr;
    }


    /**
     * RSA私钥解密(支持超长文本)
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt4LongText(String str, String privateKey) {
        try {
            if (StringUtils.isEmpty(str)) {
                return null;
            }
            StringBuilder sb = new StringBuilder();
            String[] tempStr = str.split(JOIN_CHAR_3);
            for (String s : tempStr) {
                if (StringUtils.isEmpty(s)) {
                    return s;
                }
                String temp = decrypt(s, privateKey);
                //LogUtil.info(CommonLogType.CODE_COMMON, "decrypt4LongText分段解密,原始密文>>>", s, "解密结果>>>", temp);
                if (StringUtils.isEmpty(temp)) {
                    return temp;
                }
                sb.append(temp);
            }
            return URLDecoder.decode(sb.toString(), "UTF-8");
        } catch (Exception e) {
            LogUtil.error(CommonLogType.CODE_ERROR, "decrypt4LongText解密出现异常", e.getMessage(), e);
            return null;
        }
    }


    /**
     * 验证签名信息
     *
     * @param params  参数信息
     * @param signKey 加密之后key
     * @return 延签是否正常
     */
    public static boolean verifySign(Map<String, Object> params, String signKey) {
        try {
            if (StringUtils.isEmpty(params) || StringUtils.isEmpty(signKey)) {
                return Boolean.FALSE;
            }
            Object signValue = "";
            if (params.containsKey(signKey)) {
                signValue = params.remove(signKey);
            }
            if (StringUtils.isEmpty(signValue)) {
                return Boolean.FALSE;
            }
            StringBuilder sb = new StringBuilder().append(TIME_STAMP).append(JOIN_CHAR_1).append(params.get(TIME_STAMP));
            // 将参数以参数名的字典升序排序
            Map<String, Object> sortParams = new TreeMap<>(params);
            // 遍历排序的字典,并拼接"key=value"格式
            for (Map.Entry<String, Object> entry : sortParams.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (!StringUtils.isEmpty(value) && (value instanceof Number || value instanceof String))
                    sb.append(JOIN_CHAR_2).append(key).append(JOIN_CHAR_1).append(value);
            }
            String md5 = DigestUtils.md5Hex(sb.toString()).toUpperCase();
            LogUtil.info(CommonLogType.CODE_COMMON, "verifySign验签参数", sb.toString(), md5);
            return signValue.equals(md5);
        } catch (Exception e) {
            LogUtil.error(CommonLogType.CODE_ERROR, "verifySign验签出现问题", e.getMessage(), e);
            return false;
        }
    }


    /**
     * 数据加密+签名(公钥)
     *
     * @param params    params
     * @param timestamp timestamp
     * @param publicKey publicKey
     * @return String
     * @throws Exception
     */
    public static String encryptByPublicKey(Map<String, Object> params, String timestamp, String publicKey, Map<String, Object> noSignatureMap) throws Exception {
        StringBuilder sb = new StringBuilder();
        Map<String, Object> sortParams = new TreeMap<>(params);
        sortParams.forEach((key, value) -> {
            if (U.isNotBlank(value)) {
                sb.append(key).append("=").append(value).append("&");
            }
        });
        String sortedBody = sb.substring(0, sb.length() - 1),
                salt = "timestamp=" + timestamp,
                signatureBeforeMD5 = salt + "&" + sortedBody;
        String signature = DigestUtils.md5Hex(signatureBeforeMD5).toUpperCase();
        sortParams.put("signature", signature);
        if (Objects.nonNull(noSignatureMap)) {
            sortParams.putAll(noSignatureMap);
        }
        String toJson = JSONUtil.toJsonStr(sortParams);
        log.info("sortedBody={},salt={},signatureBeforeMD5={},signature={},toJson={}", sortedBody, salt, signatureBeforeMD5, signature, toJson);
        Iterable<String> chunks = Splitter.fixedLength(100).split(URLEncoder.encode(toJson, "UTF-8"));
        Iterator<String> iterator = chunks.iterator();
        StringBuilder sbEncryption = new StringBuilder();
        while (iterator.hasNext()) {
            String encrypt = encrypt(iterator.next(), publicKey);
            sbEncryption.append(encrypt).append(",");
        }
        Map<String, String> bodyData = Maps.newHashMap();
        bodyData.put("data", sbEncryption.substring(0, sbEncryption.length() - 1));
        return JSONUtil.toJsonStr(bodyData);
    }


    public static void main(String[] args) throws Exception {
        //生成公钥和私钥
        List<String> list = genKeyPair();
        //加密字符串
        System.out.println("随机生成的公钥为:" + list.get(0));
        System.out.println("随机生成的私钥为:" + list.get(1));

        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, Object> map = Maps.newTreeMap();
        map.put("userName", "abc");
        map.put("currency", "CNY");
        map.put("timestamp", timestamp);

        String encryptStr = encryptByPublicKey(map, timestamp, list.get(0), null);
        System.out.println("公钥加密后信息," + encryptStr);
        JSONObject jsonObject = JSONUtil.parseObj(encryptStr);
        String decryptStr = decrypt4LongText(jsonObject.getStr("data"), list.get(1));
        System.out.println("【私钥】解密密后的字符串为:" + decryptStr);
    }

}
English
Powered by Localize
中文(简体)