有没有办法用RSA签署XML文件,并且命名空间前缀为“ds:Signature”而不是“Signature”?我花了很多时间来解决这个问题,从我看来没有解决办法。
似乎它在System.Security.Cryptography.Xml.Signature类中是硬编码的。
XmlElement element = document.CreateElement("Signature", "http://www.w3.org/2000/09/xmldsig#");
如果有人知道解决方案,我需要签名,因为导入它的软件会使用“ds:signature”验证它,所以使用“ds”前缀软件会验证它是这样的:
public static bool VerifySignature(XmlDocument doc, RSA key, string prefix)
{
SignedXml xml = new SignedXml(doc);
string str = "Signature";
if (!string.IsNullOrEmpty(prefix))
{
str = string.Format("{0}:{1}", prefix, str);
}
XmlNodeList elementsByTagName = doc.GetElementsByTagName(str);
xml.LoadXml((XmlElement)elementsByTagName[0]);
return xml.CheckSignature(key);
}
VerifySignature(xmlDoc, rsa, "ds");
通常它会这样:
<kk>blabla<Signature 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=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</DigestValue></Reference></SignedInfo><SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</SignatureValue></Signature></kk>
我需要这样做:
<kk>blabla<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><ds:DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</ds:SignatureValue></ds:Signature></kk>
答案 0 :(得分:8)
如果有人知道解决方案,我需要签名,因为导致它的软件使用“ds:signature”验证它,所以使用“ds”前缀
前缀应该不重要 - 所有重要的是元素所在的命名空间。如何表示命名空间应该无关紧要。如果确实如此,那就表明验证码中的破损,我会说。
但是,如果确实想要这样做,是否有任何理由您不想仅使用相同内容替换元素,而是使用您想要的前缀?在LINQ to XML中应该不难做到这一点。
答案 1 :(得分:7)
我找到了解决方案here
using System;
using System.Reflection;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace mysign
{
public class PrefixedSignedXML : SignedXml
{
public PrefixedSignedXML(XmlDocument document)
: base(document)
{ }
public PrefixedSignedXML(XmlElement element)
: base(element)
{ }
public PrefixedSignedXML()
: base()
{ }
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);
}
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e);
return e;
}
//Invocar por reflexión al método privado SignedXml.BuildDigestedReferences
private void BuildDigestedReferences()
{
Type t = typeof(SignedXml);
MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(this, new object[] { });
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
//string securityUrl = (this.m_containingDocument == null) ? null : this.m_containingDocument.BaseURI;
//XmlResolver xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl);
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
//CanonicalXmlNodeList namespaces = (this.m_context == null) ? null : Utils.GetPropagatedAttributes(this.m_context);
//Utils.AddNamespaces(document.DocumentElement, namespaces);
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
//canonicalizationMethodObject.Resolver = xmlResolver;
//canonicalizationMethodObject.BaseURI = securityUrl;
SetPrefix(prefix, document.DocumentElement); //establecemos el prefijo antes de se que calcule el hash (o de lo contrario la firma no será válida)
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
private void SetPrefix(string prefix, XmlNode node)
{
foreach (XmlNode n in node.ChildNodes)
SetPrefix(prefix, n);
node.Prefix = prefix;
}
}
}
答案 2 :(得分:1)
我试过这些解决方案,但他们没有成功。但是,通过查看.NET源代码(http://referencesource.microsoft.com/),我们可以看到,通过向SignedXml提供派生的XmlDocument类可以很容易地实现这一点,SignedXml可以添加命名空间。但是,拥有&#34; ds&#34; “签名信息”中的前缀&#39;和后代将导致siganture失败。这是我在不破坏签名的情况下所做的最好的事情:
XmlDsigDocument.cs
using System;
using System.Collections.Generic;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
namespace CustomSecurity
{
class XmlDsigDocument : XmlDocument
{
// Constants
public const string XmlDsigNamespacePrefix = "ds";
/// <summary>
/// Override CreateElement function as it is extensively used by SignedXml
/// </summary>
/// <param name="prefix"></param>
/// <param name="localName"></param>
/// <param name="namespaceURI"></param>
/// <returns></returns>
public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
{
// CAntonio. If this is a Digital signature security element, add the prefix.
if (string.IsNullOrEmpty(prefix))
{
// !!! Note: If you comment this line, you'll get a valid signed file! (but without ds prefix)
// !!! Note: If you uncomment this line, you'll get an invalid signed file! (with ds prefix within 'Signature' object)
//prefix = GetPrefix(namespaceURI);
// The only way to get a valid signed file is to prevent 'Prefix' on 'SignedInfo' and descendants.
List<string> SignedInfoAndDescendants = new List<string>();
SignedInfoAndDescendants.Add("SignedInfo");
SignedInfoAndDescendants.Add("CanonicalizationMethod");
SignedInfoAndDescendants.Add("InclusiveNamespaces");
SignedInfoAndDescendants.Add("SignatureMethod");
SignedInfoAndDescendants.Add("Reference");
SignedInfoAndDescendants.Add("Transforms");
SignedInfoAndDescendants.Add("Transform");
SignedInfoAndDescendants.Add("InclusiveNamespaces");
SignedInfoAndDescendants.Add("DigestMethod");
SignedInfoAndDescendants.Add("DigestValue");
if (!SignedInfoAndDescendants.Contains(localName))
{
prefix = GetPrefix(namespaceURI);
}
}
return base.CreateElement(prefix, localName, namespaceURI);
}
/// <summary>
/// Select the standar prefix for the namespaceURI provided
/// </summary>
/// <param name="namespaceURI"></param>
/// <returns></returns>
public static string GetPrefix(string namespaceURI)
{
if (namespaceURI == "http://www.w3.org/2001/10/xml-exc-c14n#")
return "ec";
else if (namespaceURI == SignedXml.XmlDsigNamespaceUrl)
return "ds";
return string.Empty;
}
}
}
这用于SignedXml Creation:
// Create a new XML document.
XmlDsigDocument doc = new XmlDsigDocument();
// Load the passed XML file using its name.
doc.Load(new XmlTextReader(FileName));
// Create a SignedXml object.
SignedXml signedXml = new SignedXml(doc);
您可以在以下位置查看完整的源文件:
答案 3 :(得分:0)
可能是正确的SetPrefix代码如下所示:
private void SetPrefix(String prefix, XmlNode node) {
foreach (XmlNode n in node.ChildNodes)
{
SetPrefix(prefix, n);
n.Prefix = prefix;
}
}
答案 4 :(得分:0)
我同意Prefix不重要,但是......
如果您使用XPath,XML在C#中变得更容易 :
var s = signedXml.GetXml();
XmlNodeList nodes = s.SelectNodes("descendant-or-self::*");
foreach (XmlNode childNode in nodes)
{
childNode.Prefix = "dsig";
}
答案 5 :(得分:0)
代码George Dima提供作品。
我会解释它是如何运作的。
当您调用ComputeSignature方法时,这将通过消化SignedInfo节点的值来生成签名值。
George Dima提供的代码在获取摘要值之前将前缀添加到SignedInfo节点及其子节点。这不会将前缀添加到整个xml结构
这是生成signedinfo节点的摘要值的方法
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); //HERE'S WHERE THE PREFIX IS ADDED TO GET THE DIGEST VALUE
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
因此,您现在拥有带有前缀的SignedInfo节点的摘要值,并且此值将用于获取签名值,但您仍然没有带前缀的xml,所以如果您这样做
signedXml.GetXml();
你将获得没有前缀的xml,当然因为签名值是根据ds前缀计算的,你将拥有一个无效的签名所以你需要做的是调用GetXml传递前缀的值,在这里case“ds”喜欢这个
signedXml.GetXml("ds");