手动验证SAML响应中的RSA签名遇断言失败问题求助
手动验证SAML RSA签名失败排查求助
我发现.NET的<SignedXml>组件存在问题——同一个SAML响应在Java环境中可以正常验证,但在.NET里无法通过。于是我尝试手动编写RSA签名验证代码来校验SAML响应的签名,同时用外部工具计算了<SignedInfo>元素的预期摘要用于对比。已经确认代码中的大小端转换和签名解密逻辑符合预期,但最终Debug.Assert断言还是失败了,求帮忙排查问题。
我的代码如下:
var sigString = "Xxfh0hq34DGIQibeBrNcYuU/XD0aX...IviQm/5jZK6pD9wReldSPoo="; var sigBytes = Convert.FromBase64String(sigString).Reverse().Append<byte>(0x0).ToArray(); var s = new BigInteger(sigBytes); var modulusString = "lg4dkGLwfAApoNtWoX...oxvjjaGfH9PzoE="; var modulusBytes = Convert.FromBase64String(modulusString).Reverse().Append<byte>(0x0).ToArray(); var n = new BigInteger(modulusBytes); var exponentString = "AQAB"; var exponentBytes = Convert.FromBase64String(exponentString).Reverse().Append<byte>(0x0).ToArray(); var e = new BigInteger(exponentBytes); var r = BigInteger.ModPow(s, e, n); var expectedB64 = "XeHIN99Mkt5A/HswHotEndGJ6ahw/0l8jbjy92Fjsaw="; var expected = new BigInteger(Convert.FromBase64String(expectedB64).Reverse().Append<byte>(0x0).ToArray()); var expectedHex = expected.ToString("X"); var resultHex = r.ToString("X"); //Removing pkcs1.5 padding var actualHex = resultString.Substring(resultString.Length - expectedString.Length); Debug.Assert(resultHex == actualHex); //Fail
关键问题点排查:
- 变量名拼写错误:代码中计算
actualHex时使用了resultString和expectedString,但前面定义的变量是resultHex和expectedHex,这会直接导致取值错误,是断言失败的直接原因。 - PKCS#1.5 padding解析逻辑错误:手动截取末尾固定长度的方式不严谨,PKCS#1.5签名的padding有固定格式:开头是
0x00 0x01,接着是连续的0xFF,然后是0x00分隔符,最后才是摘要内容。正确的做法是从解密后的字节数组中找到0x00分隔符,取后面的内容,而不是按长度截取。 - BigInteger字节序处理错误:.NET的
BigInteger(byte[] value)构造函数默认按大端序解析,且会把最高位为1的字节数组解析为负数。你这里对所有字节数组做了Reverse()反转,还额外添加了0x0字节,会导致模数、指数、签名值的BigInteger实例数值完全错误。正确的做法是使用BigInteger(byte[] value, bool isUnsigned)重载,直接传入原始字节数组(RSA的模数、指数、签名都是大端存储的)。 - 额外添加0x0字节的问题:只有当原始字节数组最高位是
0x80及以上时,才需要添加0x0字节避免BigInteger被解析为负数,但盲目添加会改变数值,导致计算错误。
修正后的示例代码:
var sigString = "Xxfh0hq34DGIQibeBrNcYuU/XD0aX...IviQm/5jZK6pD9wReldSPoo="; var sigBytes = Convert.FromBase64String(sigString); // 使用无符号解析,直接传入大端字节数组 var s = new BigInteger(sigBytes, isUnsigned: true); var modulusString = "lg4dkGLwfAApoNtWoX...oxvjjaGfH9PzoE="; var modulusBytes = Convert.FromBase64String(modulusString); var n = new BigInteger(modulusBytes, isUnsigned: true); var exponentString = "AQAB"; var exponentBytes = Convert.FromBase64String(exponentString); var e = new BigInteger(exponentBytes, isUnsigned: true); // 计算模幂,转换为大端无符号字节数组 var rBytes = s.ModPow(e, n).ToByteArray(isUnsigned: true, isBigEndian: true); // 正确解析PKCS#1.5 padding int separatorIndex = Array.IndexOf(rBytes, (byte)0x00); if (separatorIndex == -1 || separatorIndex < 2 || rBytes[0] != 0x00 || rBytes[1] != 0x01) { throw new InvalidOperationException("无效的PKCS#1.5签名填充"); } // 截取分隔符后的摘要内容 var actualDigestBytes = rBytes[(separatorIndex + 1)..]; var expectedB64 = "XeHIN99Mkt5A/HswHotEndGJ6ahw/0l8jbjy92Fjsaw="; var expectedDigestBytes = Convert.FromBase64String(expectedB64); // 直接对比字节数组,避免Hex字符串转换的潜在问题 Debug.Assert(actualDigestBytes.SequenceEqual(expectedDigestBytes));
内容的提问来源于stack exchange,提问作者npen
相关产品推荐
相关产品推荐

