为何.NET生成的ES256算法ECDSA签名JWT在PHP中验证失败?
为何.NET生成的ES256算法ECDSA签名JWT在PHP中验证失败?
我之前也踩过一模一样的坑,折腾了好一阵才理清问题所在,结合你的代码来看,大概率是这几个细节出了问题:
1. 公钥的导出与使用方式错误
这是最常见的原因,要么是你导出的公钥格式不符合OpenSSL要求,要么是PHP里调用Firebase JWT的方式不对。
先修正.NET的公钥导出逻辑
你的.NET代码是动态生成ECDSA密钥对的,但默认生成的密钥没有导出为标准PEM格式。你需要在生成privateKey后,添加这段代码导出符合X.509规范的PEM公钥:
// 在生成privateKey后添加,导出公钥到PEM文件 var pubKeyBytes = privateKey.ExportSubjectPublicKeyInfo(); var pubKeyPem = "-----BEGIN PUBLIC KEY-----\n" + Convert.ToBase64String(pubKeyBytes, Base64FormattingOptions.InsertLineBreaks) + "\n-----END PUBLIC KEY-----"; File.WriteAllText("ec-public-key.pem", pubKeyPem);
这样导出的PEM才是OpenSSL和Firebase JWT能正确识别的格式,别再用OpenSSL命令生成另一对密钥了(除非你在.NET里加载OpenSSL的私钥)。
修正PHP里的公钥使用方式
Firebase JWT的Key类直接接收PEM字符串比接收OpenSSL资源更稳定,建议修改PHP代码:
<?php require 'vendor/autoload.php'; use Firebase\JWT\JWT; use Firebase\JWT\Key; $jwt = '<JWT STRING HERE>'; // 从.NET复制过来的JWT // 直接读取PEM文件内容,不要用openssl_pkey_get_public $publicKey = file_get_contents('path/to/ec-public-key.pem'); try { $decoded = JWT::decode($jwt, new Key($publicKey, 'ES256')); print_r($decoded); } catch (Exception $e) { echo 'Validation error: ' . $e->getMessage(); }
2. 密钥对不匹配的隐形坑
如果你是想用OpenSSL生成的密钥对,那.NET代码里不能动态生成新密钥,必须加载OpenSSL的私钥文件:
// 替换原来的ECDsa.Create,加载OpenSSL生成的PEM私钥 var privateKey = ECDsa.CreateFromPemFile("ec-private-key.pem"); // 后续的securityKey、credentials、token生成逻辑不变
这样.NET和PHP用的就是完全同一对密钥,彻底排除密钥不匹配的可能。
3. 签名格式的兼容性问题(少见但需排查)
JWS规范要求ECDSA签名是64字节的紧凑r+s拼接格式(把r和s两个256位值拼接后base64url编码),虽然.NET Core 3.1+的JwtSecurityTokenHandler默认符合规范,但如果你的.NET版本过低,可能会输出ASN.1 DER格式的签名(长度约70+字节),这会导致OpenSSL验证失败。
排查方式:把JWT的第三段(签名部分)做base64url解码,看二进制长度是否为64字节。如果不是,建议升级.NET版本,或者强制.NET输出紧凑格式的签名。
内容来源于stack exchange
相关产品推荐
相关产品推荐

