如何解决如何配置 SignedXML 以对带有前缀和其他仪式的 XML 文档进行签名?
我需要使用 .NET Core 和 SignedXml
使用证书对 XML 文档进行签名,但我不知道如何在签名标签中配置对方期望的一些元素。我检查了很多文章,one of this,authored by Rick Stahl 非常相似,但我仍然验证了错误。不幸的是,我无法发送任何好的日志消息,我只有一个笼统的答案,即签名无效。
签名的预期内容如下所示。看起来所有细节 - 标签、前缀、标识符、架构 - 都是必需的。
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<ds:Signature Id="{{someValue}}" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="soapenv urn urn1 urn2 urn3 urn4" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#{{someValue}}">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="urn urn1 urn2 urn3 urn4" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>VALUEOFDIGIESTVALUE</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>VALUEOFSIGNATUREVALUE</ds:SignatureValue>
<ds:KeyInfo Id="{{someValue}}">
<wsse:SecurityTokenReference wsu:Id="{{someValue}}">
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>{{someValue}}</ds:X509IssuerName>
<ds:X509SerialNumber>{{someValue}}</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
每个提示都有帮助,我不希望得到全面的答案,但我最感兴趣的是以下问题:
在与 SignedXml
战斗两周后,我需要在 2021 年确认相同的状态,即 described by Rick Stahl in 2008:
难怪签名和证书加密的安全性在 Windows 上被如此轻易地使用。这是一个令人头疼的问题,有可怕的记录,需要大量工具。
虽然Java县的同事说这个在国外简单很多,但我认为问题是SOAP服务的规范过于复杂。
解决方法
这是基于提到的 Rick Stahl's article 的部分解决方案。假设我们有要签名的 XML 文档:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#"
xmlns:star="http://www.starstandards.org/STAR"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:oa="http://www.openapplications.org/oagis">
<SOAP:Body>
<!-- data to be signed here -->
</SOAP:Body>
</SOAP:Envelope>
使用该代码:
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
namespace XmlSigner
{
class Program
{
static void Main(string[] args)
{
// Create a new XML document.
var doc = new XmlDocument();
// Format the document to ignore white spaces.
doc.PreserveWhitespace = false;
// Load the passed XML file using it's name.
doc.Load(new XmlTextReader("C:\\Repos\\request.xml"));
// Initialize the certificate.
var certificate = new X509Certificate2("C:\\Repos\\certificate.pfx","youdontguessthispassword");
// Sign the document
var signedDoc = SignSoapBody(doc,certificate);
// Write to the output file.
File.WriteAllText("C:\\Repos\\signed.xml",signedDoc.OuterXml);
// Validate the result.
if (ValidateSoapBodySignature(signedDoc,certificate))
Console.WriteLine("Everything looks good,keep going.");
else
Console.WriteLine("You've screwed,back to work.");
}
private const string STR_SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/";
private const string STR_SOAPSEC_NS = "http://schemas.xmlsoap.org/soap/security/2000-12";
/// <summary>
/// Signs the SOAP document and adds a digital signature to it.
///
/// Note a lot of optional settings are applied against
/// key and certificate info to match the required XML document
/// structure the server requests.
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="certFriendlyName">Friendly Name of Cert installed in the Certificate Store under CurrentUser | Personal</param>
/// <returns></returns>
static public XmlDocument SignSoapBody(XmlDocument xmlDoc,X509Certificate2 cert)
{
// Add search Namespaces references to ensure we can reliably work
// against any SOAP docs regardless of tag naming
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("SOAP",STR_SOAP_NS);
ns.AddNamespace("SOAP-SEC",STR_SOAPSEC_NS);
// Grab the body element - this is what we create the signature from
XmlElement body = xmlDoc.DocumentElement.SelectSingleNode(@"//SOAP:Body",ns) as XmlElement;
if (body == null)
throw new ApplicationException("No body tag found");
// We'll only encode the <SOAP:Body> - add id: Reference as #Body
var bodyId = $"id-{Guid.NewGuid()}";
body.SetAttribute("id",bodyId);
// Signed XML will create Xml Signature - Xml fragment
SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.Signature.Id = $"sid-{Guid.NewGuid()}";
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
// Create a KeyInfo structure
KeyInfo keyInfo = new KeyInfo();
keyInfo.Id = $"kid-{Guid.NewGuid()}";
// The actual key for signing - MAKE SURE THIS ISN'T NULL!
signedXml.SigningKey = cert.PrivateKey;
// Specifically use the issuer and serial number for the data rather than the default
KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
keyInfoData.AddIssuerSerial(cert.Issuer,cert.GetSerialNumberString());
keyInfo.AddClause(keyInfoData);
// provide the certficate info that gets embedded - note this is only
// for specific formatting of the message to provide the cert info
signedXml.KeyInfo = keyInfo;
// Again unusual - meant to make the document match template
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
// Now create reference to sign: Point at the Body element
Reference reference = new Reference();
reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
reference.Uri = $"#{bodyId}"; // reference id=body section in same doc
reference.AddTransform(new XmlDsigExcC14NTransform()); // required to match doc
signedXml.AddReference(reference);
// Finally create the signature
signedXml.ComputeSignature();
// Result is an XML node with the signature detail below it
// Now let's add the sucker into the SOAP-HEADER
XmlElement signedElement = signedXml.GetXml();
// Create SOAP-SEC:Signature element
XmlElement soapSignature = xmlDoc.CreateElement("Security",STR_SOAPSEC_NS);
soapSignature.Prefix = "wsse";
//soapSignature.SetAttribute("MustUnderstand","","1");
// And add our signature as content
soapSignature.AppendChild(signedElement);
// Now add the signature header into the master header
XmlElement soapHeader = xmlDoc.DocumentElement.SelectSingleNode("//SOAP:Header",ns) as XmlElement;
if (soapHeader == null)
{
soapHeader = xmlDoc.CreateElement("Header",STR_SOAP_NS);
soapHeader.Prefix = "SOAP";
xmlDoc.DocumentElement.InsertBefore(soapHeader,xmlDoc.DocumentElement.ChildNodes[0]);
}
soapHeader.AppendChild(soapSignature);
return xmlDoc;
}
/// <summary>
/// Validates the Xml Signature in a document.
///
/// This routine is significantly simpler because the key parameters
/// are embedded into the signature itself. All that's needed is a
/// certificate to provide the key - the rest can be read from the
/// Signature itself.
/// </summary>
/// <param name="doc"></param>
/// <param name="publicCertFileName"></param>
/// <returns></returns>
static public bool ValidateSoapBodySignature(XmlDocument doc,X509Certificate2 cert)
{
// Load the doc this time
SignedXml sdoc = new SignedXml(doc);
// Find the signature and load it into SignedXml
XmlNodeList nodeList = doc.GetElementsByTagName("Signature");
sdoc.LoadXml((XmlElement)nodeList[0]);
// Now read the actual signature and validate
bool result = sdoc.CheckSignature(cert,true);
return result;
}
}
}
我有这个结果:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#" xmlns:star="http://www.starstandards.org/STAR" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oa="http://www.openapplications.org/oagis">
<SOAP:Header>
<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/soap/security/2000-12">
<Signature Id="sid-f3d74053-b47a-4740-b105-74ba1a550d38" xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#id-9718651b-c7a4-47a0-bcd1-f89e1a5f8395">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>SOME_SECRET_VALUES</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>SOME_SECRET_VALUES</SignatureValue>
<KeyInfo Id="kid-626cac63-9f43-46a9-80e3-ed350ef6cea2">
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=XX,OU=XX,O=XX,L=XX,C=XX</X509IssuerName>
<X509SerialNumber>SOME_SECRET_VALUES</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</KeyInfo>
</Signature>
</wsse:Security>
</SOAP:Header>
<SOAP:Body id="id-9718651b-c7a4-47a0-bcd1-f89e1a5f8395"> <!-- data to be signed here -->
</SOAP:Body>
</SOAP:Envelope>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。