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
中文(简体)