Я работаю над отправкой XML-сообщения в государственное учреждение (используя спецификации этого государственного учреждения, поэтому я не могу контролировать, как должен выглядеть результирующий XML), и я использую C# для своей разработки (политика компании).
Два человека, которые намного лучше меня разбираются в C# и интернет-технологиях, просмотрели XML раньше меня и сообщили мне, что WCF не поддерживает методы, необходимые для создания подписи в XML-документе (это было немного облегчением, поскольку я не разрабатывал никаких проектов WCF, и это пугает, так как я понимаю, что WCF является зрелой веб-технологией).
Итак, я использовал комбинацию LINQ to XML и System.Xml для создания сообщения и попытки подписать его.
Вот немного урезанный образец XML:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap-sec="http://schemas.xmlsoap.org/security/2000-12"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:cns="http://customNamespace1.com"
xmlns:cnt="http://customNamespace2.com"
>
<soapenv:Header>
<ns2:Element1 xmlns:ns2="http://namespace2.element1.com" wsu:Id="id-1">
...
</ns2:Element1>
<ns2:Element2 xmlns:ns2="http://namespace2.element2.com/" wsu:Id="id-2">
...
</ns2:Element2>
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<wsu:Timestamp wsu:Id="id-3">
...
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="id-4">
...
</wsse:UsernameToken>
<wsse:BinarySecurityToken ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-5bf699c7-5336-4695-b395-88d2b984fe54" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
...
</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-6" 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="SOAP-ENV cnt soap-sec soapenv sp cns wsdl wsp wsse wsu xs xsi" 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="#id-1">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="SOAP-ENV cnt soap-sec soapenv sp cns wsdl wsp wsse wsu xs xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-2">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="SOAP-ENV cnt soap-sec soapenv sp cns wsdl wsp wsse wsu xs xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-3">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="SOAP-ENV cnt soap-sec soapenv sp cns wsdl wsp wsse xs xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-4">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="SOAP-ENV cnt soap-sec soapenv sp cns wsdl wsp wsu xs xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-5">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="SOAP-ENV cnt soap-sec sp cns wsdl wsp wsse wsu xs xsi" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
...
</ds:SignatureValue>
<ds:KeyInfo Id="KI-ABDCFEC7595B7819C213402151542862">
<wsse:SecurityTokenReference wsu:Id="STR-ABDCFEC7595B7819C213402151542863">
<wsse:Reference URI="#SecurityToken-5bf699c7-5336-4695-b395-88d2b984fe54" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" />
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</soapenv:Header>
<soapenv:Body wsu:Id="id-5">
<ns5:bodyelement xmlns:ns4="http://namespace4.com/" xmlns:ns3="http://namespace3.com/" xmlns:ns2="http://bodynamespace2.com/" xmlns:ns5="http://namespace5.com/">
...
</ns5:bodyelement>
</soapenv:Body>
</soapenv:Envelope>
Вот часть кода, который я пробовал (3 разных метода попытки заставить фрагмент uri работать). Я публикую здесь только часть кода, так как для создания соответствующего XML-кода потребовалось более 200 строк кода, который я сейчас пытаюсь подписать:
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)Key;
SignedXml xmlWSig = new SignedXml(myDoc);
xmlWSig.SigningKey = Key;
xmlWSig.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
XmlDsigExcC14NTransform canMethod = (XmlDsigExcC14NTransform)xmlWSig.SignedInfo.CanonicalizationMethodObject;
canMethod.InclusiveNamespacesPrefixList = "SOAP-ENV cns soap-sec soapenv sp cnt wsdl wsp wsse wsu xs xsi";
Uri uri = new Uri("#id-1");
Reference ref1 = new Reference(uri.ToString());
XmlDsigExcC14NTransform transform1 = new XmlDsigExcC14NTransform("SOAP-ENV cns soap-sec soapenv sp cnt wsdl wsp wsse wsu xs xsi");
ref1.AddTransform(transform1);
ref1.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
xmlWSig.AddReference(ref1);
Reference ref2 = new Reference("#id-2");
XmlDsigExcC14NTransform transform2 = new XmlDsigExcC14NTransform("SOAP-ENV cns soap-sec soapenv sp cnt wsdl wsp wsse wsu xs xsi");
ref2.AddTransform(transform2);
ref2.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
xmlWSig.AddReference(ref2);
Reference ref3 = new Reference("");
ref3.Uri = "#id-3";
XmlDsigExcC14NTransform transform3 = new XmlDsigExcC14NTransform("SOAP-ENV cns soap-sec soapenv sp cnt wsdl wsp wsse xs xsi");
ref3.AddTransform(transform3);
ref3.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
xmlWSig.AddReference(ref3);
//repeat things for id-4, and id-5
KeyInfo myKeyInfo = new KeyInfo();
myKeyInfo.AddClause(new RSAKeyValue((RSA)Key));
xmlWSig.KeyInfo = myKeyInfo;
xmlWSig.ComputeSignature();
XmlElement signedXmlElement = xmlWSig.GetXml();
Ключ — это закрытый ключ, полученный из сертификата X509 (и это то, что следует использовать в качестве ключа для подписи документа). myDoc — это сгенерированный мной System.Xml XmlDocument, в который мне нужно вставить подпись.
Метод № 1 дает мне System.UriFormatException: Invalid URI: формат URI не может быть определен.
Метод № 2. Дает мне элемент ссылки System.Security.Cryptography.CryptograpicException: искаженный формат (если я удаляю # из Uri, это дает мне System.UriFormatException: Invalid URI: URI пуст).
Метод №3 дает те же ошибки, что и метод №2.
Из всей документации по использованию Uris для подписей разрешено использование только фрагмента Uri (при условии, что элемент, на который делается ссылка, находится внутри того же документа), но класс Uri в C#, похоже, не принимает фрагменты как приемлемый Uri.
Ссылочному классу также требуется полный Uri, а не только фрагмент.
Я открыт для любых предложений о том, как я могу правильно сгенерировать подпись в этом XML, используя спецификации.
ОБНОВЛЕНИЕ: Хотя SignedXml + Reference + Transform кажется лучшим решением, теперь я начинаю полагать, что .NET имеет большой пробел в этих библиотеках, и переход к некоторым библиотекам более низкого уровня для создания подписи может быть необходимый.
К сожалению, я все еще пытаюсь сказать, какие библиотеки потребуются, и алгоритм поиска того, что нужно подписать. Мое понимание эксклюзивной канонизации заключалось в том, что вы подписывали только элементы, указанные префиксами, перечисленными в InclusiveNamespaces PrefixList, но URI в справке указывает вложенный документ, над которым должна стоять подпись, но элементы внутри указанных элементов не не использовать большинство включенных пространств имен. Я понимаю, как должны работать эти ссылки?