PHP+CodeIgniter4验证Bluem支付Webhook签名(.crt文件)
在CodeIgniter 4中验证Bluem支付网关Webhook签名
Bluem的Webhook签名验证核心是用他们提供的公钥证书(.crt文件),验证其签名是否与接收到的Webhook请求体匹配,确保请求来自Bluem且未被篡改,以下是具体实现步骤和代码:
核心逻辑
Bluem使用自身私钥对Webhook的原始请求体进行签名,我们通过其提供的.crt公钥证书,验证该签名是否对应请求体,以此确认请求合法性。
实现步骤与代码
1. 获取原始请求数据与签名
在CodeIgniter 4中必须获取未解析的原始请求体(不能用getPost()等解析方法,否则会破坏签名验证的原始数据),同时提取请求头中的Signature字段:
2. 加载并解析.crt公钥证书
将Bluem提供的.crt文件放在非公开目录(如app/Config/),避免被外部直接访问,再通过OpenSSL函数从证书中提取公钥。
3. 执行签名验证
使用openssl_verify()函数完成验证,注意签名通常是Base64编码的,需要先解码。
完整控制器示例
<?php namespace App\Controllers; use CodeIgniter\Controller; class BluemWebhook extends Controller { public function handle() { // 获取原始请求体 $rawBody = $this->request->getRawBody(); // 获取Signature请求头 $signature = $this->request->getHeaderLine('Signature'); if (empty($signature)) { return $this->response->setStatusCode(400)->setJSON(['status' => 'error', 'message' => '缺少签名']); } // 加载证书文件(替换为你的实际路径) $certPath = APPPATH . 'Config/bluem_public.crt'; if (!file_exists($certPath)) { return $this->response->setStatusCode(500)->setJSON(['status' => 'error', 'message' => '证书文件不存在']); } // 解析证书并提取公钥 $cert = openssl_x509_read(file_get_contents($certPath)); if (!$cert) { return $this->response->setStatusCode(500)->setJSON(['status' => 'error', 'message' => '无效的证书文件']); } openssl_x509_export($cert, $certString); $pubKey = openssl_pkey_get_public($certString); if (!$pubKey) { return $this->response->setStatusCode(500)->setJSON(['status' => 'error', 'message' => '提取公钥失败']); } // 解码签名(Bluem的签名通常是Base64编码) $decodedSignature = base64_decode($signature); if ($decodedSignature === false) { return $this->response->setStatusCode(400)->setJSON(['status' => 'error', 'message' => '签名格式无效']); } // 执行签名验证(确认Bluem的签名算法,默认用SHA256) $verifyResult = openssl_verify($rawBody, $decodedSignature, $pubKey, OPENSSL_ALGO_SHA256); // 释放OpenSSL资源 openssl_x509_free($cert); openssl_pkey_free($pubKey); // 处理验证结果 if ($verifyResult === 1) { // 签名验证通过,执行你的业务逻辑 // 比如更新订单状态、记录日志等 return $this->response->setStatusCode(200)->setJSON(['status' => 'success']); } elseif ($verifyResult === 0) { return $this->response->setStatusCode(403)->setJSON(['status' => 'error', 'message' => '签名无效']); } else { return $this->response->setStatusCode(500)->setJSON(['status' => 'error', 'message' => '签名验证过程出错']); } } }
关键注意事项
- 启用OpenSSL扩展:确保PHP环境已启用OpenSSL扩展,否则加密验证函数无法运行。
- 确认签名算法:如果Bluem使用的不是SHA256,需替换
openssl_verify中的算法常量(如OPENSSL_ALGO_SHA512),具体参考Bluem官方文档。 - 原始请求体不可修改:必须使用接收到的原始请求体,不能添加、删除或修改任何字符(包括空格、换行),否则验证会失败。
- 证书安全存储:.crt文件必须放在非Web可访问目录,防止被恶意下载。
内容的提问来源于stack exchange,提问作者Dhruv Pandya
相关产品推荐
相关产品推荐

