Есть ли способ установить префикс для подписи подписанного XML-документа (класс SignedXml в .Net)?
Так что вместо:
<Signature xmlns = "http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
У меня могло быть следующее:
<ds:Signature xmlns:ds = "http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>





Во-первых, для этого действительно нет никаких веских причин. Эти две формы функционально эквивалентны. Любой хорошо настроенный XML-процессор будет обрабатывать их абсолютно идентично. Поэтому, если вы не пытаетесь поговорить с приложением, которое не реализует должным образом пространства имен XML, лучше (IMO) просто оставить форму по умолчанию в покое. (И даже в этом случае было бы лучше, если это вообще возможно, исправить ошибочную заявку.)
Тем не менее, вы можете вручную установить префикс для XmlElement, который возвращается SignedXml.GetXml () и его дочерними элементами, используя XPath следующим образом:
XmlElement signature = signedXml.GetXml();
foreach (XmlNode node in signature.SelectNodes(
"descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
{
node.Prefix = "ds";
}
Это может нарушить подпись, в зависимости от того, был ли элемент подписи включен в подписываемую информацию (если преобразование не исключает его, он сломается). Так что это не сработает во всех возможных случаях.
Если вы посмотрите на класс SignedXml в Reflector, он, похоже, в значительной степени жестко привязан к тому, чтобы НЕ использовать префикс, поэтому, если вы не можете заставить его исключить элемент Signature из подписи, я не уверен, что есть какие-либо другие работоспособные варианты ...
Для тех, кто сейчас находит эту ветку, я бы порекомендовал dotPeek от jetbrains now вместо продукта red gate. В 2008 году, безусловно, лучшим был рефлектор. Я определенно считаю, что dotPeek лучше.
Это невозможно. Если вы измените XML после подписания, его невозможно будет проверить, как это было в приведенном выше примере. ИМО, это недостаток в реализации цифровой подписи MSFT, с которым вам придется смириться.
Многие люди скажут, что для этого нет причин, и что они технически правы. Но когда вы имеете дело с крупным поставщиком (т. Е. С правительством штата или банком), удачи, чтобы они поменяли его со своей стороны. Большинство эталонных реализаций включают его.
ОБНОВЛЕНИЕ: подпись подписывает все в элементе SignedInfo, поэтому, если вы обновите этот элемент постфактум, подпись больше не действительна. Вы "подделали" сообщение.
какое-либо решение по этому поводу, как мы устанавливаем префикс и проверяем его?
Это можно сделать, но необходимо изменить класс SignedXml, чтобы добавить префикс до получения значения дайджеста узла SignedInfo.
Метод ComputeSignature будет изменен для добавления параметра префикса.
public void ComputeSignature(string prefix){...}
Когда этот метод вызывается, он вычисляет значение подписи путем переваривания значения узла SignedInfo. Если вы получите это значение без префикса «ds», а затем добавите префикс, вы получите недопустимую подпись, поэтому вам придется добавить префикс ДО получения значения дайджеста узла подписанной информации.
Это значение дайджеста создается в методе GetC14NDigest, поэтому этот метод будет изменен для добавления параметра префикса и добавления префикса ДО получения значения дайджеста.
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); /*Set the prefix before getting the HASH*/
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
Итак, теперь у вас есть значение подписи узлов SignedInfo с префиксом "ds", при этом у вас еще НЕТ xml с префиксом, поэтому, если вы просто вызовете метод GetXml, вы получите xml без префикс и, конечно же, поскольку значение подписи было вычислено с учетом префикса ds, у вас будет недействительная подпись. Чтобы избежать этого и получить структуру xml с префиксом, вам необходимо изменить метод GetXml, добавить параметр префикса и вызвать метод SetPrefix, который добавит префикс «ds» ко всем узлам в Signature Xml.
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e); //return the xml structure with the prefix
return e;
}
Я оставлю здесь класс с этими модификациями
ТАМОЖЕННЫЙ КЛАСС
internal sealed class CustomSignedXml : SignedXml
{
XmlElement obj = null;
public CustomSignedXml (XmlDocument xml)
: base(xml)
{
}
public CustomSignedXml (XmlElement xmlElement)
: base(xmlElement)
{
}
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e);
return e;
}
public void ComputeSignature(string prefix)
{
this.BuildDigestedReferences();
AsymmetricAlgorithm signingKey = this.SigningKey;
if (signingKey == null)
{
throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
if (!(signingKey is DSA))
{
if (!(signingKey is RSA))
{
throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
}
}
else
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
}
}
SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
if (description == null)
{
throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
}
HashAlgorithm hash = description.CreateDigest();
if (hash == null)
{
throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
}
this.GetC14NDigest(hash, prefix);
this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); //Set the prefix before getting the HASH
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
private void BuildDigestedReferences()
{
Type t = typeof(SignedXml);
MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(this, new object[] { });
}
private void SetPrefix(string prefix, XmlNode node)
{
foreach (XmlNode n in node.ChildNodes)
SetPrefix(prefix, n);
node.Prefix = prefix;
}
}
И способ его использования
CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
.
//compute the signature with the "ds" prefix
signedXml.ComputeSignature("ds");
//get the xml of the signature with the "ds" prefix
XmlElement xmlDigitalSignature = signedXml.GetXml("ds");
Этот код не работает, потому что он вызывает this.SignedInfo.GetXml(); в GetC14NDigest, у которого нет необходимого префикса в сгенерированных элементах XML.
Я согласен с тем, что это то же самое, и что он должен работать. Однако я не уверен, смогу ли я заставить другую сторону работать без префикса ds. Разве простое изменение префикса после сгенерированного sig не приведет к сбою проверки?