You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sendgrid Webhook签名验证始终失败,请求技术协助

SendGrid Webhook签名验证始终返回false的排查与解决

可能的问题点及对应解决方案

1. 请求体不一致(最常见原因)

Spring的@RequestBody String rawData可能会自动对请求体做转义、编码转换或格式调整,导致和SendGrid发送的原始payload不一致。

解决方法:直接读取原始请求流,避免Spring的自动处理:

@PostMapping("/sendgrid-callback")
public boolean acceptSendgridCallback(HttpServletRequest request) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, SignatureException, InvalidKeyException {
    String timestamp = request.getHeader("X-Twilio-Email-Event-Webhook-Timestamp");
    String signature = request.getHeader("X-Twilio-Email-Event-Webhook-Signature");
    
    // 读取原始UTF-8编码的请求体
    String rawData = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))
            .lines()
            .collect(Collectors.joining("\n"));
    
    System.out.println("Raw payload: " + rawData);
    
    ECPublicKey ecdsaKey = eventWebhook.ConvertPublicKeyToECDSA("your-sendgrid-public-key");
    boolean b = eventWebhook.VerifySignature(ecdsaKey, rawData.getBytes(StandardCharsets.UTF_8), signature, timestamp);
    return b;
}

同时对比webhook.site收到的原始payload和控制器打印的rawData,确保完全一致(包括空格、换行、转义符、编码)。

2. 公钥转换逻辑错误

SendGrid提供的公钥是PEM格式,如果你的ConvertPublicKeyToECDSA方法没有正确处理格式,会导致公钥无效。

正确的公钥转换代码:

public ECPublicKey ConvertPublicKeyToECDSA(String publicKeyPem) throws NoSuchAlgorithmException, InvalidKeySpecException {
    // 清理PEM格式的页眉、页脚和换行符
    String cleanedPem = publicKeyPem.replaceAll("-----BEGIN PUBLIC KEY-----", "")
                                    .replaceAll("-----END PUBLIC KEY-----", "")
                                    .replaceAll("\\s+", "");
    
    byte[] keyBytes = Base64.getDecoder().decode(cleanedPem);
    KeyFactory keyFactory = KeyFactory.getInstance("EC");
    return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));
}

3. 测试流程中payload被篡改

从webhook.site导出Curl再导入Postman的过程中,可能出现payload格式被修改(比如自动URL编码、转义字符变化)。

解决方法:

  • 直接复制webhook.site中的原始payload文本,手动粘贴到Postman的请求体中
  • 确保请求头的X-Twilio-Email-Event-Webhook-Timestamp和X-Twilio-Email-Event-Webhook-Signature完全和webhook.site收到的一致,不要手动修改

4. 编码不一致导致字节数组错误

SendGrid生成签名时使用UTF-8编码,你的代码中如果默认用了系统编码,会导致字节数组不匹配。

修改签名验证代码,明确指定UTF-8:

public boolean VerifySignature(ECPublicKey publicKey, byte[] payload, String signature, String timestamp)
        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, IOException {

    final ByteArrayOutputStream payloadWithTimestamp = new ByteArrayOutputStream();
    // 明确使用UTF-8编码
    payloadWithTimestamp.write(timestamp.getBytes(StandardCharsets.UTF_8));
    payloadWithTimestamp.write(payload);

    final Signature signatureObject = Signature.getInstance("SHA256withECDSA", "BC");
    signatureObject.initVerify(publicKey);
    signatureObject.update(payloadWithTimestamp.toByteArray());

    final byte[] signatureInBytes = Base64.getDecoder().decode(signature);
    return signatureObject.verify(signatureInBytes);
}

5. BouncyCastle提供者未正确配置

如果项目中没有引入BouncyCastle依赖或未注册提供者,会导致签名算法初始化失败。

Maven依赖添加:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

代码中注册提供者:

// 在应用启动时添加
Security.addProvider(new BouncyCastleProvider());

内容的提问来源于stack exchange,提问作者vigamage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 01:01:11