From 587a1f5739219a0caf324e10f87d2a0c6ed11e3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=88=E4=B9=8B=E7=8D=A0?= <1224073217@qq.com>
Date: Thu, 17 Sep 2020 23:04:04 +0800
Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=BF=E9=87=8C?=
=?UTF-8?q?=E4=BA=91=E3=80=81=E8=85=BE=E8=AE=AF=E4=BA=91API=E6=97=B6?=
=?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E7=AD=BE=E5=90=8D=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cn/hutool/crypto/AliYunSignature.java | 107 ++++++++++++++++++
.../hutool/crypto/TencentCloudSignature.java | 91 +++++++++++++++
2 files changed, 198 insertions(+)
create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/AliYunSignature.java
create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/TencentCloudSignature.java
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/AliYunSignature.java b/hutool-crypto/src/main/java/cn/hutool/crypto/AliYunSignature.java
new file mode 100644
index 0000000000..5f2dd2282f
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/AliYunSignature.java
@@ -0,0 +1,107 @@
+package cn.hutool.crypto;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Filter;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.TreeMap;
+
+/**
+ *
构建请求 阿里云API 时需要用到的签名
+ * 2020-09-17 21:02
+ *
+ * @author Dan
+ **/
+public class AliYunSignature {
+
+ /**
+ * 初始化签名参数
+ * 组建阿里云签名常用的公共请求参数
+ *
+ * @param accessKeyId 访问密钥ID,AccessKey用于调用API
+ * @return 返回一个Map,包含阿里云签名的公共请求参数
+ */
+ public static Map signatureParameterInitialization(String accessKeyId) {
+ // 构建请求参数
+ Map parameters = new TreeMap<>();
+ // 第一部分由系统参数组成
+ parameters.put("SignatureMethod", "HMAC-SHA1");
+ parameters.put("SignatureNonce", IdUtil.objectId());
+ parameters.put("AccessKeyId", accessKeyId);
+ parameters.put("SignatureVersion", "1.0");
+ parameters.put("Timestamp", DateUtil.format(new DateTime(TimeZone.getTimeZone("GMT+:08:00")), "yyyy-MM-dd'T'HH:mm:ss'Z'"));
+ parameters.put("Format", "json");
+ return parameters;
+ }
+
+ /**
+ * 生成阿里云API请求签名信息
+ *
+ * @param parameters 参与签名构建的参数信息
+ * @param accessKeySecret 加密签名字符串和服务器端验证签名字符串的密钥
+ * @param domain 产品域名
+ * @return 返回构建的签名、参数、请求地址
+ * {
+ * "signature":"构建的签名信息",
+ * "parameters":"请求参数信息",
+ * "requestUrl":"最终拼接的url地址"
+ * }
+ */
+ public static JSONObject getAliYunSignature(Map parameters, String accessKeySecret, String domain) {
+
+ // 过滤Map中为空的参数
+ parameters = MapUtil.filter(parameters, (Filter>) o -> StrUtil.isNotBlank(o.getValue()));
+
+ try {
+ // 构造待签名的字符串
+ Iterator it = parameters.keySet().iterator();
+ StringBuilder sortQueryStringTmp = new StringBuilder();
+ while (it.hasNext()) {
+ String key = it.next();
+ sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(parameters.get(key)));
+ }
+ // 构建签名字符串
+ String stringToSign = "GET&" + specialUrlEncode("/") + "&" + specialUrlEncode(sortQueryStringTmp.substring(1));
+ // 构建签名
+ Mac mac = Mac.getInstance("HmacSHA1");
+ mac.init(new SecretKeySpec(StrUtil.format("{}&", accessKeySecret).getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
+ byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+ String sign = new sun.misc.BASE64Encoder().encode(signData);
+ // 最终构建的签名数据
+ String signature = specialUrlEncode(sign);
+ JSONObject jsonObject = new JSONObject();
+ // 签名最后也要做特殊URL编码
+ jsonObject.set("signature", signature);
+ jsonObject.set("parameters", sortQueryStringTmp);
+ jsonObject.set("requestUrl", StrUtil.format("http://{}/?Signature={}{}", domain, signature, sortQueryStringTmp));
+ return jsonObject;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new JSONObject();
+ }
+ }
+
+ /**
+ * 构造待签名的请求串
+ * 一个特殊的URL编码这个是POP特殊的一种规则
+ * 即在一般的URLEncode后再增加三种字符替换:加号 (+)替换成 %20、星号 (*)替换成 %2A、 %7E 替换回波浪号 (~)参考代码如下
+ *
+ * @param value 需要构造的url值
+ * @return 返回构造结果
+ * @throws Exception 抛出异常
+ */
+ private static String specialUrlEncode(String value) throws Exception {
+ return URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
+ }
+}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/TencentCloudSignature.java b/hutool-crypto/src/main/java/cn/hutool/crypto/TencentCloudSignature.java
new file mode 100644
index 0000000000..75b20ea684
--- /dev/null
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/TencentCloudSignature.java
@@ -0,0 +1,91 @@
+package cn.hutool.crypto;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * 构建请求 腾讯云API 时需要用到的签名
+ * 2020-09-17 20:53
+ *
+ * @author Dan
+ **/
+public class TencentCloudSignature {
+
+ /**
+ * 生成腾讯云API请求签名信息
+ *
+ * @param parameters 参与签名构建的参数信息
+ * @param endpoint 请求域名信息 [例: sms.tencentcloudapi.com]
+ * @param secretId 腾讯云账户密钥对secretId
+ * @param secretKey 腾讯云账户密钥对secretKey
+ * @return 返回构建的签名、参数、时间戳
+ * {
+ * "signature":"构建的签名信息",
+ * "timestamp":"生成签名时的时间秒数"
+ * }
+ */
+ public static JSONObject getTencentCloudSignature(Map parameters, String endpoint, String secretId, String secretKey) {
+ // 拼接规范请求串
+ String canonicalRequest = StrUtil.format("POST\n/\n\n{}\ncontent-type;host\n{}",
+ StrUtil.format("content-type:application/json; charset=utf-8\nhost:{}\n", endpoint),
+ sha256Hex(JSONUtil.toJsonStr(parameters)));
+
+ // 计算时间
+ String timestamp = String.valueOf(DateUtil.currentSeconds());
+ String date = DateUtil.format(new DateTime(TimeZone.getTimeZone("UTC")), "yyyy-MM-dd");
+ String credentialScope = StrUtil.format("{}/{}/tc3_request", date, endpoint.split("\\.")[0]);
+ String stringToSign = StrUtil.format("TC3-HMAC-SHA256\n{}\n{}\n{}",
+ timestamp, credentialScope,
+ sha256Hex(canonicalRequest));
+
+ // 计算签名
+ byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(StandardCharsets.UTF_8), date);
+ byte[] secretService = hmac256(secretDate, endpoint.split("\\.")[0]);
+ byte[] secretSigning = hmac256(secretService, "tc3_request");
+ String authorization = StrUtil.format("TC3-HMAC-SHA256 Credential={}/{}, SignedHeaders=content-type;host, Signature={}",
+ secretId, credentialScope,
+ DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase());
+
+ // 构建返回信息
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.set("signature", authorization);
+ jsonObject.set("timestamp", timestamp);
+ return jsonObject;
+ }
+
+ private static byte[] hmac256(byte[] key, String msg) {
+ try {
+ Mac mac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
+ mac.init(secretKeySpec);
+ return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
+ } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private static String sha256Hex(String s) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8));
+ return DatatypeConverter.printHexBinary(d).toLowerCase();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
--
Gitee
From 5b5b271683114416eabb140e3a3ce125011e77de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=88=E4=B9=8B=E7=8D=A0?= <1224073217@qq.com>
Date: Thu, 17 Sep 2020 23:04:58 +0800
Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=BF=E9=87=8C?=
=?UTF-8?q?=E4=BA=91=E3=80=81=E8=85=BE=E8=AE=AF=E4=BA=91API=E6=97=B6?=
=?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E7=AD=BE=E5=90=8D=E6=B5=8B=E8=AF=95?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../crypto/test/AliYunSignatureTest.java | 102 +++++++++++++++++
.../test/TencentCloudSignatureTest.java | 106 ++++++++++++++++++
2 files changed, 208 insertions(+)
create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/AliYunSignatureTest.java
create mode 100644 hutool-crypto/src/test/java/cn/hutool/crypto/test/TencentCloudSignatureTest.java
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/AliYunSignatureTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/AliYunSignatureTest.java
new file mode 100644
index 0000000000..f60bb5e449
--- /dev/null
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/AliYunSignatureTest.java
@@ -0,0 +1,102 @@
+package cn.hutool.crypto.test;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Console;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.crypto.AliYunSignature;
+import cn.hutool.json.JSONObject;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.TreeMap;
+
+/**
+ * 阿里云API签名测试
+ * 获取ID、SECRET: https://ram.console.aliyun.com/manage/ak?spm=5176.12818093.nav-right.dak.488716d00nyfCh
+ * 2020-09-17 21:07
+ *
+ * @author Dan
+ **/
+public class AliYunSignatureTest {
+
+ /**
+ * 访问密钥ID 用于调用API
+ */
+ private static final String ACCESSKEY_ID = "";
+
+ /**
+ * 密钥
+ */
+ private static final String ACCESSKEY_SECRET = "";
+
+ /**
+ * 短信发送
+ */
+ @Test
+ public void sendSms() {
+ JSONObject templateParam = new JSONObject();
+ templateParam.set("code", "123456");
+ templateParam.set("time", "5");
+ // 构建请求参数
+ Map parameters = AliYunSignature.signatureParameterInitialization(ACCESSKEY_ID);
+ // 第二部分由业务API参数组成
+ parameters.put("Action", "SendSms");
+ parameters.put("Version", "2017-05-25");
+ parameters.put("RegionId", "cn-hangzhou");
+ parameters.put("PhoneNumbers", "+8613844444444");
+ parameters.put("SignName", "hutool");
+ parameters.put("TemplateCode", "SMS_199222222");
+ parameters.put("TemplateParam", templateParam.toString());
+ parameters.put("OutId", "");
+ parameters.put("SmsUpExtendCode", "");
+ Console.log(AliYunSignature.getAliYunSignature(parameters, ACCESSKEY_SECRET, "dysmsapi.aliyuncs.com"));
+ }
+
+ /**
+ * 获取云服务器信息
+ */
+ @Test
+ public void getEcs() {
+ Map parameters = new TreeMap() {{
+ // 第一部分由系统参数组成
+ put("SignatureMethod", "HMAC-SHA1");
+ put("SignatureNonce", IdUtil.objectId());
+ put("AccessKeyId", ACCESSKEY_ID);
+ put("SignatureVersion", "1.0");
+ put("Timestamp", DateUtil.format(new DateTime(TimeZone.getTimeZone("GMT+:08:00")), "yyyy-MM-dd'T'HH:mm:ss'Z'"));
+ put("Format", "json");
+ // 第二部分由业务API参数组成
+ put("Action", "DescribeInstances");
+ put("Version", "2014-05-26");
+ put("RegionId", "cn-hangzhou");
+ }};
+ Console.log(AliYunSignature.getAliYunSignature(parameters, ACCESSKEY_SECRET, "ecs.aliyuncs.com"));
+ }
+
+ /**
+ * 设置安全组
+ */
+ @Test
+ public void setSecurityGroup() {
+ Map parameters = new TreeMap() {{
+ // 第一部分由系统参数组成
+ put("SignatureMethod", "HMAC-SHA1");
+ put("SignatureNonce", IdUtil.objectId());
+ put("AccessKeyId", ACCESSKEY_ID);
+ put("SignatureVersion", "1.0");
+ put("Timestamp", DateUtil.format(new DateTime(TimeZone.getTimeZone("GMT+:08:00")), "yyyy-MM-dd'T'HH:mm:ss'Z'"));
+ put("Format", "json");
+ // 第二部分由业务API参数组成
+ put("Action", "AuthorizeSecurityGroup");
+ put("Version", "2014-05-26");
+ put("IpProtocol", "tcp");
+ put("PortRange","8000/9600");
+ put("RegionId","cn-hangzhou");
+ put("SecurityGroupId","sg-bp17zst8hsu4rpwy2l37");
+ put("SourceCidrIp","0.0.0.0/0");
+ }};
+ Console.log(AliYunSignature.getAliYunSignature(parameters, ACCESSKEY_SECRET, "ecs.aliyuncs.com"));
+ }
+}
diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/test/TencentCloudSignatureTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/test/TencentCloudSignatureTest.java
new file mode 100644
index 0000000000..db55047e2c
--- /dev/null
+++ b/hutool-crypto/src/test/java/cn/hutool/crypto/test/TencentCloudSignatureTest.java
@@ -0,0 +1,106 @@
+package cn.hutool.crypto.test;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.crypto.TencentCloudSignature;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 腾讯云API签名测试
+ * 2020-09-17 21:55
+ *
+ * @author Dan
+ **/
+public class TencentCloudSignatureTest {
+
+ /**
+ * 访问密钥ID 用于调用API
+ */
+ private static final String SECRET_ID = "";
+
+ /**
+ * 密钥
+ */
+ private static final String SECRET_KEY = "";
+
+ /**
+ * 短信发送
+ */
+ @Test
+ public void sendSms() {
+ // 需要发送的电话(需要+86)
+ JSONArray phoneNumberSet = new JSONArray();
+ phoneNumberSet.add("+8613844444444");
+ phoneNumberSet.add("+8613855555555");
+
+ // 模板参数
+ JSONArray templateParam = new JSONArray();
+ templateParam.add("123456");
+ templateParam.add("3");
+
+ // 构建请求参数
+ Map parameters = new HashMap(8) {{
+ put("PhoneNumberSet", phoneNumberSet);
+ put("TemplateID", "777777");
+ put("TemplateParamSet", templateParam);
+ put("SmsSdkAppid", "4444444444");
+ put("Sign", "hutool");
+ put("ExtendCode", "");
+ put("SessionContext", "");
+ put("SenderId", "");
+ }};
+
+ // 构建签名信息
+ JSONObject signature = TencentCloudSignature.getTencentCloudSignature(parameters, "sms.tencentcloudapi.com", SECRET_ID, SECRET_KEY);
+ Console.log(signature);
+
+ // 构建链式请求
+// String result = HttpRequest.post("https://sms.tencentcloudapi.com/")
+// .header("Content-Type", "application/json; charset=utf-8")
+// .header("Host", "sms.tencentcloudapi.com")
+// .header("Authorization", signature.getStr("signature"))
+// .header("X-TC-Action", "SendSms")
+// .header("X-TC-Timestamp", signature.getStr("timestamp"))
+// .header("X-TC-Version", "2019-07-11")
+// .header("X-TC-RequestClient", "SDK_JAVA_3.1.130")
+// .header("X-TC-Region", "")
+// .body(JSONUtil.toJsonStr(parameters).getBytes(StandardCharsets.UTF_8))
+// .timeout(-1).execute().body();
+// Console.log(result);
+ }
+
+ /**
+ * 获取云服务器信息
+ */
+ @Test
+ public void getCvm() {
+
+ // 构建请求参数
+ Map parameters = new HashMap(2) {{
+ // 不是必须参数
+ put("Offset",0);
+ put("Limit",20);
+ }};
+
+ // 构建签名信息
+ JSONObject signature = TencentCloudSignature.getTencentCloudSignature(parameters, "cvm.tencentcloudapi.com", SECRET_ID, SECRET_KEY);
+ Console.log(signature);
+
+// String result = HttpRequest.post("https://sms.tencentcloudapi.com/")
+// .header("Content-Type", "application/json; charset=utf-8")
+// .header("Host", "cvm.tencentcloudapi.com")
+// .header("Authorization", signature.getStr("signature"))
+// .header("X-TC-Action", "DescribeInstances")
+// .header("X-TC-Timestamp", signature.getStr("timestamp"))
+// .header("X-TC-Version", "2017-03-12")
+// .header("X-TC-RequestClient", "SDK_JAVA_3.1.130")
+// .header("X-TC-Region", "ap-shanghai")
+// .body(JSONUtil.toJsonStr(parameters).getBytes(StandardCharsets.UTF_8))
+// .timeout(-1).execute().body();
+// Console.log(result);
+ }
+}
--
Gitee
From f2eea324a0fe7306025b60dc337c5a2e76d8de41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=88=E4=B9=8B=E7=8D=A0?= <1224073217@qq.com>
Date: Thu, 17 Sep 2020 23:06:46 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=BF=E9=87=8C?=
=?UTF-8?q?=E4=BA=91=E3=80=81=E8=85=BE=E8=AE=AF=E4=BA=91API=E6=97=B6?=
=?UTF-8?q?=E7=9A=84=E8=AF=B7=E6=B1=82=E7=AD=BE=E5=90=8D=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hutool-crypto/pom.xml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml
index 46cb014300..6b6883aba3 100644
--- a/hutool-crypto/pom.xml
+++ b/hutool-crypto/pom.xml
@@ -27,6 +27,11 @@
hutool-core
${project.parent.version}
+
+ cn.hutool
+ hutool-json
+ ${project.parent.version}
+
org.bouncycastle
bcprov-jdk15to18
--
Gitee