用户可以通过推送请求Header中的 x-mns-signing-cert-url 获取签名证书,并根据相应的方法来验证该请求是否由MNS系统发出,防止恶意请求对用户造成负面影响。
在 MNS 推送请求的 Header 中,Authorization 字段的值是MNS根据待签名字符串,用 SHA1-RSA 签名算法生成签名。Endpoint 可以使用公钥对签名进行验证,具体的验证方法如下:
在 MNS 发送给 Endpoint 的 http 请求 Header 中,x-mns-signing-cert-url 指定了签名证书的地址( Base64 编码),用户需要通过 Base64 解码,获取该签名文件 URL 地址,再从中提取出签名的公钥。
对Authorization 签名字段进行 Base64 Decode 解码后,使用从第一步从证书中提取的公钥对其进行解密;
比较第二步生成的待签名字符串与第三步解密的结果是否一致。如果一致,则表明请求来自MNS,访问合法。
CanonicalizedMNSHeaders(即x-mns-开头的head)在签名验证前需要符合以下规范:
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
基于您的描述,这里提供一个简化的流程和概念理解,以及如何在Java中实现MNS推送请求签名验证的概要指导。请注意,实际应用中需要根据具体情况进行调整,并确保使用的库和方法与当前的安全标准和阿里云MNS服务的最新API兼容。
x-mns-signing-cert-url
Header获取Base64编码的证书URL地址,解码后访问该URL获取X509证书,进而提取公钥。以下是一个简化的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的具体要求构造待签名字符串的部分。