更新时间:2026-08-27 GMT+08:00
分享

示例代码

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);
		if (sig.verify(sigByte)) {
			System.out.println("Verify success");
		} else {
			System.out.println("Verify failed");
		}
	} catch (Exception e) {
		throw new SecurityException("Verify method failed.", e);
	} finally {
		if (in != null) {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

使用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)) {
		message = buildSubscriptionMessage(msg);
	} else if ("HttpSubscriptionUserConfirm".equals(type)) {
		message = buildSubscriptionUserConfirmMessage(msg);
	} else if ("HttpSubscriptionUserUnsubscribe".equals(type)) {
		message = buildSubscriptionUserUnsubscribeMessage(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 static String buildNotificationMessage(Map<String, String> 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;
}

private static String buildSubscriptionUserConfirmMessage(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 += "type\n";
	stringMessage += msg.get("type") + "\n";
	return stringMessage;
}

private static String buildSubscriptionUserUnsubscribeMessage(Map<String, String> msg) {
	String stringMessage = "message\n";
	stringMessage += msg.get("message") + "\n";
	stringMessage += "message_id\n";
	stringMessage += msg.get("message_id") + "\n";
	stringMessage += "timestamp\n";
	stringMessage += msg.get("timestamp") + "\n";
	stringMessage += "type\n";
	stringMessage += msg.get("type") + "\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) {
        console.log("verify success");
        return true;
    } else {
        console.log('verify failed, result: ' + verifyResult);
        return false;
    }
}

/**
 * 从证书中获取签名算法
 */
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' || type === 'UnsubscribeConfirmation') {
        message = buildSubscriptionMessage(msg);
    } else if (type === 'HttpSubscriptionUserConfirm') {
        message = buildSubscriptionUserConfirmMessage(msg);
    } else if (type === 'HttpSubscriptionUserUnsubscribe') {
        message = buildSubscriptionUserUnsubscribeMessage(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;
}

function buildSubscriptionUserConfirmMessage(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 += 'type\n' + msg.type + '\n';
    return signMessage;
}

function buildSubscriptionUserUnsubscribeMessage(msg) {
    let signMessage = 'message\n' + msg.message + '\n';
    signMessage += 'message_id\n' + msg.message_id + '\n';
    signMessage += 'timestamp\n' + msg.timestamp + '\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"`
	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 {
		fmt.Println("Verify failed")
		return false
	} else {
		fmt.Println("Verify success")
		return true
	}
}

func buildMessage(msg *Message) string {
	if msg.Type == "Notification" {
		return buildNotificationMessage(msg)
	} else if msg.Type == "SubscriptionConfirmation" || msg.Type == "UnsubscribeConfirmation" {
		return buildSubscriptionMessage(msg)
	} else if msg.Type == "HttpSubscriptionUserConfirm" {
		return buildSubscriptionUserConfirmMessage(msg)
	} else if msg.Type == "HttpSubscriptionUserUnsubscribe" {
		return buildSubscriptionUserUnsubscribeMessage(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()
}

func buildSubscriptionUserConfirmMessage(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("type\n" + msg.Type + "\n")
	return buf.String()
}

func buildSubscriptionUserUnsubscribeMessage(msg *Message) string {
	buf := bytes.Buffer{}
	buf.WriteString("message\n" + msg.Message + "\n")
	buf.WriteString("message_id\n" + msg.MessageId + "\n")
	buf.WriteString("timestamp\n" + msg.Timestamp + "\n")
	buf.WriteString("type\n" + msg.Type + "\n")
	return buf.String()
}

该示例代码已在go 1.15版本上测试通过。

相关文档