XmlDigitalSignatureProcessor.cs source code in C# .NET
Source code for the .NET framework in C#
Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / XmlDigitalSignatureProcessor.cs / 1 / XmlDigitalSignatureProcessor.cs
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// Implementation of the W3C Digital Signature Handler.
// Generates and consumes XmlDSig-compliant digital signatures based on the subset
// specified by the Opc file format.
//
// History:
// 05/13/2002: BruceMac: Initial implementation.
// 05/31/2003: LGolding: Ported to WCP tree.
// 01/25/2004: BruceMac: Ported to address Security Mitigation and API changes for Opc
// 11/10/2005: BruceMac:
// - Rename GetManifest to ParseManifest, rename AssembleManifest
// to GenerateManifest to match pattern of other methods.
// - Tighten up parsing logic to throw in more cases when spec is violated.
// - Require at least a single object in the tag as
// specified by the w3c digsig spec.
//-----------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security; // for SecurityCritical and SecurityTreatAsSafe
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Xml;
using System.IO;
using System.Windows;
using System.IO.Packaging;
using MS.Internal;
namespace MS.Internal.IO.Packaging
{
///
/// Signature Handler implementation that follows the Feb 12, 2002 W3C DigSig Recommendation
///
/// See: http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/ for details
internal class XmlDigitalSignatureProcessor
{
//-----------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
///
/// Constructor - called from PackageDigitalSignatureManager when opening an existing signature
///
/// current DigitalSignatureManager
/// public signature object
/// the part that will/does house the associated XML signature
internal XmlDigitalSignatureProcessor(PackageDigitalSignatureManager manager,
PackagePart signaturePart, PackageDigitalSignature packageSignature) : this(manager, signaturePart)
{
_signature = packageSignature;
}
///
/// Factory method that creates a new PackageDigitalSignature
///
internal static PackageDigitalSignature Sign(
PackageDigitalSignatureManager manager,
PackagePart signaturePart,
IEnumerable parts,
IEnumerable relationshipSelectors,
X509Certificate2 signer,
String signatureId,
bool embedCertificate,
IEnumerable signatureObjects,
IEnumerable objectReferences)
{
// create
XmlDigitalSignatureProcessor p = new XmlDigitalSignatureProcessor(manager, signaturePart);
// and sign
return p.Sign(parts, relationshipSelectors, signer, signatureId,
embedCertificate, signatureObjects, objectReferences);
}
///
/// Verify the given signature
///
/// throws if no certificate found in the signature
/// true if the data stream has not been altered since it was signed
internal bool Verify()
{
return Verify(Signer);
}
///
/// Verify the given signature
///
/// certificate to use (ignores any embedded cert)
/// true if the data stream has not been altered since it was signed
///
/// Critical - 1) Elevate to unrestricted to work around a feature in the .NET XML libraries.
/// - 2) We are calling GenerateDigestValueNode which is SecurityCritical due to the Transform parameter.
/// TreatAsSafe - 1) This is to work around a feature in the Xml layer. The assert makes it possible for the XML
/// layer to perform a transform on the data "under the covers".
/// (http://bugcheck/default.asp?URL=/bugs/SQLBUDefectTracking/392346.asp)
/// 2) The one parameter of concern (Transform) is trusted. The reasoning is that we get the
/// instance from trusted sources. The Transform is obtained from a trusted method
/// (DigitalSignatureProcessor.StringToTranform) that only creates built-in .NET Transform
/// instances which are safe XML Transforms.
///
[SecurityCritical, SecurityTreatAsSafe]
internal bool Verify(X509Certificate2 signer)
{
Invariant.Assert(signer != null);
// Create a SignedXml to do the dirty work
SignedXml xmlSig = EnsureXmlSignatureParsed();
bool result = false;
// Validate the Reference tags in the SignedInfo as per the
// restrictions imposed by the OPC spec
ValidateReferences(xmlSig.SignedInfo.References, true /*allowPackageSpecificReference*/);
(new PermissionSet(PermissionState.Unrestricted)).Assert();
try
{
// verify "standard" XmlSignature portions
result = xmlSig.CheckSignature(signer, true);
}
finally
{
PermissionSet.RevertAssert();
}
if (result)
{
HashAlgorithm hashAlgorithm = null; // optimize - generally only need to create and re-use one of these
String currentHashAlgorithmName = String.Empty; // guaranteed not to match
try
{
try
{
// if that succeeds, verify the Manifest including Part/Relationship content and ContentTypes
ParsePackageDataObject();
}
catch (XmlException)
{
// parsing exception - means this is a bad signature
return false;
}
foreach (PartManifestEntry partEntry in _partEntryManifest)
{
// compare the content
Stream s = null;
// Relationship requires special handling
if (partEntry.IsRelationshipEntry)
{
// This behaves correctely even if the Relationship Part is missing entirely
s = GetRelationshipStream(partEntry);
}
else // Part entry - inspect raw stream
{
// part is guaranteed to exist at this point because we fail early in PackageDigitalSignature.Verify()
Debug.Assert(_manager.Package.PartExists(partEntry.Uri));
// Compare the content type first so we can fail early if it doesn't match
// (faster than hashing the content itself).
// Compare ordinal case-sensitive which is more strict than normal ContentType
// comparision because this is manadated by the OPC specification.
PackagePart part = _manager.Package.GetPart(partEntry.Uri);
if (String.CompareOrdinal(
partEntry.ContentType.OriginalString,
part.ValidatedContentType.OriginalString) != 0)
{
result = false; // content type mismatch
break;
}
s = part.GetStream(FileMode.Open, FileAccess.Read);
}
using (s)
{
// ensure hash algorithm object is available - re-use if possible
if (((hashAlgorithm != null) && (!hashAlgorithm.CanReuseTransform)) ||
String.CompareOrdinal(partEntry.HashAlgorithm, currentHashAlgorithmName) != 0)
{
if (hashAlgorithm != null)
((IDisposable)hashAlgorithm).Dispose();
currentHashAlgorithmName = partEntry.HashAlgorithm;
hashAlgorithm = GetHashAlgorithm(currentHashAlgorithmName);
// not a supported or recognized algorithm?
if (hashAlgorithm == null)
{
// return invalid result instead of throwing exception
result = false;
break;
}
}
// calculate hash
String base64EncodedHashValue = GenerateDigestValue(s, partEntry.Transforms, hashAlgorithm);
// now compare the hash - must be identical
if (String.CompareOrdinal(base64EncodedHashValue, partEntry.HashValue) != 0)
{
result = false; // hash mismatch
break;
}
}
}
}
finally
{
if (hashAlgorithm != null)
((IDisposable)hashAlgorithm).Dispose();
}
}
return result;
}
///
/// Get the list of transforms applied to the given part (works for Relationship parts too)
///
/// part to get transforms for
/// possibly empty, ordered list of transforms applied to the given part (never null)
/// This method only returns transform names. Transform-specific properties can be obtained by parsing the actual
/// signature contents which conform to the W3C XmlDSig spec.
internal List GetPartTransformList(Uri partName)
{
// query the parsed manifest
ParsePackageDataObject();
List transformList = null;
// look through the manifest for the requested part
foreach (PartManifestEntry entry in _partEntryManifest)
{
if (PackUriHelper.ComparePartUri(entry.Uri, partName) == 0)
{
transformList = entry.Transforms;
break;
}
}
// never return null - an empty list is better form
if (transformList == null)
transformList = new List();
return transformList;
}
//------------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
///
/// Content type of signature parts created by this processor
///
internal static ContentType ContentType
{
get
{
return _xmlSignaturePartType;
}
}
///
/// Associated signature part
///
internal PackagePart SignaturePart
{
get
{
return _signaturePart;
}
}
///
/// Obtain the list of signed parts
///
/// Authors identity in handler-proprietary format
/// if signature xml is malformed
internal List PartManifest
{
get
{
ParsePackageDataObject();
return _partManifest;
}
}
///
/// Obtain the author's identity as a byte stream
///
/// Authors identity in handler-proprietary format
/// if signature xml is malformed
internal List RelationshipManifest
{
get
{
ParsePackageDataObject();
return _relationshipManifest;
}
}
///
/// Obtain the author's identity in X509 Certificate form
///
/// Authors identity as a certificate in X509 form, or null if none found
internal X509Certificate2 Signer
{
get
{
// lazy init when loading existing cert - Sign may have assigned this for us
if (_certificate == null)
{
// first look for cert part
if (PackageSignature.GetCertificatePart() != null)
_certificate = PackageSignature.GetCertificatePart().GetCertificate();
else
{
// look in signature
if (_lookForEmbeddedCert)
{
IEnumerator keyInfoClauseEnum = EnsureXmlSignatureParsed().KeyInfo.GetEnumerator(typeof(KeyInfoX509Data));
while (keyInfoClauseEnum.MoveNext())
{
KeyInfoX509Data x509Data = (KeyInfoX509Data)keyInfoClauseEnum.Current;
foreach (X509Certificate2 cert in x509Data.Certificates)
{
// just take the first one for now
_certificate = cert;
break;
}
// just need one for now
if (_certificate != null)
break;
}
// only need to do this once
_lookForEmbeddedCert = false;
}
}
}
return _certificate; // may be null
}
}
///
/// encrypted hash value
///
///
internal byte[] SignatureValue
{
get
{
return EnsureXmlSignatureParsed().SignatureValue;
}
}
///
/// The object that actually creates the signature
/// Note: This API is exposed to the public API surface through the
/// PackageDigitalSignature.Signature property. Through this API it is
/// possible to create Signatures that are not OPC compliant. However,
/// at verify time, we will throw exceptions for non-conforming signatures.
///
/// object of type System.Security.Cryptography.Xml.Signature
internal Signature Signature
{
get
{
return EnsureXmlSignatureParsed().Signature;
}
set
{
UpdatePartFromSignature(value);
}
}
///
/// Get the given signature
///
internal PackageDigitalSignature PackageSignature
{
get
{
return _signature;
}
}
///
/// Time that the signature was created
///
/// Time of signing if available, or DateTime.MinValue if signature was not time-stamped
internal DateTime SigningTime
{
get
{
// lazy init
ParsePackageDataObject();
return _signingTime;
}
}
internal String TimeFormat
{
get
{
// lazy init
ParsePackageDataObject();
return _signingTimeFormat;
}
}
//------------------------------------------------------
//
// Digest Helper Functions
//
//------------------------------------------------------
///
/// Generate digest value tag
///
///
/// name of the single transform to use - may be null
/// hash algorithm to use
///
///
/// Critical - We are calling the TransformXml method which is Critical due to the Transform parameter.
/// TreatAsSafe - It is safe because we are creating only built-in Transform instances.
///
[SecurityCritical, SecurityTreatAsSafe]
internal static String GenerateDigestValue(
Stream s,
String transformName,
HashAlgorithm hashAlgorithm)
{
List transforms = null;
if (transformName != null)
{
transforms = new List(1);
transforms.Add(transformName);
}
return GenerateDigestValue(s, transforms, hashAlgorithm);
}
///
/// Generate digest value tag
///
/// transforms to apply - may be null and list may contain empty strings
/// stream to hash
/// algorithm to use
///
/// Critical - We are calling the TransformXml method which is Critical due to the Transform parameter.
/// TreatAsSafe - It is safe because we are creating only built-in Transform instances.
///
[SecurityCritical, SecurityTreatAsSafe]
internal static String GenerateDigestValue(
Stream s,
List transforms,
HashAlgorithm hashAlgorithm)
{
s.Seek(0, SeekOrigin.Begin);
// We need to be able to dispose streams generated by the
// Transform object but we don't want to dispose the stream
// passed to us so we insert this to block any propagated Dispose() calls.
Stream transformStream = new IgnoreFlushAndCloseStream(s);
List transformStreams = null;
// canonicalize the part content if asked
if (transforms != null)
{
transformStreams = new List(transforms.Count);
transformStreams.Add(transformStream);
foreach (String transformName in transforms)
{
// ignore empty strings at this point (as well as Relationship Transforms) - these are legal
if ((transformName.Length == 0)
|| (String.CompareOrdinal(transformName, XTable.Get(XTable.ID.RelationshipsTransformName)) == 0))
{
continue;
}
// convert the transform names into objects (if defined)
Transform transform = StringToTransform(transformName);
if (transform == null)
{
// throw XmlException so the outer loop knows the signature is invalid
throw new XmlException(SR.Get(SRID.UnsupportedTransformAlgorithm));
}
transformStream = TransformXml(transform, transformStream);
transformStreams.Add(transformStream);
}
}
// hash it and encode to Base64
String hashValueString = System.Convert.ToBase64String(HashStream(hashAlgorithm, transformStream));
// dispose of any generated streams
if (transformStreams != null)
{
foreach (Stream stream in transformStreams)
stream.Close();
}
return hashValueString;
}
///
/// Synthesizes a NodeList of Reference tags to hash
///
///
///
internal static Stream GenerateRelationshipNodeStream(IEnumerable relationships)
{
// create a NodeList containing valid Relationship XML and serialize it to the stream
Stream s = new MemoryStream();
// Wrap in a stream that ignores Flush and Close so the XmlTextWriter
// will not close it.
// use UTF-8 encoding by default
using (XmlTextWriter writer = new XmlTextWriter(new IgnoreFlushAndCloseStream(s),
System.Text.Encoding.UTF8))
{
// start outer Relationships tag
writer.WriteStartElement(XTable.Get(XTable.ID.RelationshipsTagName), PackagingUtilities.RelationshipNamespaceUri);
// generate a valid Relationship tag according to the Opc schema
InternalRelationshipCollection.WriteRelationshipsAsXml(writer, relationships,
true, /* systematically write target mode */
false /* not in streaming production */
);
// end of Relationships tag
writer.WriteEndElement();
}
return s;
}
///
/// Convert algorithm name to object
///
/// fully specified name
/// HashAlgorithm object or null if it does not map to a supported hash type
/// Caller is responsible for calling Dispose() on returned object
internal static HashAlgorithm GetHashAlgorithm(String hashAlgorithmName)
{
Object o = CryptoConfig.CreateFromName(hashAlgorithmName);
HashAlgorithm algorithm = o as HashAlgorithm;
// In the case that we get a valid crypto object that is not a hash algorithm
// we should attempt to dispose it if it offers IDisposable.
if (algorithm == null && o != null)
{
IDisposable disposable = o as IDisposable;
if (disposable != null)
disposable.Dispose();
}
return algorithm;
}
///
/// Critical - We are marking this function critical since we want to know the types
/// of Transform instances that are being created. If new types of transforms
/// are created in this method other than build-in .NET ones, then we should
/// probably know about it.
/// TreatAsSafe - It is safe because we are creating only built-in Transform instances.
///
///
/// IMPORTANT NOTE:
/// 1. In the XmlDigitalSignatureProcessor.IsValidXmlCanonicalizationTransform method,
/// we have similar logic regarding these two transforms.So both these methods must be updated
/// in [....].
///
[SecurityCritical, SecurityTreatAsSafe]
private static Transform StringToTransform(String transformName)
{
Invariant.Assert(transformName != null);
if (String.CompareOrdinal(transformName, SignedXml.XmlDsigC14NTransformUrl) == 0)
{
return new XmlDsigC14NTransform();
}
else if (String.CompareOrdinal(transformName, SignedXml.XmlDsigC14NWithCommentsTransformUrl) == 0)
{
return new XmlDsigC14NWithCommentsTransform();
}
else
return null;
}
// As per the OPC spec, only two tranforms are valid. Also, both of these happen to be
// XML canonicalization transforms.
// In the XmlSignatureManifest.ParseTransformsTag method we make use this method to
// validate the transforms to make sure that they are supported by the OPC spec and
// we also take advantage of the fact that both of them are XML canonicalization transforms
// IMPORTANT NOTE:
// 1. In the XmlDigitalSignatureProcessor.StringToTransform method, we have similar logic
// regarding these two transforms.So both these methods must be updated in [....].
// 2. If ever this method is updated to add other transforms, careful review must be done to
// make sure that methods calling this method are updated as required.
internal static bool IsValidXmlCanonicalizationTransform(String transformName)
{
Invariant.Assert(transformName != null);
if (String.CompareOrdinal(transformName, SignedXml.XmlDsigC14NTransformUrl) == 0 ||
String.CompareOrdinal(transformName, SignedXml.XmlDsigC14NWithCommentsTransformUrl) == 0)
{
return true;
}
else
return false;
}
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
///
/// Helper class
///
private SignedXml EnsureXmlSignatureParsed()
{
// lazy init
if (_signedXml == null)
{
_signedXml = new CustomSignedXml();
// Load the XML
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
using (Stream s = SignaturePart.GetStream())
{
using (XmlTextReader xmlReader = new XmlTextReader(s))
{
//Prohibit DTD from the markup as per the OPC spec
xmlReader.ProhibitDtd = true;
//This method expects the reader to be in ReadState.Initial.
//It will make the first read call.
PackagingUtilities.PerformInitailReadAndVerifyEncoding(xmlReader);
//If the reader.ReadState is ReadState.Initial, then XmlDocument with perform the
//first xmlReader.Read() and start loading from that node/tag.
//If the reader.ReadState is ReadState.Intermediate, then XmlDocument, will start
//loading from that location itself.
//Note: Since in the above method we perform only the first read and will have not
//moved the reader state further down in the markup, we should be okay, and
//xmlDocument.Load will load from the very begining as intended.
xmlDocument.Load(xmlReader);
// W3C spec allows for Signature tag to appear as an island and inherently allows
// for multiple Signature tags within the same XML document.
// OPC restricts this to a single, root-level Signature tag. However, Signature
// tags are allowed to exist within the non-OPC Object tags within an OPC signature.
// This is common for XAdES signatures and must be explicitly allowed.
XmlNodeList nodeList = xmlDocument.ChildNodes;
if (nodeList == null || nodeList.Count != 2)
throw new XmlException(SR.Get(SRID.PackageSignatureCorruption));
// First node must be the XmlDeclaration
if (nodeList[0].NodeType != XmlNodeType.XmlDeclaration)
throw new XmlException(SR.Get(SRID.PackageSignatureCorruption));
// Second node must be in the w3c namespace, and must be the tag
XmlNode node = nodeList[1];
if ((node.NodeType != XmlNodeType.Element) ||
(String.CompareOrdinal(node.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) != 0) ||
(String.CompareOrdinal(node.LocalName, XTable.Get(XTable.ID.SignatureTagName)) != 0))
{
throw new XmlException(SR.Get(SRID.PackageSignatureCorruption));
}
// instantiate the SignedXml from the xmlDoc
_signedXml.LoadXml((XmlElement)node);
}
}
}
// As per the OPC spec, only two Canonicalization methods can be specified
if (!IsValidXmlCanonicalizationTransform(_signedXml.SignedInfo.CanonicalizationMethod))
throw new XmlException(SR.Get(SRID.UnsupportedCanonicalizationMethod));
return _signedXml;
}
//-----------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
///
/// Constructor - called from public constructor as well as static Sign() method
///
/// current DigitalSignatureManager
/// the part that will/does house the associated XML signature
private XmlDigitalSignatureProcessor(PackageDigitalSignatureManager manager,
PackagePart signaturePart)
{
Invariant.Assert(manager != null);
Invariant.Assert(signaturePart != null);
_signaturePart = signaturePart;
_manager = manager;
_lookForEmbeddedCert = true;
}
///
/// Create a new PackageDigitalSignature
///
/// the data being protected by this signature
/// possibly null collection of relationshipSelectors that represent the
/// relationships that are to be signed
/// Identity of the author
/// Id attribute of the new Xml Signature
/// true if caller wants certificate embedded in the signature itself
/// references
/// objects to sign
///
/// Critical - Elevating for unrestricted permissions to call into .NET XML code. This is due to a feature in
/// the CLR code (http://bugcheck/default.asp?URL=/bugs/SQLBUDefectTracking/392346.asp).
/// TreatAsSafe - The elevation is causing a transform of data at the CLR level. The transforms being used
/// are built in .NET XML transforms. Since we using built in .NET transforms the transform on
/// the XML data is not a security threat. The only data we supply is data from the package.
///
[SecurityCritical, SecurityTreatAsSafe]
private PackageDigitalSignature Sign(
IEnumerable parts,
IEnumerable relationshipSelectors,
X509Certificate2 signer,
String signatureId,
bool embedCertificate,
IEnumerable signatureObjects,
IEnumerable objectReferences)
{
// don't overwrite
Debug.Assert(SignaturePart.GetStream().Length == 0, "Logic Error: Can't sign when signature already exists");
// grab hash algorithm as this may change in the future
_hashAlgorithmName = _manager.HashAlgorithm;
// keep the signer if indicated
if (_manager.CertificateOption == CertificateEmbeddingOption.NotEmbedded)
_lookForEmbeddedCert = false; // don't bother parsing
else
_certificate = signer; // save some parsing
// we only release this key if we obtain it
AsymmetricAlgorithm key = null;
bool ownKey = false;
if (signer.HasPrivateKey)
{
key = signer.PrivateKey;
}
else
{
ownKey = true;
key = GetPrivateKeyForSigning(signer);
}
try
{
_signedXml = new CustomSignedXml();
_signedXml.SigningKey = key;
_signedXml.Signature.Id = signatureId;
// put it in the XML
if (embedCertificate)
{
_signedXml.KeyInfo = GenerateKeyInfo(key, signer);
}
// Package object tag
// convert from string to class and ensure we dispose
using (HashAlgorithm hashAlgorithm = GetHashAlgorithm(_hashAlgorithmName))
{
// inform caller if hash algorithm is unknown
if (hashAlgorithm == null)
throw new InvalidOperationException(SR.Get(SRID.UnsupportedHashAlgorithm));
_signedXml.AddObject(GenerateObjectTag(hashAlgorithm, parts, relationshipSelectors, signatureId));
}
// add reference from SignedInfo to Package object tag
Reference objectReference = new Reference(XTable.Get(XTable.ID.OpcLinkAttrValue));
objectReference.Type = XTable.Get(XTable.ID.W3CSignatureNamespaceRoot) + "Object";
objectReference.DigestMethod = _hashAlgorithmName;
_signedXml.AddReference(objectReference);
// add any custom object tags
AddCustomObjectTags(signatureObjects, objectReferences);
// compute the signature
SignedXml xmlSig = _signedXml;
(new PermissionSet(PermissionState.Unrestricted)).Assert();
try
{
xmlSig.ComputeSignature();
}
finally
{
PermissionSet.RevertAssert();
}
// persist
UpdatePartFromSignature(_signedXml.Signature);
}
finally
{
if (key != null && ownKey)
((IDisposable)key).Dispose();
}
// create the PackageDigitalSignature object
_signature = new PackageDigitalSignature(_manager, this);
return _signature;
}
///
/// Assembles the sorted list of relationships for this part entry and
/// generates a stream with the Xml-equivalent as defined in the Opc spec
///
/// relationship-type part entry
///
private Stream GetRelationshipStream(PartManifestEntry partEntry)
{
Debug.Assert(partEntry.IsRelationshipEntry);
//Get the list of relationships from the RelationshipSelectors for this part
SortedDictionary partRelationships =
new SortedDictionary(StringComparer.Ordinal);
foreach (PackageRelationshipSelector relationshipSelector in partEntry.RelationshipSelectors)
{
foreach (PackageRelationship r in relationshipSelector.Select(_manager.Package))
{
if(!partRelationships.ContainsKey(r.Id))
partRelationships.Add(r.Id, r);
}
}
return GenerateRelationshipNodeStream(partRelationships.Values);
}
private void AddCustomObjectTags(IEnumerable signatureObjects,
IEnumerable objectReferences)
{
Invariant.Assert(_signedXml != null);
// add any references
if (objectReferences != null)
{
// Validate the Reference tags in the SignedInfo as per the
// restrictions imposed by the OPC spec
ValidateReferences(objectReferences, false /*allowPackageSpecificReference*/);
foreach (Reference reference in objectReferences)
{
// consistent hash algorithm for entire signature
reference.DigestMethod = _hashAlgorithmName;
_signedXml.AddReference(reference);
}
}
// any object tags
if (signatureObjects != null)
{
// thes have been pre-screened for matches against reserved OpcAttrValue and duplicates
foreach (DataObject obj in signatureObjects)
{
_signedXml.AddObject(obj);
}
}
}
private void UpdatePartFromSignature(Signature sig)
{
// write to stream
using (Stream s = SignaturePart.GetStream(FileMode.Create, FileAccess.Write))
{
using (XmlTextWriter xWriter = new XmlTextWriter(s, System.Text.Encoding.UTF8))
{
xWriter.WriteStartDocument(true);
sig.GetXml().WriteTo(xWriter);
xWriter.WriteEndDocument();
}
}
_signedXml = null; // force a re-parse
}
private static byte[] HashStream(HashAlgorithm hashAlgorithm, Stream s)
{
s.Seek(0, SeekOrigin.Begin);
// reset algorithm
hashAlgorithm.Initialize();
return hashAlgorithm.ComputeHash(s);
}
///
/// Critical - Elevates for FULL unrestricted permissions due to a feature in the XmlDocument class.
/// The XmlDocument class demands for unrestricted permissions when setting the XmlResolver.
/// This permission is overboard but we are really only transforming the stream from one form
/// to another via a supplied Transform instance. Callers should ensure the Transform is
/// trusted.
/// NOTE: This elevation is due to the feature in the CLR XML code that demands for "full trust".
/// (http://bugcheck/default.asp?URL=/bugs/SQLBUDefectTracking/392346.asp)
///
[SecurityCritical]
private static Stream TransformXml(Transform xForm, Object source)
{
(new PermissionSet(PermissionState.Unrestricted)).Assert(); // Blessed
try
{
// transform
xForm.LoadInput(source);
}
finally
{
PermissionSet.RevertAssert();
}
return (Stream)xForm.GetOutput();
}
///
/// Full parse of the Package-specific Object tag
///
/// Side effect of updating _signingTime, _signingTimeFormat,
/// _partManifest, _partEntryManifest and _relationshipManifest
/// throws if markup does not match OPC spec
private void ParsePackageDataObject()
{
if (!_dataObjectParsed)
{
EnsureXmlSignatureParsed();
// find the package-specific Object tag
XmlNodeList nodeList = GetPackageDataObject().Data;
// The legal parent is a "Package" Object tag with 2 children
// and
if (nodeList.Count != 2)
throw new XmlException(SR.Get(SRID.XmlSignatureParseError));
// get a NodeReader that allows us to easily and correctly skip comments
XmlReader reader = new XmlNodeReader(nodeList[0].ParentNode);
// parse the