单元测试SAML认证服务时遇InvalidSignatureException问题求助
解决SAML单元测试中签名无效的问题
问题背景
为便于单元测试,将SAML认证逻辑拆分到ExternalAuthenticationService层,控制器调用该服务完成认证:
控制器代码
[Route("samlConsume")] public async Task<IActionResult> AssertionConsumerAsync() { var samlLoginInfo = await externalAuthenticationService.AuthenticateAsync(HttpContext); // ... }
服务代码
public async Task<LoginInfo> AuthenticateAsync(HttpContext context) { var req = context.Request.ToGenericHttpRequest(); var binding = new Saml2PostBinding(); var saml2AuthnResponse = new Saml2AuthnResponse(config); binding.ReadSamlResponse(req, saml2AuthnResponse); if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) { throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}"); } binding.Unbind(req, saml2AuthnResponse); var sessionPrincipal = await saml2AuthnResponse.CreateSession( context, claimsTransform: (claimsPrincipal) => ClaimsTransform.Transform(claimsPrincipal) ); // 处理sessionPrincipal并返回LoginInfo }
单元测试时,模拟HttpContext、生成测试证书并伪造SAML响应,执行AuthenticateAsync时出现错误:
Message:
ITfoxtec.Identity.Saml2.Cryptography.InvalidSignatureException :
Signature is invalid.Stack Trace: Saml2Request.ValidateXmlSignature(SignatureValidation documentValidationResult)
解决方案
1. 确保签名证书与服务配置完全匹配
生成测试证书后,必须将证书的公钥添加到SAML服务的Saml2Configuration中,同时用对应的私钥签名伪造的SAML响应:
// 测试初始化时配置Saml2Configuration var testCert = new X509Certificate2("test-cert.pfx", "test-password"); var testConfig = new Saml2Configuration(); // 添加允许的签名证书(公钥会被提取用于验证) testConfig.AllowedSigningCertificates.Add(testCert);
2. 使用ITfoxtec库生成合规的签名SAML响应
不要手动构造SAML响应,直接用库的API生成带签名的测试响应,确保签名格式符合规范:
// 构造测试用SAML响应 var saml2AuthnResponse = new Saml2AuthnResponse(testConfig); saml2AuthnResponse.Status = Saml2StatusCodes.Success; // 设置断言主体等必要信息 saml2AuthnResponse.Assertion = new Saml2Assertion(testConfig); saml2AuthnResponse.Assertion.Subject = new Saml2Subject(new Saml2NameIdentifier("test-user-id")); saml2AuthnResponse.Assertion.Conditions = new Saml2Conditions { NotBefore = DateTime.UtcNow.AddMinutes(-5), NotOnOrAfter = DateTime.UtcNow.AddMinutes(5) }; // 用测试证书的私钥签名响应 saml2AuthnResponse.Sign(testCert); // 将响应绑定到POST请求,映射到模拟HttpContext var binding = new Saml2PostBinding(); var genericRequest = new GenericHttpRequest(); binding.Bind(saml2AuthnResponse, genericRequest); // 将genericRequest中的表单参数复制到模拟HttpContext的Request.Form中 var mockHttpContext = new DefaultHttpContext(); foreach (var param in genericRequest.Form) { mockHttpContext.Request.Form.TryAdd(param.Key, param.Value); }
3. 临时关闭签名验证(仅用于单元测试)
如果仅需测试业务逻辑,可临时关闭签名验证(生产环境绝对禁止):
testConfig.SignatureValidationMode = SignatureValidationMode.None;
4. 检查测试证书的有效性
确保测试证书满足以下要求:
- 包含可访问的私钥(
cert.HasPrivateKey返回true) - 密钥用途包含
DigitalSignature:
var cert = new X509Certificate2("test-cert.pfx", "test-password"); Debug.Assert(cert.HasPrivateKey, "测试证书必须包含私钥"); Debug.Assert(cert.KeyUsage.HasFlag(X509KeyUsageFlags.DigitalSignature), "证书密钥用途必须包含数字签名");
内容的提问来源于stack exchange,提问作者Scott Baker
相关产品推荐
相关产品推荐

