/// <summary> /// Tries to verify the signature(s) of the elements in the specified document. /// </summary> /// <param name="document">The document.</param> /// <param name="signature">Required parameter if the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Detached</c>; otherwise the parameter is ignored.</param> /// <returns><see langword="true" /> if the signature verification succeeds, otherwise <see langword="false" />.</returns> /// <exception cref="System.ArgumentNullException"> /// document /// or /// signature /// </exception> public bool TryVerifySignature( XmlDocument document, XmlDocument signature = null) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (signature == null && SignatureLocation == SignatureLocation.Detached) { throw new ArgumentNullException(nameof(signature)); } var signedXml = new SignedXmlWithId(document, IdAttributeNames); if (SignatureLocation != SignatureLocation.Detached) { signature = document; } var signatureElement = signature.GetElementsByTagName(_xmlSignatureLocalName) .OfType <XmlElement>() .FirstOrDefault(); if (signatureElement == null) { return(false); } signedXml.LoadXml(signatureElement); return(signedXml.CheckSignature(Asymmetric)); }
/// <summary> /// Signs the XML <paramref name="document" /> or only the elements specified by the <paramref name="xmlPath" />. /// </summary> /// <param name="document">The document to be signed.</param> /// <param name="xmlPath">The XML path selecting the XML elements to be signed. /// Can be <see langword="null" />, empty or consisted of whitespace characters only (see the remarks below). /// The parameter is ignored if the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Enveloping</c>.</param> /// <param name="namespaceManager">The namespace manager which can resolve the namespace prefixes used in the XPath expression in <paramref name="xmlPath" />. /// Can be <see langword="null" /> if <paramref name="xmlPath" /> is <see langword="null" /> or no name-space prefixes are used. /// The parameter is ignored if the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Enveloping</c>.</param> /// <param name="documentLocation">Specifies the location (URI) of the document if the property <see cref="SignatureLocation" /> is set to /// <see cref="SignatureLocation" /><c>.Detached</c> and <paramref name="xmlPath" /> specifies the whole document (e.g. is <see langword="null" />); /// otherwise the parameter is ignored. Can be <see langword="null" /> in any case.</param> /// <returns>If the property <see cref="SignatureLocation" /> is set to: /// <list type="bullet"><item><see cref="SignatureLocation" /><c>.Enveloped</c>: the method returns the modified <paramref name="document" /> with added signature element. /// Also if the <paramref name="xmlPath" /> selects one or more descending elements they will be modified too by adding /// an extra attribute that would look like this: <c>xml:id="signed0"</c>. /// </item><item><see cref="SignatureLocation" /><c>.Enveloping</c>: the method returns an XmlDocument of the signature which includes the root element of the <paramref name="document" />; /// </item><item><see cref="SignatureLocation" /><c>.Detached</c>: the method returns an XmlDocument containing the signature with a reference to the document location. /// </item></list></returns> /// <exception cref="System.ArgumentNullException">document</exception> /// <remarks><para> /// If the parameter <paramref name="xmlPath" /> is <see langword="null" />, empty or consist of whitespace characters only, /// the whole document will be signed as if the parameter was set to <c>"/"</c>. /// </para> /// <para> /// If the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Enveloped</c> (the default) and the parameter /// <paramref name="xmlPath" /> specifies the document node (e.g. <see langword="null" />), a signature element will be appended next to the children of the root element. /// If <paramref name="xmlPath" /> selects one or more elements of the document, each one will be represented in the inserted signature by a <c>Reference</c> element /// (XPath <c>"/Signature/SignedInfo/Reference"</c>) with its corresponding URI (<c>"/Signature/SignedInfo/Reference/@URI"</c>) and /// digest (<c>"/Signature/SignedInfo/Reference/DigestValue"</c>). The URI will be in the form "#xxxxx" and will refer to the signed element's /// <c>xml:Id</c> attribute. If the element does not have one, the signer will create and add a new <c>Id</c> that will look like this: <c>xml:id="signed0"</c>. /// </para> /// <para></para> /// <para> /// If the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Enveloping</c> the parameter <paramref name="xmlPath" /> will be ignored, /// the root element of the document will be signed and inserted in the element of the generated XML signature at XPath <c>"/Signature/Object"</c>. /// </para> /// <para> /// If the property <see cref="SignatureLocation" /> is set to <see cref="SignatureLocation" /><c>.Detached</c> and the path specifies the whole document /// (e.g. <see langword="null" />) the URI of the reference element of the signature (at XPath <c>"/Signature/SignedInfo/Reference/@URI"</c>) /// will contain the value of the parameter <paramref name="documentLocation" /> if specified or "". /// If the <paramref name="xmlPath" /> selects node(s) other than the document itself, the <paramref name="documentLocation" /> will be ignored and the reference URI(s) will point /// to the signed elements in the form "#xxxxx". If the elements do not have attributes specifying unique identifiers of type <c>xml:Id</c> they will be added in the /// respective elements of the <paramref name="document" />. /// </para></remarks> public XmlDocument Sign( XmlDocument document, string xmlPath = null, XmlNamespaceManager namespaceManager = null, Uri documentLocation = null) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var signer = new SignedXmlWithId(document, IdAttributeNames) { SigningKey = Asymmetric }; signer.SignedInfo.CanonicalizationMethod = _canonicalizationMethod; signer.SignedInfo.SignatureMethod = _signatureMethod; if (IncludeKeyInfo) { signer.KeyInfo = new KeyInfo(); signer.KeyInfo.AddClause(new RSAKeyValue((RSA)Asymmetric)); } foreach (var reference in GetReferences(signer, document, xmlPath, namespaceManager, documentLocation)) { signer.AddReference(reference); } signer.ComputeSignature(); var xmlSignature = signer.GetXml(); XmlDocument returnedDoc = null; XmlNode signatureParent = null; switch (SignatureLocation) { case SignatureLocation.Enveloped: returnedDoc = document; signatureParent = returnedDoc.DocumentElement; break; case SignatureLocation.Enveloping: case SignatureLocation.Detached: returnedDoc = new XmlDocument(); signatureParent = returnedDoc; break; } signatureParent.AppendChild( returnedDoc.ImportNode(xmlSignature, true)); return(returnedDoc); }