It can be done, but it's necessary to modify the SignedXml class to add the prefix before getting the digest value of the SignedInfo node.
The ComputeSignature method will be modify to add the prefix parameter
public void ComputeSignature(string prefix){...}
When this method is called it calculates the Signature Value by digesting the value of the SignedInfo node, if you get this value without the "ds" prefix and then add the prefix you will get an invalid signature, so you will have to add the prefix BEFORE getting the digest value of the signedinfo node.
This digest value is generated in the method GetC14NDigest, so this method will be modify to add the prefix parameter and to add the prefix BEFORE getting the digest value
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);
}
Ok, so now you have the Signature Value of the SignedInfo nodes WITH the "ds" prefix, that being said you still DON'T have the xml with the prefix yet, so if you just call the GetXml method you will get the xml without the prefix and of course because the signature value was calculated considering the ds prefix you will have an invalid signature.
To avoid that and get the xml structure with the prefix you have to modify the GetXml method, add the prefix parameter, and call the SetPrefix method wich will add the "ds" prefix to all the nodes in the Signature Xml
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e); //return the xml structure with the prefix
return e;
}
I'll leave here the class with those modifications
CUSTOM CLASS
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;
}
}
And the way to use it
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");