/// <summary> /// Verify if the mandatory Target attribute exists on the SignatureProperty tag /// </summary> /// <param name="reader">NodeReader positioned at the SignatureProperty tag</param> /// <param name="signatureId">value of the Id attribute on the Signature tag</param> /// <returns>true, if Target attribute is present and has the correct value, else false</returns> private static bool VerifyTargetAttribute(XmlReader reader, string signatureId) { string idTargetValue = reader.GetAttribute(XTable.Get(XTable.ID.TargetAttrName)); if (idTargetValue != null) { //whether there is an Id attribute on the <Signature> tag or no, //an empty Target attribute on <SignatureProperty> tag, is allowed. //Empty string means current document if (String.CompareOrdinal(idTargetValue, String.Empty) == 0) { return(true); } else { //If the Target attribute has a non-empty string then //it must match the <Signature> tag Id attribute value if (signatureId != null && String.CompareOrdinal(idTargetValue, "#" + signatureId) == 0) { return(true); } else { return(false); } } } else { return(false); } }
/// <summary> /// Parse the DigestMethod tag /// </summary> /// <param name="reader"></param> private static string ParseDigestAlgorithmTag(XmlReader reader) { // verify namespace and lack of attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) > 1 || String.CompareOrdinal(reader.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) != 0 || reader.Depth != 3) { throw new XmlException(SR.Get(SRID.XmlSignatureParseError)); } // get the Algorithm attribute string hashAlgorithm = null; if (reader.HasAttributes) { hashAlgorithm = reader.GetAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); } if (hashAlgorithm == null || hashAlgorithm.Length == 0) { throw new XmlException(SR.Get(SRID.UnsupportedHashAlgorithm)); } return(hashAlgorithm); }
/// <summary> /// Parses a Reference tag /// </summary> /// <param name="reader"></param> /// <returns>partManifestEntry that represents the state of the tag</returns> private static PartManifestEntry ParseReference(XmlReader reader) { Debug.Assert(reader != null); // <Reference> found - get part Uri from the tag ContentType contentType = null; Uri partUri = ParsePartUri(reader, out contentType); // only allocate if this turns out to be a Relationship transform List <PackageRelationshipSelector> relationshipSelectors = null; // move through the sub-tags: <DigestMethod>, <DigestValue> and optional <Transforms> string hashAlgorithm = null; // digest method string hashValue = null; // digest value List <String> transforms = null; // optional transform algorithm names bool transformsParsed = false; // since null is legal for transforms var we need a // bool to detect multiples while (reader.Read() && (reader.MoveToContent() == XmlNodeType.Element)) { // Correct Namespace? if (String.CompareOrdinal(reader.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) != 0 || reader.Depth != 3) { throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); } // DigestMethod? if (hashAlgorithm == null && String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.DigestMethodTagName)) == 0) { hashAlgorithm = ParseDigestAlgorithmTag(reader); continue; } // DigestValue? if (hashValue == null && String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.DigestValueTagName)) == 0) { hashValue = ParseDigestValueTag(reader); continue; } // TransformsTag? if (!transformsParsed && String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.TransformsTagName)) == 0) { transforms = ParseTransformsTag(reader, partUri, ref relationshipSelectors); transformsParsed = true; continue; } // if we get to here, we didn't see what we expected throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); } // add to our list return(new PartManifestEntry(partUri, contentType, hashAlgorithm, hashValue, transforms, relationshipSelectors)); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ /// <summary> /// Parse the Manifest tag /// </summary> /// <param name="manager"></param> /// <param name="reader">XmlReader positioned to the Manifest tag</param> /// <param name="partManifest"></param> /// <param name="partEntryManifest"></param> /// <param name="relationshipManifest"></param> internal static void ParseManifest( PackageDigitalSignatureManager manager, XmlReader reader, out List <Uri> partManifest, out List <PartManifestEntry> partEntryManifest, out List <PackageRelationshipSelector> relationshipManifest) { Invariant.Assert(manager != null); Invariant.Assert(reader != null); // these are empty (non-null) when nothing is found partManifest = new List <Uri>(); partEntryManifest = new List <PartManifestEntry>(); relationshipManifest = new List <PackageRelationshipSelector>(); // manually parse the Relationship tags because they are custom formed and the Reference class will not handle // them correctly string referenceTagName = XTable.Get(XTable.ID.ReferenceTagName); int referenceCount = 0; while (reader.Read() && (reader.MoveToContent() == XmlNodeType.Element)) { // should be on a <Reference> tag if (String.CompareOrdinal(reader.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) == 0 && (String.CompareOrdinal(reader.LocalName, referenceTagName) == 0) && reader.Depth == 2) { // Parse each reference - distinguish between Relationships and Parts // because we don't store the Relationship-part itself - just it's Relationships. PartManifestEntry partManifestEntry = ParseReference(reader); if (partManifestEntry.IsRelationshipEntry) { foreach (PackageRelationshipSelector relationshipSelector in partManifestEntry.RelationshipSelectors) { relationshipManifest.Add(relationshipSelector); } } else { partManifest.Add(partManifestEntry.Uri); } // return the manifest entry to be used for hashing partEntryManifest.Add(partManifestEntry); referenceCount++; } else { throw new XmlException(SR.Get(SRID.UnexpectedXmlTag, reader.Name)); } } // XmlDSig xsd requires at least one <Reference> tag if (referenceCount == 0) { throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); } }
/// <summary> /// Parse the Relationship-specific Transform /// </summary> /// <param name="reader"></param> /// <param name="partUri"></param> /// <param name="relationshipSelectors">may be allocated but will never be empty</param> private static void ParseRelationshipsTransform(XmlReader reader, Uri partUri, ref List <PackageRelationshipSelector> relationshipSelectors) { Uri owningPartUri = PackUriHelper.GetSourcePartUriFromRelationshipPartUri(partUri); // find all of the Relationship tags of form: // <RelationshipReference SourceId="abc123" /> // or // <RelationshipsGroupReference SourceType="reference-type-of-the-week" /> while (reader.Read() && (reader.MoveToContent() == XmlNodeType.Element) && reader.Depth == 5) { // both types have no children, a single required attribute and belong to the OPC namespace if (reader.IsEmptyElement && PackagingUtilities.GetNonXmlnsAttributeCount(reader) == 1 && (String.CompareOrdinal(reader.NamespaceURI, XTable.Get(XTable.ID.OpcSignatureNamespace)) == 0)) { // <RelationshipReference>? if (String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.RelationshipReferenceTagName)) == 0) { // RelationshipReference tags are legal and these must be empty with a single SourceId attribute // get the SourceId attribute string id = reader.GetAttribute(XTable.Get(XTable.ID.SourceIdAttrName)); if (id != null && id.Length > 0) { if (relationshipSelectors == null) { relationshipSelectors = new List <PackageRelationshipSelector>(); } // we found a legal SourceId so create a selector and continue searching relationshipSelectors.Add(new PackageRelationshipSelector(owningPartUri, PackageRelationshipSelectorType.Id, id)); continue; } } // <RelationshipsGroupReference>? else if ((String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.RelationshipsGroupReferenceTagName)) == 0)) { // RelationshipsGroupReference tags must be empty with a single SourceType attribute string type = reader.GetAttribute(XTable.Get(XTable.ID.SourceTypeAttrName)); if (type != null && type.Length > 0) { // lazy init if (relationshipSelectors == null) { relationshipSelectors = new List <PackageRelationshipSelector>(); } // got a legal SourceType attribute relationshipSelectors.Add(new PackageRelationshipSelector(owningPartUri, PackageRelationshipSelectorType.Type, type)); continue; } } } // if we get to here, we have not found a legal tag so we throw throw new XmlException(SR.Get(SRID.UnexpectedXmlTag, reader.LocalName)); } }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// GenerateRelationshipSigningReferences /// </summary> /// <param name="manager"></param> /// <param name="xDoc"></param> /// <param name="hashAlgorithm"></param> /// <param name="relationshipSelectors"></param> /// <param name="manifest"></param> /// <returns>number of references to be signed</returns> private static int GenerateRelationshipSigningReferences( PackageDigitalSignatureManager manager, XmlDocument xDoc, HashAlgorithm hashAlgorithm, IEnumerable <PackageRelationshipSelector> relationshipSelectors, XmlNode manifest) { // PartUri - and its list of PackageRelationshipSelectors Dictionary <Uri, List <PackageRelationshipSelector> > partAndSelectorDictionary = new Dictionary <Uri, List <PackageRelationshipSelector> >(); foreach (PackageRelationshipSelector relationshipSelector in relationshipSelectors) { //update the partAndSelectorDictionary for each relationshipSelector Uri relationshipPartUri = PackUriHelper.GetRelationshipPartUri(relationshipSelector.SourceUri); List <PackageRelationshipSelector> selectors; if (partAndSelectorDictionary.ContainsKey(relationshipPartUri)) { selectors = partAndSelectorDictionary[relationshipPartUri]; } else { selectors = new List <PackageRelationshipSelector>(); partAndSelectorDictionary.Add(relationshipPartUri, selectors); } selectors.Add(relationshipSelector); } // now that we have them grouped by Part name, emit the XML // Here is an optimization for saving space by declaring the OPC namespace and prefix // in the <Manifest> tag. It will become: // <Manifest xmlns:opc="http://schemas.openxmlformats.org/package/2006/digital-signature"> // Later when we generate the RelationshipSigningReference we can use the namespace prefix "opc" // instead of the long namespace itself, thus saving some space if the manifest has more than one // RelationshipSigningReference. // XmlElement xmlE = (XmlElement)manifest; xmlE.SetAttribute(XTable.Get(XTable.ID.OpcSignatureNamespaceAttribute), XTable.Get(XTable.ID.OpcSignatureNamespace)); int count = 0; foreach (Uri partName in partAndSelectorDictionary.Keys) { // emit xml and append manifest.AppendChild( GenerateRelationshipSigningReference(manager, xDoc, hashAlgorithm, partName, /* we are guaranteed that this is a valid part Uri, so we do not use PackUriHelper.CreatePartUri */ partAndSelectorDictionary[partName])); count++; } return(count); }
private static XmlNode GenerateDigestValueNode(XmlDocument xDoc, HashAlgorithm hashAlgorithm, Stream s, String transformName) { // <DigestValue> XmlElement digestValue = xDoc.CreateElement(XTable.Get(XTable.ID.DigestValueTagName), SignedXml.XmlDsigNamespaceUrl); XmlText digestValueText = xDoc.CreateTextNode(XmlDigitalSignatureProcessor.GenerateDigestValue(s, transformName, hashAlgorithm)); digestValue.AppendChild(digestValueText); return(digestValue); }
/// <summary> /// Obtain a W3C formatted SigningTime (equivalent to TimeStamp) /// </summary> /// <param name="xDoc">xml document we are building</param> /// <param name="dateTime">time to persist</param> /// <param name="signatureId">id of new signature</param> /// <param name="xmlDateTimeFormat">format to use - must be Xml date format legal syntax</param> /// <returns>given writer with SignatureProperties xml added to it</returns> /// <remarks>format matches that described in http://www.w3.org/TR/NOTE-datetime </remarks> /// <example> /// <Object Id="Package"> /// <SignatureProperties> /// <SignatureProperty Target="#signatureId"> /// <SignatureTime> /// <Format>YYYY-MM-DDThh:mm:ssTZD</Format> /// <Value>1997-07-16T19:20:30.45+01:00</Value> /// </SignatureTime> /// </SignatureProperty> /// </SignatureProperties> /// </Object> /// </example> internal static XmlElement AssembleSignatureProperties( XmlDocument xDoc, DateTime dateTime, String xmlDateTimeFormat, String signatureId) { Invariant.Assert(xDoc != null); Invariant.Assert(signatureId != null); // check for null format - use default if null if (xmlDateTimeFormat == null) { xmlDateTimeFormat = DefaultDateTimeFormat; } string[] dateTimeFormats = ConvertXmlFormatStringToDateTimeFormatString(xmlDateTimeFormat); // <SignatureProperties> XmlElement signatureProperties = xDoc.CreateElement(XTable.Get(XTable.ID.SignaturePropertiesTagName), SignedXml.XmlDsigNamespaceUrl); // <SignatureProperty Id="idSignatureTime" Target="#signatureId"> XmlElement signatureProperty = xDoc.CreateElement(XTable.Get(XTable.ID.SignaturePropertyTagName), SignedXml.XmlDsigNamespaceUrl); signatureProperties.AppendChild(signatureProperty); XmlAttribute idAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.SignaturePropertyIdAttrName)); idAttr.Value = XTable.Get(XTable.ID.SignaturePropertyIdAttrValue); signatureProperty.Attributes.Append(idAttr); XmlAttribute targetAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.TargetAttrName)); targetAttr.Value = "#" + signatureId; signatureProperty.Attributes.Append(targetAttr); // <SignatureTime> XmlElement signatureTime = xDoc.CreateElement(XTable.Get(XTable.ID.SignatureTimeTagName), XTable.Get(XTable.ID.OpcSignatureNamespace)); XmlElement signatureTimeFormat = xDoc.CreateElement(XTable.Get(XTable.ID.SignatureTimeFormatTagName), XTable.Get(XTable.ID.OpcSignatureNamespace)); XmlElement signatureTimeValue = xDoc.CreateElement(XTable.Get(XTable.ID.SignatureTimeValueTagName), XTable.Get(XTable.ID.OpcSignatureNamespace)); signatureTimeFormat.AppendChild(xDoc.CreateTextNode(xmlDateTimeFormat)); signatureTimeValue.AppendChild(xDoc.CreateTextNode(DateTimeToXmlFormattedTime(dateTime, dateTimeFormats[0]))); signatureTime.AppendChild(signatureTimeFormat); signatureTime.AppendChild(signatureTimeValue); signatureProperty.AppendChild(signatureTime); return(signatureProperties); }
private static XmlNode GenerateDigestMethod( PackageDigitalSignatureManager manager, XmlDocument xDoc) { // <DigestMethod> XmlElement digestMethod = xDoc.CreateElement(XTable.Get(XTable.ID.DigestMethodTagName), SignedXml.XmlDsigNamespaceUrl); XmlAttribute digestAlgorithmAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); digestAlgorithmAttr.Value = manager.HashAlgorithm; digestMethod.Attributes.Append(digestAlgorithmAttr); return(digestMethod); }
/// <summary> /// Locates and selects the target XmlElement from all available Object tags /// </summary> /// <param name="signature"></param> /// <param name="idValue"></param> /// <returns>element if found; null if not found</returns> /// <remarks>relies on XPath query to search the Xml in each Object tag</remarks> private static XmlElement SelectSubObjectNodeForXAdESInDataObjects(Signature signature, string idValue) { XmlElement node = null; bool foundMatch = false; // true if id has matched, even with the wrong namespace // now find an object tag that includes an element that matches foreach (DataObject dataObject in signature.ObjectList) { // skip the package object if (String.CompareOrdinal(dataObject.Id, XTable.Get(XTable.ID.OpcAttrValue)) != 0) { XmlElement element = dataObject.GetXml(); // NOTE: this is executing an XPath query // idValue has already been tested as an NCName (unless overridden for compatibility), so there's no // escaping that needs to be done here. XmlNodeList nodeList = element.SelectNodes(".//*[@Id='" + idValue + "']"); if (nodeList.Count > 0) { if (!AllowAmbiguousReferenceTargets() && (nodeList.Count > 1 || foundMatch)) { throw new XmlException(SR.Get(SRID.DuplicateObjectId)); } foundMatch = true; XmlNode local = nodeList[0] as XmlElement; if (local != null) { XmlNode temp = local; // climb the tree towards the root until we find our namespace while ((temp != null) && (temp.NamespaceURI.Length == 0)) { temp = temp.ParentNode; } // only match if the target is in the XAdES namespace if ((temp != null) && (String.CompareOrdinal(temp.NamespaceURI, _XAdESNameSpace) == 0)) { node = local as XmlElement; // continue searching, to find duplicates from different objects } } } } } return(node); }
/// <summary> /// Verify if the SignatureProperty tag has a valid Id attribute /// </summary> /// <param name="reader">NodeReader positioned at the SignatureProperty tag</param> /// <returns>true, if Id attribute is present and has the correct value, else false</returns> private static bool VerifyIdAttribute(XmlReader reader) { string idAttrValue = reader.GetAttribute(XTable.Get(XTable.ID.SignaturePropertyIdAttrName)); if (idAttrValue != null && (String.CompareOrdinal(idAttrValue, XTable.Get(XTable.ID.SignaturePropertyIdAttrValue)) == 0)) { return(true); } else { return(false); } }
/// <summary> /// Generate Manifest tag /// </summary> /// <param name="manager">manager</param> /// <param name="xDoc">current Xml doc</param> /// <param name="hashAlgorithm">hash algorithm to hash with</param> /// <param name="parts">parts to sign - possibly null</param> /// <param name="relationshipSelectors">relationshipSelectors that represent the /// relationships that have to be signed - possibly null</param> /// <returns></returns> internal static XmlNode GenerateManifest( PackageDigitalSignatureManager manager, XmlDocument xDoc, HashAlgorithm hashAlgorithm, IEnumerable <Uri> parts, IEnumerable <PackageRelationshipSelector> relationshipSelectors) { Debug.Assert(manager != null); Debug.Assert(xDoc != null); Debug.Assert(hashAlgorithm != null); // check args if (!hashAlgorithm.CanReuseTransform) { throw new ArgumentException(SR.Get(SRID.HashAlgorithmMustBeReusable)); } // <Manifest> XmlNode manifest = xDoc.CreateNode(XmlNodeType.Element, XTable.Get(XTable.ID.ManifestTagName), SignedXml.XmlDsigNamespaceUrl); // add part references if (parts != null) { // loop and write - may still be empty foreach (Uri partUri in parts) { // generate a reference tag manifest.AppendChild(GeneratePartSigningReference(manager, xDoc, hashAlgorithm, partUri)); } } // any relationship references? int relationshipCount = 0; if (relationshipSelectors != null) { relationshipCount = GenerateRelationshipSigningReferences(manager, xDoc, hashAlgorithm, relationshipSelectors, manifest); } // did we sign anything? Manifest can NOT be empty if (parts == null && relationshipCount == 0) { throw new ArgumentException(SR.Get(SRID.NothingToSign)); } return(manifest); }
private static XmlNode GeneratePartSigningReference( PackageDigitalSignatureManager manager, XmlDocument xDoc, HashAlgorithm hashAlgorithm, Uri partName) { PackagePart part = manager.Package.GetPart(partName); // <Reference> XmlElement reference = xDoc.CreateElement(XTable.Get(XTable.ID.ReferenceTagName), SignedXml.XmlDsigNamespaceUrl); // add Uri with content type as Query XmlAttribute uriAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.UriAttrName)); uriAttr.Value = PackUriHelper.GetStringForPartUri(partName) + _contentTypeQueryStringPrefix + part.ContentType; reference.Attributes.Append(uriAttr); // add transforms tag if necessary String transformName = String.Empty; if (manager.TransformMapping.ContainsKey(part.ContentType)) { transformName = manager.TransformMapping[part.ContentType]; // <Transforms> XmlElement transforms = xDoc.CreateElement(XTable.Get(XTable.ID.TransformsTagName), SignedXml.XmlDsigNamespaceUrl); // <Transform> XmlElement transform = xDoc.CreateElement(XTable.Get(XTable.ID.TransformTagName), SignedXml.XmlDsigNamespaceUrl); XmlAttribute algorithmAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); algorithmAttr.Value = transformName; transform.Attributes.Append(algorithmAttr); transforms.AppendChild(transform); reference.AppendChild(transforms); } // <DigestMethod> reference.AppendChild(GenerateDigestMethod(manager, xDoc)); // <DigestValue> using (Stream s = part.GetStream(FileMode.Open, FileAccess.Read)) { reference.AppendChild(GenerateDigestValueNode(xDoc, hashAlgorithm, s, transformName)); } return(reference); }
/// <summary> /// Locates and selects the target XmlElement from all available Object tags /// </summary> /// <param name="signature"></param> /// <param name="idValue"></param> /// <returns>element if found; null if not found</returns> /// <remarks>relies on XPath query to search the Xml in each Object tag</remarks> private static XmlElement SelectSubObjectNodeForXAdESInDataObjects(Signature signature, string idValue) { XmlElement node = null; // now find an object tag that includes an element that matches foreach (DataObject dataObject in signature.ObjectList) { // skip the package object if (String.CompareOrdinal(dataObject.Id, XTable.Get(XTable.ID.OpcAttrValue)) != 0) { XmlElement element = dataObject.GetXml(); // NOTE: this is executing an XPath query XmlElement local = element.SelectSingleNode("//*[@Id='" + idValue + "']") as XmlElement; if (local == null) { continue; } // node found? if (local != null) { XmlNode temp = local; // climb the tree towards the root until we find our namespace while ((temp != null) && (temp.NamespaceURI.Length == 0)) { temp = temp.ParentNode; } // only match if the target is in the XAdES namespace if ((temp != null) && (String.CompareOrdinal(temp.NamespaceURI, _XAdESNameSpace) == 0)) { node = local as XmlElement; break; } } } } return(node); }
/// <summary> /// Get the part uri and it's content type from the current Reference tag /// </summary> /// <param name="reader"></param> /// <param name="contentType">contentType extracted from the query portion of the Uri</param> /// <returns>PackagePart uri and contentType</returns> private static Uri ParsePartUri(XmlReader reader, out ContentType contentType) { // should be a relative Package uri with a query portion that contains the ContentType contentType = ContentType.Empty; Uri partUri = null; // must be one and only one attribute if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) == 1) { string uriAttrValue = reader.GetAttribute(XTable.Get(XTable.ID.UriAttrName)); if (uriAttrValue != null) { partUri = ParsePartUriAttribute(uriAttrValue, out contentType); } } // will be null if we had no success if (partUri == null) { throw new XmlException(SR.Get(SRID.RequiredXmlAttributeMissing, XTable.Get(XTable.ID.UriAttrName))); } return(partUri); }
//----------------------------------------------------------------------------- // // Private Methods // //----------------------------------------------------------------------------- /// <summary> /// Parse the SignatureTime tag /// </summary> /// <param name="reader">NodeReader positioned at the SignatureProperty tag</param> /// <param name="timeFormat">format found</param> /// <exception cref="XmlException">illegal format</exception> /// <returns>signing time</returns> private static DateTime ParseSignatureTimeTag(XmlReader reader, out String timeFormat) { //There are no attributes on all the three tags that we parse in this method //<SignatureTime>, <Format>, <Value> int expectedAttributeCount = 0; string opcSignatureNameSpace = XTable.Get(XTable.ID.OpcSignatureNamespace); string signaturePropertyTag = XTable.Get(XTable.ID.SignaturePropertyTagName); string signatureTimeTag = XTable.Get(XTable.ID.SignatureTimeTagName); string timeValueTagName = XTable.Get(XTable.ID.SignatureTimeValueTagName); string timeFormatTagName = XTable.Get(XTable.ID.SignatureTimeFormatTagName); // <SignatureTime> must be one of <Format> or <Time> timeFormat = null; string timeValue = null; //Look for <SignatureTime> Tag if (reader.Read() && reader.MoveToContent() == XmlNodeType.Element && (String.CompareOrdinal(reader.NamespaceURI, opcSignatureNameSpace) == 0) && (String.CompareOrdinal(reader.LocalName, signatureTimeTag) == 0) && reader.Depth == 3 && PackagingUtilities.GetNonXmlnsAttributeCount(reader) == expectedAttributeCount) { while (reader.Read()) { if (String.CompareOrdinal(reader.NamespaceURI, opcSignatureNameSpace) == 0 && reader.MoveToContent() == XmlNodeType.Element && reader.Depth == 4) { // which tag do we have? if ((String.CompareOrdinal(reader.LocalName, timeValueTagName) == 0) && PackagingUtilities.GetNonXmlnsAttributeCount(reader) == expectedAttributeCount) { if (timeValue == null && reader.Read() && reader.MoveToContent() == XmlNodeType.Text && reader.Depth == 5) { //After reading the content, the reader progresses to the next element. //So after this method is called, the reader is positioned at the //EndElement corresponding to Value tag - </Value> // Note: ReadContentAsString will return String.Empty but never null timeValue = reader.ReadContentAsString(); Debug.Assert(reader.NodeType == XmlNodeType.EndElement); } else { //This would happen if we found more than one Value tags or if there //are other nested elements of if they are of a different XmlNodeType type throw new XmlException(SR.PackageSignatureCorruption); } } else if ((String.CompareOrdinal(reader.LocalName, timeFormatTagName) == 0) && PackagingUtilities.GetNonXmlnsAttributeCount(reader) == expectedAttributeCount) { if (timeFormat == null && reader.Read() && reader.MoveToContent() == XmlNodeType.Text && reader.Depth == 5) { //After reading the content, the reader progresses to the next element. //So after this method is called, the reader is positioned at the //EndElement corresponding to Format tag - </Format> // Note: ReadContentAsString will return String.Empty but never null timeFormat = reader.ReadContentAsString(); Debug.Assert(reader.NodeType == XmlNodeType.EndElement); } else { //This would happen if we found more than one Format tags or if there //are other nested elements of if they are of a different XmlNodeType type throw new XmlException(SR.PackageSignatureCorruption); } } else { //If we encounter any tag other than <Format> or <Time> nested within the <SignatureTime> tag throw new XmlException(SR.PackageSignatureCorruption); } } else //If we have encountered the end tag for the <SignatureTime> tag //then we are done parsing the tag, and we can stop the parsing. if (String.CompareOrdinal(signatureTimeTag, reader.LocalName) == 0 && (reader.NodeType == XmlNodeType.EndElement)) { //We must find a </SignatureProperty> tag at this point, //else it could be that there are more SignatureTime or //other tags nested here and that is an error. if (reader.Read() && reader.MoveToContent() == XmlNodeType.EndElement && String.CompareOrdinal(signaturePropertyTag, reader.LocalName) == 0) { break; } else { throw new XmlException(SR.PackageSignatureCorruption); } } else { // if we do not find the nested elements as expected throw new XmlException(SR.PackageSignatureCorruption); } } } else { throw new XmlException(SR.Format(SR.RequiredTagNotFound, signatureTimeTag)); } // generate an equivalent DateTime object if (timeValue != null && timeFormat != null) { return(XmlFormattedTimeToDateTime(timeValue, timeFormat)); } else { throw new XmlException(SR.PackageSignatureCorruption); } }
/// <summary> /// Generates a Reference tag that contains a Relationship transform /// </summary> /// <param name="manager">manager</param> /// <param name="relationshipPartName">name of the relationship part</param> /// <param name="xDoc">current xml document</param> /// <param name="hashAlgorithm">hash algorithm = digest method</param> /// <param name="relationshipSelectors">relationshipSelectors that represent the relationships to sign </param> /// <remarks>ContentType is known and part name can be derived from the relationship collection</remarks> private static XmlNode GenerateRelationshipSigningReference( PackageDigitalSignatureManager manager, XmlDocument xDoc, HashAlgorithm hashAlgorithm, Uri relationshipPartName, IEnumerable <PackageRelationshipSelector> relationshipSelectors) { string relPartContentType = PackagingUtilities.RelationshipPartContentType.ToString(); // <Reference> XmlElement reference = xDoc.CreateElement(XTable.Get(XTable.ID.ReferenceTagName), SignedXml.XmlDsigNamespaceUrl); // add Uri // persist the Uri of the associated Relationship part String relationshipPartString; if (PackUriHelper.ComparePartUri(relationshipPartName, PackageRelationship.ContainerRelationshipPartName) == 0) { relationshipPartString = PackageRelationship.ContainerRelationshipPartName.ToString(); } else { relationshipPartString = PackUriHelper.GetStringForPartUri(relationshipPartName); } XmlAttribute uriAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.UriAttrName)); uriAttr.Value = relationshipPartString + _contentTypeQueryStringPrefix + relPartContentType; reference.Attributes.Append(uriAttr); // add transforms tag (always necessary) // <Transforms> XmlElement transforms = xDoc.CreateElement(XTable.Get(XTable.ID.TransformsTagName), SignedXml.XmlDsigNamespaceUrl); // add Relationship transform String opcNamespace = XTable.Get(XTable.ID.OpcSignatureNamespace); String opcNamespacePrefix = XTable.Get(XTable.ID.OpcSignatureNamespacePrefix); XmlElement transform = xDoc.CreateElement(XTable.Get(XTable.ID.TransformTagName), SignedXml.XmlDsigNamespaceUrl); XmlAttribute algorithmAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); algorithmAttr.Value = XTable.Get(XTable.ID.RelationshipsTransformName); transform.Attributes.Append(algorithmAttr); // <RelationshipReference SourceId="abc" /> or // <RelationshipGroupReference SourceType="xyz" /> foreach (PackageRelationshipSelector relationshipSelector in relationshipSelectors) { switch (relationshipSelector.SelectorType) { case PackageRelationshipSelectorType.Id: { XmlNode relationshipNode = xDoc.CreateElement(opcNamespacePrefix, XTable.Get(XTable.ID.RelationshipReferenceTagName), opcNamespace); XmlAttribute idAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.SourceIdAttrName)); idAttr.Value = relationshipSelector.SelectionCriteria; relationshipNode.Attributes.Append(idAttr); transform.AppendChild(relationshipNode); } break; case PackageRelationshipSelectorType.Type: { XmlNode relationshipNode = xDoc.CreateElement(opcNamespacePrefix, XTable.Get(XTable.ID.RelationshipsGroupReferenceTagName), opcNamespace); XmlAttribute typeAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.SourceTypeAttrName)); typeAttr.Value = relationshipSelector.SelectionCriteria; relationshipNode.Attributes.Append(typeAttr); transform.AppendChild(relationshipNode); } break; default: Invariant.Assert(false, "This option should never be executed"); break; } } transforms.AppendChild(transform); // add non-Relationship transform (if any) String transformName = null; if (manager.TransformMapping.ContainsKey(relPartContentType)) { transformName = manager.TransformMapping[relPartContentType]; // let them override //Currently we only support two transforms and so we validate whether its //one of those if (transformName == null || transformName.Length == 0 || !XmlDigitalSignatureProcessor.IsValidXmlCanonicalizationTransform(transformName)) { throw new InvalidOperationException(SR.Get(SRID.UnsupportedTransformAlgorithm)); } // <Transform> transform = xDoc.CreateElement(XTable.Get(XTable.ID.TransformTagName), SignedXml.XmlDsigNamespaceUrl); algorithmAttr = xDoc.CreateAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); algorithmAttr.Value = transformName; transform.Attributes.Append(algorithmAttr); transforms.AppendChild(transform); } reference.AppendChild(transforms); // <DigestMethod> reference.AppendChild(GenerateDigestMethod(manager, xDoc)); // <DigestValue> - digest the virtual node list made from these Relationship tags using (Stream s = XmlDigitalSignatureProcessor.GenerateRelationshipNodeStream(GetRelationships(manager, relationshipSelectors))) // serialized node list { reference.AppendChild(GenerateDigestValueNode(xDoc, hashAlgorithm, s, transformName)); } return(reference); }
/// <summary> /// Parse the xml and determine the signing time /// </summary> /// <param name="reader">NodeReader positioned at the SignatureProperties tag</param> /// <param name="signatureId">value of the Id attribute on the Signature tag</param> /// <param name="timeFormat">format found</param> /// <exception cref="XmlException">illegal format</exception> /// <returns>signing time</returns> internal static DateTime ParseSigningTime(XmlReader reader, string signatureId, out String timeFormat) { if (reader == null) { throw new ArgumentNullException("reader"); } bool signatureTimePropertyFound = false; bool signatureTimeIdFound = false; string w3cSignatureNameSpace = SignedXml.XmlDsigNamespaceUrl; // <SignatureProperty> tag string signaturePropertyTag = XTable.Get(XTable.ID.SignaturePropertyTagName); string signaturePropertiesTag = XTable.Get(XTable.ID.SignaturePropertiesTagName); //initializing to a dummy value DateTime signingTime = DateTime.Now; timeFormat = null; while (reader.Read()) { //Looking for <SignatureProperty> tag if (reader.MoveToContent() == XmlNodeType.Element && (String.CompareOrdinal(reader.NamespaceURI, w3cSignatureNameSpace) == 0) && (String.CompareOrdinal(reader.LocalName, signaturePropertyTag) == 0) && reader.Depth == 2) { //Verify Attributes //Look for well-defined Id attribute and if it is present if (VerifyIdAttribute(reader)) { //If we encounter more than one <SignatureProperty> tag with the expected //id, then its an error. if (signatureTimeIdFound) { throw new XmlException(SR.PackageSignatureCorruption); } else { signatureTimeIdFound = true; } //VerifyTargetAttribute will return false, if the Target attribute is missing //or contains an incorrect value. if (VerifyTargetAttribute(reader, signatureId)) { signingTime = ParseSignatureTimeTag(reader, out timeFormat); signatureTimePropertyFound = true; } } } else //Expected <SignatureProperty> tag not found. //Look for end tag corresponding to </SignatureProperty> or //if these are other custom defined properties, then anything with //depth greater than 2 should be ignored as these can be nested elements. if (((String.CompareOrdinal(signaturePropertyTag, reader.LocalName) == 0 && (reader.NodeType == XmlNodeType.EndElement))) || reader.Depth > 2) { continue; } else //If we find the end tag for </SignatureProperties> then we can stop parsing if ((String.CompareOrdinal(signaturePropertiesTag, reader.LocalName) == 0 && (reader.NodeType == XmlNodeType.EndElement))) { break; } else { throw new XmlException(SR.Format(SR.RequiredTagNotFound, signaturePropertyTag)); } } //We did find one or more <SignatureProperty> tags but there were none that //defined the id attribute and target attribute and <SignatureTime> element tag correctly. if (!signatureTimePropertyFound) { throw new XmlException(SR.PackageSignatureCorruption); } return(signingTime); }
/// <summary> /// Parses Transforms tag /// </summary> /// <param name="reader">node to parse</param> /// <param name="partUri">Part Uri for the part owning the relationships</param> /// <param name="relationshipSelectors">allocates and returns a list of /// PackageRelationshipSelectors if Relationship transform</param> /// <returns>ordered list of Transform names</returns> private static List <String> ParseTransformsTag(XmlReader reader, Uri partUri, ref List <PackageRelationshipSelector> relationshipSelectors) { // # reference that signs multiple PackageRelationships // <Reference URI="/shared/_rels/image.jpg.rels?ContentType=image/jpg"> // <Transforms> // <Transform Algorithm="http://schemas.openxmlformats.org/package/2006/RelationshipTransform"> // <RelationshipReference SourceId="1" /> // <RelationshipReference SourceId="2" /> // <RelationshipReference SourceId="8" /> // </Transform> // <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> // </Transforms> // <DigestMethod Algorithm="sha1" /> // <DigestValue>... </DigestValue> // </Reference> // verify lack of attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) != 0) { throw new XmlException(SR.Get(SRID.XmlSignatureParseError)); } List <String> transforms = null; bool relationshipTransformFound = false; int transformsCountWhenRelationshipTransformFound = 0; // Look for transforms. // There are currently only 3 legal transforms which can be arranged in any // combination. while (reader.Read() && (reader.MoveToContent() == XmlNodeType.Element)) { String transformName = null; // at this level, all tags must be Transform tags if (reader.Depth != 4 || String.CompareOrdinal(reader.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) != 0 || String.CompareOrdinal(reader.LocalName, XTable.Get(XTable.ID.TransformTagName)) != 0) { throw new XmlException(SR.Get(SRID.XmlSignatureParseError)); } // inspect the Algorithm attribute to determine the type of transform if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) == 1) { transformName = reader.GetAttribute(XTable.Get(XTable.ID.AlgorithmAttrName)); } // legal transform name? if ((transformName != null) && (transformName.Length > 0)) { // what type of transform? if (String.CompareOrdinal(transformName, XTable.Get(XTable.ID.RelationshipsTransformName)) == 0) { if (!relationshipTransformFound) { // relationship transform ParseRelationshipsTransform(reader, partUri, ref relationshipSelectors); if (transforms == null) { transforms = new List <String>(); } transforms.Add(transformName); relationshipTransformFound = true; transformsCountWhenRelationshipTransformFound = transforms.Count; continue; // success } else { throw new XmlException(SR.Get(SRID.MultipleRelationshipTransformsFound)); } } else { // non-Relationship transform should have no children if (reader.IsEmptyElement) { if (transforms == null) { transforms = new List <String>(); } if (XmlDigitalSignatureProcessor.IsValidXmlCanonicalizationTransform(transformName)) { transforms.Add(transformName); // return it continue; // success } else { throw new InvalidOperationException(SR.Get(SRID.UnsupportedTransformAlgorithm)); } } } } throw new XmlException(SR.Get(SRID.XmlSignatureParseError)); } if (transforms.Count == 0) { throw new XmlException(SR.Get(SRID.XmlSignatureParseError)); } //If we found another transform after the Relationship transform, it will be validated earlier //in this method to make sure that its a supported xml canonicalization algorithm and so we can //simplify this test condition - As per the OPC spec - Relationship transform must be followed //by a canonicalization algorithm. if (relationshipTransformFound && (transforms.Count == transformsCountWhenRelationshipTransformFound)) { throw new XmlException(SR.Get(SRID.RelationshipTransformNotFollowedByCanonicalizationTransform)); } return(transforms); }