A.8 HTTP(S)终端节点使用样例
A.8.4 示例代码
Java 语言
验证消息是否有效,其中signing_cert_url、signature是从HTTP(S)消息格式描述获取 的值,message为特定消息的签名键值。以下为示例代码,仅供参考。
private static void isMessageValid(String signing_cert_url, String signature, Map<String, String> message) { InputStream in = null;
try {
URL url = new URL(signing_cert_url);
in = url.openStream();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
Signature sig = Signature.getInstance(cert.getSigAlgName());
sig.initVerify(cert.getPublicKey());
sig.update(buildSignMessage(message).getBytes("UTF-8"));
byte[] sigByte = Base64.getDecoder().decode(signature);
使用Java 8 版本以下的用户,可以使用第三方jar包commons-codec.jar进行Base64解码。
并将上述样例代码中的"byte[] sigByte = Base64.getDecoder().decode(signature);"调整为
"byte[] sigByte = Base64.decodeBase64(signature);"。
构建校验签名的示例代码
private static String buildSignMessage(Map<String,String> msg) { String type = msg.get("type");
String message = null;
if ("Notification".equals(type)){
message = buildNotificationMessage(msg);
} else if ("SubscriptionConfirmation".equals(type) ||
"UnsubscribeConfirmation".equals(type)){
用户指南 A 附录
message = buildSubscriptionMessage(msg);
}
return message;
}
private static String buildSubscriptionMessage(Map<String, String> msg) { String stringMessage = "message\n";
stringMessage += msg.get("message") + "\n";
stringMessage += "message_id\n";
stringMessage += msg.get("message_id") + "\n";
stringMessage += "subscribe_url\n";
stringMessage += msg.get("subscribe_url") + "\n";
stringMessage += "timestamp\n";
stringMessage += msg.get("timestamp") + "\n";
stringMessage += "topic_urn\n";
stringMessage += msg.get("topic_urn") + "\n";
stringMessage += "type\n";
stringMessage += msg.get("type") + "\n";
return stringMessage;
}
private String buildNotificationMessage(Map<String, Object> msg) {
String stringMessage = "message\n";
stringMessage += msg.get("message").toString() + "\n";
stringMessage += "message_id\n";
stringMessage += msg.get("message_id").toString() + "\n";
if (msg.get("subject") != null){
stringMessage += "subject\n";
stringMessage += msg.get("subject").toString() + "\n";
}
stringMessage += "timestamp\n";
stringMessage += msg.get("timestamp").toString() + "\n";
stringMessage += "topic_urn\n";
stringMessage += msg.get("topic_urn").toString() + "\n";
stringMessage += "type\n";
stringMessage += msg.get("type").toString() + "\n";
return stringMessage;
}
Node.js
const fs = require('fs');
const crypto = require('crypto');
const jsrsag = require('jsrsasign');
/** * 校验消息签名
* @param pemFile 签名文件存储路径(下载到本地证书文件路径) * @param signature 待验证的签名值
* @param message 待验证的消息内容
* @returns {boolean} true:签名值验证通过;false:签名值校验不通过 */function verifyMessage(pemFile, signature, message) {
const pubPem = fs.readFileSync(pemFile);
const verify = crypto.createVerify(signatureAlgorithm(pubPem));
verify.update(buildSignMessage(message));
const verifyResult = verify.verify(pubPem, signature, 'base64');
if (verifyResult) {
*/function signatureAlgorithm(pubPem) { const certObject = new jsrsag.X509();
certObject.readCertPEM(pubPem.toString());
let algorithm = certObject.getSignatureAlgorithmField();
if (algorithm.split('with').length > 1) {
algorithm = algorithm.split('with')[1] + '-' + algorithm.split('with')[0];
}
return algorithm;
}
function buildSignMessage(msg) { const type = msg.type;
let message = '';
if (type === 'Notification') {
message = buildNotificationMessage(msg);
} else if (type === 'SubscriptionConfirmation') { message = buildSubscriptionMessage(msg);
}
return message;
}
function buildNotificationMessage(msg) {
let signMessage = 'message\n' + msg.message + '\n';
signMessage += 'message_id\n' + msg.message_id + '\n';
if (msg.subject) {
signMessage += 'subject\n' + msg.subject + '\n';
}
signMessage += 'timestamp\n' + msg.timestamp + '\n';
signMessage += 'topic_urn\n' + msg.topic_urn + '\n';
signMessage += 'type\n' + msg.type + '\n';
return signMessage;
}
function buildSubscriptionMessage(msg) {
let signMessage = 'message\n' + msg.message + '\n';
signMessage += 'message_id\n' + msg.message_id + '\n';
signMessage += 'subscribe_url\n' + msg.subscribe_url + '\n';
signMessage += 'timestamp\n' + msg.timestamp + '\n';
signMessage += 'topic_urn\n' + msg.topic_urn + '\n';
signMessage += 'type\n' + msg.type + '\n';
return signMessage;
}
说明
该示例代码已在Nodejs v14.17.5版本上测试通过。
Go 语言
package demo import ( "bytes"
"crypto"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
)
type Message struct {
Signature string `json:"signature"`
Subject *string `json:"subject"`
TopicUrn string `json:"topic_urn"`
MessageId string `json:"message_id"`
用户指南 A 附录
SignatureVersion string `json:"signature_version"`
Type string `json:"type"`
Message string `json:"message"`
SubscribeUrl string `json:"subscribe_url"`
UnsubscribeUrl string `json:"unsubscribe_url"`
SigningCertUrl string `json:"signing_cert_url"`
Timestamp string `json:"timestamp"`
}
func VerifyMessage(pemFile string, message string) bool { msg := Message{}
err := json.Unmarshal([]byte(message), &msg) if err != nil {
fmt.Println("Convert json to struct failed") return false
}
pemContent, err := ioutil.ReadFile(pemFile) if err != nil {
fmt.Println("Read pem file failed") return false
}
certDerblock, _ := pem.Decode(pemContent) if certDerblock == nil {
fmt.Println("Decode pem file failed") return false
}
cert, err := x509.ParseCertificate(certDerblock.Bytes) if err != nil {
fmt.Println("Parse cert failed") return false
}
msgString := buildMessage(&msg) msgHash := crypto.SHA256.New() msgHash.Write([]byte(msgString)) msgHashSum := msgHash.Sum(nil)
decodeSign, _ := base64.StdEncoding.DecodeString(msg.Signature) publicKey := cert.PublicKey.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, msgHashSum, decodeSign) if err != nil {
func buildMessage(msg *Message) string { if msg.Type == "Notification" {
return buildNotificationMessage(msg)
} else if msg.Type == "SubscriptionConfirmation" || msg.Type == "UnsubscribeConfirmation" { return buildSubscriptionMessage(msg)
} return ""
}
func buildNotificationMessage(msg *Message) string { buf := bytes.Buffer{}
buf.WriteString("message\n" + msg.Message + "\n") buf.WriteString("message_id\n" + msg.MessageId + "\n") // msg中存在Subject字段不存在的场景,需要特殊处理 if msg.Subject != nil {
buf.WriteString("subject\n" + *msg.Subject + "\n") }
buf.WriteString("timestamp\n" + msg.Timestamp + "\n") buf.WriteString("topic_urn\n" + msg.TopicUrn + "\n")
buf.WriteString("type\n" + msg.Type + "\n") return buf.String()
}
func buildSubscriptionMessage(msg *Message) string { buf := bytes.Buffer{}
buf.WriteString("message\n" + msg.Message + "\n") buf.WriteString("message_id\n" + msg.MessageId + "\n") buf.WriteString("subscribe_url\n" + msg.SubscribeUrl + "\n") buf.WriteString("timestamp\n" + msg.Timestamp + "\n") buf.WriteString("topic_urn\n" + msg.TopicUrn + "\n") buf.WriteString("type\n" + msg.Type + "\n") return buf.String()
}
说明
该示例代码已在go 1.15版本上测试通过。
用户指南 A 附录
B 修订记录
版本日期 变更说明
2020-09-30 第十八次正式发布。
本次变更说明如下:
整体优化文档。
2020-01-20 第十七次正式发布。
变更说明:
“权限管理”章节删除IAM相关概念及操作内容,修 改为链接至IAM手册。
2019-07-05 第十六次正式发布。
变更说明:
新增“权限管理”章节。
2019-03-30 第十五次正式发布。
变更说明:
修改标签键的校验规则。
2018-11-30 第十四次正式发布。
变更说明:
● 新增“移动推送管理”章节
● 新增应用协议 2018-07-18 第十三次正式发布。
变更说明:
● 新增常见问题“短信发送失败,如何查看发送失败 原因?”
● 更新截图
版本日期 变更说明
2018-07-02 第十二次正式发布。
变更说明:
● 将FunctionGraph协议修改为 FunctionGraph(工 作流)协议
● 将FunctionStage协议修改为FunctionGraph(函 数)协议
● 新增设置主题标签
● 更新部分截图 2018-04-30 第十一次正式发布。
修改:
● 海外短信支持的国家和地区
● 短信签名、短信模板和发送短信章节截图刷新
● 修改“记录消息通知服务”章节 2018-01-30 第十次正式发布。
修改: 2017-10-30 第九次正式发布。
新增:
● FunctionGraph订阅协议。
● DMS订阅协议。
● 发送短信功能。
● 短信配置事件功能。
2017-8-18 第八次正式发布。
修改:
所有页面截图。
新增:
FunctionStage订阅协议。
2017-06-30 第七次正式发布。
新增:
创建短信签名。
用户指南 B 修订记录
版本日期 变更说明
2017-05-30 第六次正式发布。
新增:
● 删除创建主题页面的发布消息按钮。
● 输入主题别名不允许换行。
2017-3-13 第五次发布。
新增:
修改主题简介。
2016-11-30 第四次发布。
新增:
高级策略配置。
2016-10-29 第三次正式发布。
新增:
对接云审计服务记录资源操作。
2016-09-30 第二次正式发布。
新增:
修改主题策略。
2016-08-25 第一次正式发布。