示例代码
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); } 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; }
Node.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
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') { 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"` 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) } 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版本上测试通过。