开发者社区 问答 正文

消息服务的Endpoint签名认证是什么?


用户可以通过推送请求Header中的 x-mns-signing-cert-url 获取签名证书,并根据相应的方法来验证该请求是否由MNS系统发出,防止恶意请求对用户造成负面影响。
在 MNS 推送请求的 Header 中,Authorization 字段的值是MNS根据待签名字符串,用 SHA1-RSA 签名算法生成签名。Endpoint 可以使用公钥对签名进行验证,具体的验证方法如下:

[backcolor=transparent]第一步:获取X509证书

在 MNS 发送给 Endpoint 的 http 请求 Header 中,x-mns-signing-cert-url 指定了签名证书的地址( Base64 编码),用户需要通过 Base64 解码,获取该签名文件 URL 地址,再从中提取出签名的公钥。

[backcolor=transparent]第二步:计算待签名字符串
[backcolor=transparent]

[backcolor=transparent]

[backcolor=transparent]第三步:Authorization 解密

对Authorization 签名字段进行 Base64 Decode 解码后,使用从第一步从证书中提取的公钥对其进行解密;

[backcolor=transparent]第四步:认证

比较第二步生成的待签名字符串与第三步解密的结果是否一致。如果一致,则表明请求来自MNS,访问合法。

[backcolor=transparent]注意事项

CanonicalizedMNSHeaders(即x-mns-开头的head)在签名验证前需要符合以下规范:
  • head 的名字需要变成小写
  • head 自小到大排序
  • 分割 head name 和 value 的冒号前后不能有空格
  • 每个Head之后都有一个\n,如果没有以x-mns-开头的head,则在签名时CanonicalizedMNSHeaders就设置为空

[backcolor=transparent]其他
  1. 用来签名的字符串为 UTF-8 格式;
  2. 签名的方法用 RFC 3447 (http://tools.ietf.org/html/rfc3447)中定义的 sha1WithRSAEncryption 方法;
  3. Base64 是指使用 base64 算法转码文本。

[backcolor=transparent]
[backcolor=transparent]Java示例代码
[backcolor=transparent]

[backcolor=transparent]

[backcolor=transparent]

展开
收起
轩墨 2017-10-24 15:19:31 1922 分享 版权
阿里云 AI 助理回答

基于您的描述,这里提供一个简化的流程和概念理解,以及如何在Java中实现MNS推送请求签名验证的概要指导。请注意,实际应用中需要根据具体情况进行调整,并确保使用的库和方法与当前的安全标准和阿里云MNS服务的最新API兼容。

简化流程回顾:

  1. 获取X509证书:从x-mns-signing-cert-url Header获取Base64编码的证书URL地址,解码后访问该URL获取X509证书,进而提取公钥。
  2. 计算待签名字符串:根据MNS规范构造待签名字符串,包括HTTP Method、CanonicalizedMNSHeaders、CanonicalizedResource等部分。
  3. Authorization解密:将Authorization字段的值进行Base64解码,然后使用公钥对解码后的数据进行RSA-SHA1解密。
  4. 认证:比较第二步生成的待签名字符串与第三步解密的结果是否一致,一致则验证通过。

Java示例代码概览

以下是一个简化的Java示例代码框架,用于说明如何执行上述验证过程的关键步骤。请确保你的项目已经添加了必要的依赖,如Apache HttpClient用于网络请求,Bouncy Castle或其他支持RSA和Base64操作的库。

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.util.io.pem.PemObject;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class MNSRequestValidator {

    public static void main(String[] args) throws Exception {
        // 假设已从Header中获取到以下信息
        String signingCertUrlBase64 = "your-base64-encoded-certificate-url";
        String authorization = "your-authorization-header-value";

        // 第一步:获取X509证书
        String certUrl = new String(Base64.getDecoder().decode(signingCertUrlBase64), StandardCharsets.UTF_8);
        PublicKey publicKey = fetchPublicKeyFromUrl(certUrl);

        // 第二步:计算待签名字符串(此处省略具体实现,需根据MNS规范构造)
        String stringToSign = "your-computed-string-to-sign"; // 实际应用中需正确构造

        // 第三步:Authorization解密
        byte[] signatureBytes = Base64.getDecoder().decode(authorization);
        boolean isValid = verifySignature(publicKey, stringToSign, signatureBytes);

        if (isValid) {
            System.out.println("The request is authenticated by MNS.");
        } else {
            System.out.println("The request failed the authentication.");
        }
    }

    private static PublicKey fetchPublicKeyFromUrl(String certUrl) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(certUrl);
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            if (response.getStatusLine().getStatusCode() == 200) {
                InputStream content = response.getEntity().getContent();
                PEMParser pemParser = new PEMParser(new InputStreamReader(content));
                PemObject pemObject = pemParser.readPemObject();
                pemParser.close();
                X509CertificateHolder certificateHolder = new X509CertificateHolder(pemObject.getContent());
                JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
                return converter.getCertificate(certificateHolder).getPublicKey();
            } else {
                throw new IOException("Failed to fetch certificate from URL: " + certUrl);
            }
        }
    }

    private static boolean verifySignature(PublicKey publicKey, String data, byte[] signature) throws Exception {
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(publicKey);
        sig.update(data.getBytes(StandardCharsets.UTF_8));
        return sig.verify(signature);
    }
}

这段代码展示了如何下载并解析证书中的公钥,构造待验证的字符串,以及如何验证签名。请根据实际情况调整和完善代码细节,特别是处理异常、资源管理以及按照MNS的具体要求构造待签名字符串的部分。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答