我正在使用 c# 和 asp.net 向 idp 发送一个签名的 authnRequest。我的代码对 authnRequest 进行签名,但签名验证在 idp 处失败。
详
我尝试了很多解决方案,但徒劳无功。 这是我按照 https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf 设定的指导方针所做的:
-
步骤
- 压缩身份验证请求,然后对其进行 base64 编码,最后对其进行 URL 编码。我们称之为AR
- 对中继状态进行 URL 编码。我们称之为RS
- 对签名算法字符串进行 URL 编码。我们称之为SA
- 所以要签名的字符串现在变成SAMLRequest=AR&RelayState=RS&SigAlg=SA
- 现在,我使用我们的私钥(服务提供商私钥(对我们在步骤#4中获得的字符串进行签名。 6.我得到的结果签名,我基于 64 对其进行编码,然后对其进行 URL 编码。因此,我得到了一个base64和url编码的签名。我们称之为SG
- 现在,我将我们在步骤 #6 中获得的签名附加到步骤 #4 中的查询字符串中。所以最终的查询字符串变成了SAMLRequest=AR&RelayState=RS&SigAlg=SA&Signature=SG
所有这些都工作正常,但签名验证失败!
这是我的代码,类似于此处的代码 https://github.com/Sustainsys/Saml2/blob/v0.21.2/Kentor.AuthServices/WebSSO/Saml2RedirectBinding.cs#L53-L68
protected void btnSendAuthRequest_Click(object sender, EventArgs e)
{
string authRequest = txtInput.Text;
//authRequest = authRequest.TrimEnd('r', 'n');
authRequest = DeflateBase64UrlEncode(authRequest);
string spPrivateKey= txtKey.Text;
string relayState = HttpUtility.UrlEncode("https://example.com/pages/home.aspx");
string qs = "SAMLRequest=" + authRequest + "&RelayState=" + relayState;
qs = AddSignature(qs, spPrivateKey);
txtOutput.Text = qs;
}
public string AddSignature(string queryString, string PrivateKeyNoHeaders)
{
RSACryptoServiceProvider tmpRsa = RSAKeyTests.RSAKeyUtils.DecodePrivateKeyInfo(Convert.FromBase64String(PrivateKeyNoHeaders));
string signingAlgorithmUrl = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
queryString += "&SigAlg=" + HttpUtility.UrlEncode(signingAlgorithmUrl);
var signatureDescription = (SignatureDescription)CryptoConfig.CreateFromName(signingAlgorithmUrl);
HashAlgorithm hashAlg = signatureDescription.CreateDigest();
hashAlg.ComputeHash(Encoding.UTF8.GetBytes(queryString));
AsymmetricSignatureFormatter asymmetricSignatureFormatter =
signatureDescription.CreateFormatter(
((RSACryptoServiceProvider)tmpRsa));
//.GetSha256EnabledRSACryptoServiceProvider());
// Is the signature failing because of above ?
byte[] signatureValue = asymmetricSignatureFormatter.CreateSignature(hashAlg);
queryString += "&Signature=" + HttpUtility.UrlEncode(Convert.ToBase64String(signatureValue));
return queryString;
}
private string DeflateBase64UrlEncode(string input)
{
var inputs = string.Format(input, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(inputs);
using (var output = new MemoryStream())
{
using (var zip = new DeflateStream(output, CompressionMode.Compress))
{
zip.Write(bytes, 0, bytes.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
return HttpUtility.UrlEncode(base64);
}
}
CryptoConfig.createFromName(...)
不知道http://www.w3.org/2000/09/xmldsig#rsa-sha1
是摘要+签名算法。如果CryptoConfig.createFromName()
未返回 null,则为http://www.w3.org/2000/09/xmldsig#rsa-sha1
注册的任何算法都可能不是 RSA-SHA1。以下是使用 RSA 和 SHA1SignatureDescription
的显式实现:
public class RSASHA1SignatureDescription : SignatureDescription {
public RSASHA1SignatureDescription() {
KeyAlgorithm = "System.Security.Cryptography.RSA";
DigestAlgorithm = "System.Security.Cryptography.SHA1Cng";
FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
_hashAlgorithm = "SHA1";
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) {
AsymmetricSignatureDeformatter item = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
item.setKey(key);
item.SetHashAlgorithm(_hashAlgorithm);
return item;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key) {
AsymmetricSignatureFormatter item = (AsymmetricSignatureFormatter) CryptoConfig.CreateFromName(FormatterAlgorithm);
item.setKey(key);
item.SetHashAlgorithm(_hashAlgorithm);
return item;
}
private string _hashAlgorithm;
}
另一种可能性是,您验证签名不需要 rsa-sha1(许多身份提供程序通过配置禁止 rsa-sha1(或验证不正确。尝试使用真正的 IdP 注册,例如 Okta 或 Salesforce,并在那里进行验证。