Laravel/PHP转换SendGrid公钥时ECDSA验证库报错问题
解决SendGrid Webhook签名验证的gmp_init错误
问题核心
你遇到的gmp_init(): Argument #1 ($num) is not an integer string错误,本质是SendGrid提供的PEM格式公钥与ecdsa-php库PublicKey::fromString要求的格式不匹配。该方法无法直接解析PKCS#8格式的PEM公钥,需要先转换为纯Base64编码的原始公钥,或直接提取EC坐标创建公钥。
解决方案1:清理公钥格式后调用转换函数
SendGrid提供的公钥是带PEM头尾标记的PKCS#8格式,先清理掉标记和换行符,再传入转换函数:
// 原始SendGrid PEM格式公钥 $sendGridPemKey = '-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----'; // 清理公钥:移除PEM头尾和所有空白字符 $cleanPublicKey = preg_replace('/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----|\s+/', '', $sendGridPemKey); // 调用SendGrid库的转换方法 $eventWebhook = new EventWebhook(); try { $ecPublicKey = $eventWebhook->convertPublicKeyToECDSA($cleanPublicKey); } catch (\Exception $e) { var_dump($e->getMessage()); }
解决方案2:直接提取EC坐标创建公钥(更可靠)
如果方案1仍报错,可通过OpenSSL解析PEM公钥,提取EC曲线的X/Y坐标,直接用ecdsa-php的底层API创建公钥:
use StarkBank\Ecdsa\Curve; use StarkBank\Ecdsa\Point; use StarkBank\Ecdsa\PublicKey; use StarkBank\Ecdsa\BigInteger; $sendGridPemKey = '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----'; // 用OpenSSL解析公钥 $publicKeyResource = openssl_pkey_get_public($sendGridPemKey); $keyDetails = openssl_pkey_get_details($publicKeyResource); // 提取EC坐标(原始二进制转十六进制字符串) $xHex = bin2hex($keyDetails['ec']['x']); $yHex = bin2hex($keyDetails['ec']['y']); // 创建ECDSA公钥(SendGrid使用secp256r1曲线,即NIST P-256) $curve = new Curve('secp256r1'); $ecPublicKey = new PublicKey( new Point( $curve, new BigInteger($xHex, 16), new BigInteger($yHex, 16) ) ); // 验证签名逻辑 $timestamp = $_SERVER['HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_TIMESTAMP']; $signature = $_SERVER['HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_SIGNATURE']; $requestBody = file_get_contents('php://input'); $verifyResult = $ecPublicKey->verify( hash('sha256', $timestamp . $requestBody, true), base64_decode($signature) ); var_dump($verifyResult); // 验证成功返回true
额外检查项
- 确保PHP已安装并启用
gmp扩展(可通过phpinfo()查看) - 确认SendGrid公钥对应的曲线是
secp256r1(NIST P-256),这是SendGrid Webhook签名的默认曲线
内容的提问来源于stack exchange,提问作者ZcomZ
相关产品推荐
相关产品推荐

