签署 SAML 响应会生成 CryptographicException:封装转换需要 XmlDocument 上下文
Signing a SAML response generates CryptographicException: An XmlDocument context is required for enveloped transforms
我正在尝试使用 System.Security.Xml 签署以下 SAML 断言。
<saml:Assertion xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" mlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfx96e500ef-d656-e97c-17ee-bbeff75c7235" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
以及以下代码:
[TestMethod]
public void SignAssertion()
{
var xmlDoc = new XmlDocument { PreserveWhitespace = true };
xmlDoc.Load("UnsignedAssertion.xml");
var certStore = new X509Store("MY", StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs;
try
{
certs = certStore.Certificates.Find(X509FindType.FindBySubjectName, "AValidCertificate", true);
}
finally
{
certStore.Close();
}
var cert = certs[0];
var signedXml = new SignedXml(xmlDoc);
var transforms = new TransformChain();
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
transforms.Add(new XmlDsigExcC14NTransform());
var reference = new Reference
{
Id = "#pfx96e500ef-d656-e97c-17ee-bbeff75c7235",
TransformChain = transforms
};
signedXml.AddReference(reference);
signedXml.SigningKey = cert.PrivateKey;
signedXml.ComputeSignature();
}
调用 ComputeSignature 失败并出现以下错误:
Message: Test method SamlPoc.UnitTest1.SignAssertion threw exception:
System.Security.Cryptography.CryptographicException: An XmlDocument
context is required for enveloped transforms.
以及以下堆栈跟踪:
at System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform.GetOutput()
at System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Object inputObject, Type inputType, XmlResolver resolver, String baseUri)
at System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Stream input, XmlResolver resolver, String baseUri)
at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList)
at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences()
at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
在我看来,我将一个有效的 XmlDocument 传递给了 SignedXml,它应该被视为一个有效的 XMLDocument 上下文。我错过了什么?
这个question shows where an enveloped signature lives in a signed XML document. There's a difference between an enveloping and enveloped signature.
如果您开始于:
<saml:Assertion>
...
</saml:Assertion>
并且您在根节点级别 (Assertion
) 签署该文档,您正在创建一个 enveloping 而不是 enveloped signature 因为签名包含了所有的签名信息。这就是为什么,当您添加:
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
你得到:
An XmlDocument context is required for enveloped transforms
因为 enveloped 签名需要一个封闭的 XmlDocument
作为信封。
最终你所做的是正确的,但因为它是一个测试,没有完整的 SAML
Response
,你会看到那个错误。在生产中,Assertion
不会单独发送,它会包含一个包含 XmlDocument
例如:
<saml:Response>
<saml:Assertion...
</saml:Assertion>
</saml:Response>
和 <saml:Response>
元素将为 enveloped 签名提供所需的 XmlDocument
上下文。
要使测试通过,请将 Assertion
包含在 Response
中并使用 XmlDsigEnvelopedSignatureTransform
.
对 Assertion
节点进行签名
我混淆了引用的 ID 属性 和 Uri 属性。 A Reference's Uri property is required,即使设置为空字符串。空字符串将设置对整个 xml 文档的引用。或者,我可以将 Uri 设置为前面带有 # 符号的断言的 Id。
public void SignAssertion()
{
var xmlDoc = new XmlDocument { PreserveWhitespace = false };
xmlDoc.Load("UnsignedAssertion.xml");
var signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = getCert().PrivateKey;
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
var canMethod = (XmlDsigExcC14NTransform)signedXml.SignedInfo.CanonicalizationMethodObject;
canMethod.InclusiveNamespacesPrefixList = "Sign";
var transforms = new TransformChain();
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
var reference = new Reference
{
Id = "#pfx96e500ef-d656-e97c-17ee-bbeff75c7235",
TransformChain = transforms,
Uri = "" // This is the solution...
};
signedXml.AddReference(reference);
signedXml.ComputeSignature();
}
我正在尝试使用 System.Security.Xml 签署以下 SAML 断言。
<saml:Assertion xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" mlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfx96e500ef-d656-e97c-17ee-bbeff75c7235" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
<saml:AudienceRestriction>
<saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">test@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
以及以下代码:
[TestMethod]
public void SignAssertion()
{
var xmlDoc = new XmlDocument { PreserveWhitespace = true };
xmlDoc.Load("UnsignedAssertion.xml");
var certStore = new X509Store("MY", StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs;
try
{
certs = certStore.Certificates.Find(X509FindType.FindBySubjectName, "AValidCertificate", true);
}
finally
{
certStore.Close();
}
var cert = certs[0];
var signedXml = new SignedXml(xmlDoc);
var transforms = new TransformChain();
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
transforms.Add(new XmlDsigExcC14NTransform());
var reference = new Reference
{
Id = "#pfx96e500ef-d656-e97c-17ee-bbeff75c7235",
TransformChain = transforms
};
signedXml.AddReference(reference);
signedXml.SigningKey = cert.PrivateKey;
signedXml.ComputeSignature();
}
调用 ComputeSignature 失败并出现以下错误:
Message: Test method SamlPoc.UnitTest1.SignAssertion threw exception: System.Security.Cryptography.CryptographicException: An XmlDocument context is required for enveloped transforms.
以及以下堆栈跟踪:
at System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform.GetOutput()
at System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Object inputObject, Type inputType, XmlResolver resolver, String baseUri)
at System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Stream input, XmlResolver resolver, String baseUri)
at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList)
at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences()
at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
在我看来,我将一个有效的 XmlDocument 传递给了 SignedXml,它应该被视为一个有效的 XMLDocument 上下文。我错过了什么?
这个question shows where an enveloped signature lives in a signed XML document. There's a difference between an enveloping and enveloped signature.
如果您开始于:
<saml:Assertion>
...
</saml:Assertion>
并且您在根节点级别 (Assertion
) 签署该文档,您正在创建一个 enveloping 而不是 enveloped signature 因为签名包含了所有的签名信息。这就是为什么,当您添加:
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
你得到:
An XmlDocument context is required for enveloped transforms
因为 enveloped 签名需要一个封闭的 XmlDocument
作为信封。
最终你所做的是正确的,但因为它是一个测试,没有完整的 SAML
Response
,你会看到那个错误。在生产中,Assertion
不会单独发送,它会包含一个包含 XmlDocument
例如:
<saml:Response>
<saml:Assertion...
</saml:Assertion>
</saml:Response>
和 <saml:Response>
元素将为 enveloped 签名提供所需的 XmlDocument
上下文。
要使测试通过,请将 Assertion
包含在 Response
中并使用 XmlDsigEnvelopedSignatureTransform
.
Assertion
节点进行签名
我混淆了引用的 ID 属性 和 Uri 属性。 A Reference's Uri property is required,即使设置为空字符串。空字符串将设置对整个 xml 文档的引用。或者,我可以将 Uri 设置为前面带有 # 符号的断言的 Id。
public void SignAssertion()
{
var xmlDoc = new XmlDocument { PreserveWhitespace = false };
xmlDoc.Load("UnsignedAssertion.xml");
var signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = getCert().PrivateKey;
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
var canMethod = (XmlDsigExcC14NTransform)signedXml.SignedInfo.CanonicalizationMethodObject;
canMethod.InclusiveNamespacesPrefixList = "Sign";
var transforms = new TransformChain();
transforms.Add(new XmlDsigEnvelopedSignatureTransform());
var reference = new Reference
{
Id = "#pfx96e500ef-d656-e97c-17ee-bbeff75c7235",
TransformChain = transforms,
Uri = "" // This is the solution...
};
signedXml.AddReference(reference);
signedXml.ComputeSignature();
}