ドキュメントに署名するのはあなたなので、これは簡単なはずですが、System.Security.Cryptography.Xml のクラスは実際にはこれをサポートしていません。
署名要素にプレフィックスを付けるだけでよい場合は、署名自体が参照されていない場合 (または、参照されている要素の一部である場合は、「http://www.w3のように変換で削除されている限り)」 .org/2000/09/xmldsig#enveloped-signature ") の場合は、変更した SignedInfo 要素に基づいて SignatureValue を再計算するだけです。例については、以下の SignEnveloped メソッドを参照してください。
ただし、署名は、MSDN の How to: Verify the Digital Signatures of XML Documentsで概説されている検証に合格しません。これは、ドキュメントの SignedInfo を実際に読み取ってチェックする SignatureValue を計算するのではなく、SignedXml クラスが新しいものを生成しないように見えるためです。プレフィックス付きの要素。以下のクラスは、SignedXmls の一見誤った実装を回避しますが、接頭辞付きの要素を想定していない他のフレームワークでも検証の問題が発生する可能性があります。
public static class XmlSigning
{
private static Type tSignedXml = typeof(SignedXml);
private static ResourceManager SecurityResources = new ResourceManager("system.security", tSignedXml.Assembly);
//these methods from the SignedXml class still work with prefixed Signature elements, but they are private
private static ParameterExpression thisSignedXmlParam = Expression.Parameter(tSignedXml);
private static Func<SignedXml, bool> CheckSignatureFormat
= Expression.Lambda<Func<SignedXml, bool>>(
Expression.Call(thisSignedXmlParam, tSignedXml.GetMethod("CheckSignatureFormat", BindingFlags.NonPublic | BindingFlags.Instance)),
thisSignedXmlParam).Compile();
private static Func<SignedXml, bool> CheckDigestedReferences
= Expression.Lambda<Func<SignedXml, bool>>(
Expression.Call(thisSignedXmlParam, tSignedXml.GetMethod("CheckDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance)),
thisSignedXmlParam).Compile();
public static void SignEnveloped(XmlDocument xmlDoc, RSACryptoServiceProvider key, string signatureNamespacePrefix)
{
SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = key;
Reference reference = new Reference("");
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);
signedXml.ComputeSignature();
XmlElement xmlSignature = signedXml.GetXml();
if (!string.IsNullOrEmpty(signatureNamespacePrefix))
{
//Here we set the namespace prefix on the signature element and all child elements to "ds", invalidating the signature.
AssignNameSpacePrefixToElementTree(xmlSignature, "ds");
//So let's recompute the SignatureValue based on our new SignatureInfo...
//For XPath
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); //this prefix is arbitrary and used only for XPath
XmlElement xmlSignedInfo = xmlSignature.SelectSingleNode("ds:SignedInfo", namespaceManager) as XmlElement;
//Canonicalize the SignedInfo element
XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
XmlDocument signedInfoDoc = new XmlDocument();
signedInfoDoc.LoadXml(xmlSignedInfo.OuterXml);
transform.LoadInput(signedInfoDoc);
//Compute the new SignatureValue
string signatureValue = Convert.ToBase64String(key.SignData(transform.GetOutput() as MemoryStream, new SHA1CryptoServiceProvider()));
//Set it in the xml
XmlElement xmlSignatureValue = xmlSignature.SelectSingleNode("ds:SignatureValue", namespaceManager) as XmlElement;
xmlSignatureValue.InnerText = signatureValue;
}
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlSignature, true));
}
public static bool CheckSignature(XmlDocument xmlDoc, RSACryptoServiceProvider key)
{
if (key == null)
throw new ArgumentNullException("key");
SignedXml signedXml = new SignedXml(xmlDoc);
//For XPath
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); //this prefix is arbitrary and used only for XPath
XmlElement xmlSignature = xmlDoc.SelectSingleNode("//ds:Signature", namespaceManager) as XmlElement;
signedXml.LoadXml(xmlSignature);
//These are the three methods called in SignedXml's CheckSignature method, but the built-in CheckSignedInfo will not validate prefixed Signature elements
return CheckSignatureFormat(signedXml) && CheckDigestedReferences(signedXml) && CheckSignedInfo(signedXml, key);
}
private static bool CheckSignedInfo(SignedXml signedXml, AsymmetricAlgorithm key)
{
//Copied from reflected System.Security.Cryptography.Xml.SignedXml
SignatureDescription signatureDescription = CryptoConfig.CreateFromName(signedXml.SignatureMethod) as SignatureDescription;
if (signatureDescription == null)
throw new CryptographicException(SecurityResources.GetString("Cryptography_Xml_SignatureDescriptionNotCreated"));
Type type = Type.GetType(signatureDescription.KeyAlgorithm);
Type type2 = key.GetType();
if (type != type2 && !type.IsSubclassOf(type2) && !type2.IsSubclassOf(type))
return false;
HashAlgorithm hashAlgorithm = signatureDescription.CreateDigest();
if (hashAlgorithm == null)
throw new CryptographicException(SecurityResources.GetString("Cryptography_Xml_CreateHashAlgorithmFailed"));
//Except this. The SignedXml class creates and cananicalizes a Signature element without any prefix, rather than using the element from the document provided
byte[] c14NDigest = GetC14NDigest(signedXml, hashAlgorithm);
AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = signatureDescription.CreateDeformatter(key);
return asymmetricSignatureDeformatter.VerifySignature(c14NDigest, signedXml.Signature.SignatureValue);
}
private static byte[] GetC14NDigest(SignedXml signedXml, HashAlgorithm hashAlgorithm)
{
Transform canonicalizeTransform = signedXml.SignedInfo.CanonicalizationMethodObject;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(signedXml.SignedInfo.GetXml().OuterXml);
canonicalizeTransform.LoadInput(xmlDoc);
return canonicalizeTransform.GetDigestedOutput(hashAlgorithm);
}
private static void AssignNameSpacePrefixToElementTree(XmlElement element, string prefix)
{
element.Prefix = prefix;
foreach (var child in element.ChildNodes)
{
if (child is XmlElement)
AssignNameSpacePrefixToElementTree(child as XmlElement, prefix);
}
}
}