PackageDigitalSignatureManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / System / IO / Packaging / PackageDigitalSignatureManager.cs / 1305600 / PackageDigitalSignatureManager.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This class provides api's to add/remove/verify signatures on an MMCF container. 
//
// History: 
//  03/22/2004: BruceMac: Initial Implementation
//
//-----------------------------------------------------------------------------
 
// Allow use of presharp warning numbers [6506] unknown to the compiler
#pragma warning disable 1634, 1691 
 
using System;
using System.Collections.Generic; 
using System.Windows;                                   // For Exception strings - SRID
using System.Text;                                      // for StringBuilder
using System.Diagnostics;                               // for Assert
using System.Security;                                  // for SecurityCritical tag 
using System.Security.Permissions;                      // for LinkDemand
using System.Security.Cryptography.Xml;                 // for SignedXml 
using System.Security.Cryptography.X509Certificates;    // for X509Certificate 
using MS.Internal.IO.Packaging;                         // for internal helpers
using System.Collections.ObjectModel;                   // for ReadOnlyCollection<> 
using MS.Internal;                                      // for ContentType
using MS.Internal.WindowsBase;

namespace System.IO.Packaging 
{
    ///  
    /// Options for storing the signing Certificate 
    /// 
    public enum CertificateEmbeddingOption : int 
    {
        /// 
        /// Embed certificate in its own PackagePart (or share if same cert already exists)
        ///  
        InCertificatePart = 0,      // embed the certificate in its own, possibly-shared part
        ///  
        /// Embed certificate within the signature PackagePart 
        /// 
        InSignaturePart = 1,        // embed the certificate within the signature 
        /// 
        /// Do not embed
        /// 
        NotEmbedded = 2,            // do not embed the certificate at all 
    }
 
    ///  
    /// Type of the handler that is invoked if signature validation is non-success.
    ///  
    /// signature
    /// event arguments - containing the result
    /// true to continue verifying other signatures, false to abandon effort
    public delegate void InvalidSignatureEventHandler(object sender, SignatureVerificationEventArgs e); 

    ///  
    /// Signature Verification Event Args - information about a verification event 
    /// 
    public class SignatureVerificationEventArgs : EventArgs 
    {
        //-----------------------------------------------------
        //
        //  Public Members 
        //
        //----------------------------------------------------- 
        ///  
        /// Signature being processed
        ///  
        public PackageDigitalSignature Signature
        {
            get
            { 
                return _signature;
            } 
        } 

        ///  
        /// Result of Verification
        /// 
        public VerifyResult VerifyResult
        { 
            get
            { 
                return _result; 
            }
        } 

        //------------------------------------------------------
        //
        //  Internal Members 
        //
        //----------------------------------------------------- 
        internal SignatureVerificationEventArgs(PackageDigitalSignature signature, 
            VerifyResult result)
        { 
            // verify arguments
            if (signature == null)
                throw new ArgumentNullException("signature");
 
            if (result < VerifyResult.Success || result > VerifyResult.NotSigned)
                throw new System.ArgumentOutOfRangeException("result"); 
 
            _signature = signature;
            _result = result; 
        }

        //------------------------------------------------------
        // 
        //  Private Members
        // 
        //------------------------------------------------------ 
        private PackageDigitalSignature                 _signature;
        private VerifyResult                            _result; 
    }

    /// 
    /// PackageDigitalSignatureManager 
    /// 
    public sealed class PackageDigitalSignatureManager 
    { 
        #region Public Members
        //----------------------------------------------------- 
        //
        //  Public Events
        //
        //------------------------------------------------------ 
        /// 
        /// Event to subscribe to for signature validation activities 
        ///  
        public event InvalidSignatureEventHandler InvalidSignatureEvent;
 
        //-----------------------------------------------------
        //
        //  Public Properties
        // 
        //-----------------------------------------------------
        ///  
        /// Does this container hold digital signatures? 
        /// 
        /// true if signatures exist 
        /// this does not evaluate the signatures - they may be invalid even if this returns true
        public bool IsSigned
        {
            get 
            {
                EnsureSignatures(); 
                return (_signatures.Count > 0); 
            }
        } 

        /// 
        /// Signatures in container
        ///  
        /// read only list of immutable signatures found in the container
        public ReadOnlyCollection Signatures 
        { 
            get
            { 
                // ensure signatures are loaded from origin
                EnsureSignatures();

                // Return a read-only collection referring to them. 
                // This list will be automatically updated when the underlying collection is changed.
                if (_signatureList == null) 
                    _signatureList = new ReadOnlyCollection(_signatures); 

                return _signatureList; 
            }
        }

        ///  
        /// ContentType - Transform mapping dictionary
        ///  
        /// Dictionary of transform Uri's indexed by ContentType. 
        /// Contains a single transform to be applied
        /// before hashing any Part encountered with that ContentType 
        public Dictionary TransformMapping
        {
            get
            { 
                return _transformDictionary;
            } 
        } 

        ///  
        /// Handle of parent window to use when displaying certificate selection dialog
        /// 
        /// 
        /// not necessary if certificates are provided in calls to sign 
        public IntPtr ParentWindow
        { 
            get 
            {
                return _parentWindow; 
            }
            set
            {
                _parentWindow = value; 
            }
        } 
 
        /// 
        /// Hashalgorithm to use when creating/verifying signatures 
        /// 
        /// 
        /// defaults to SHA1
        public String HashAlgorithm 
        {
            get 
            { 
                return _hashAlgorithmString;
            } 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value"); 

                if (value == String.Empty) 
                    throw new ArgumentException(SR.Get(SRID.UnsupportedHashAlgorithm), "value"); 

                _hashAlgorithmString = value; 
            }
        }

        ///  
        /// How to embed certificates when Signing
        ///  
        ///  
        public CertificateEmbeddingOption CertificateOption
        { 
            get
            {
                return _certificateEmbeddingOption;
            } 
            set
            { 
                if ((value < CertificateEmbeddingOption.InCertificatePart) || (value > CertificateEmbeddingOption.NotEmbedded)) 
                    throw new ArgumentOutOfRangeException("value");
 
                _certificateEmbeddingOption = value;
            }
        }
 
        /// 
        /// How to format the SignatureTime in new signatures 
        ///  
        /// Legal formats specified in Opc book and reproduced here:
        /// YYYY-MM-DDThh:mm:ss.sTZD 
        /// YYYY-MM-DDThh:mm:ssTZD
        /// YYYY-MM-DDThh:mmTZD
        /// YYYY-MM-DD
        /// YYYY-MM 
        /// YYYY
        /// 
        /// where: 
        /// Y = year, M = month integer (leading zero), D = day integer (leading zero),
        /// hh = 24hr clock hour 
        /// mm = minutes (leading zero)
        /// ss = seconds (leading zero)
        /// .s = tenths of a second
        ///  
        public String TimeFormat
        { 
            get 
            {
                return _signatureTimeFormat; 
            }
            set
            {
                if (value == null) 
                    throw new ArgumentNullException("value");
 
                if (XmlSignatureProperties.LegalFormat(value)) 
                    _signatureTimeFormat = value;
                else 
                    throw new FormatException(SR.Get(SRID.BadSignatureTimeFormatString));
            }
        }
 
        //-----------------------------------------------------
        // 
        //  Public Fields 
        //
        //------------------------------------------------------ 
        /// 
        /// Name of signature origin part
        /// 
        ///  
        /// This value may vary by Package because the name is not formally defined. While this
        /// implementation will generally use the same default value, the value returned by this property will reflect 
        /// whatever origin is already present in the current Package (if any) which may vary between implementations. 
        /// 
        public Uri SignatureOrigin 
        {
            get
            {
                OriginPartExists();           // force search for OriginPart in case it is different from default 
                return _originPartName;
            } 
        } 

        ///  
        /// Type of default signature origin relationship
        /// 
        /// 
        static public String SignatureOriginRelationshipType 
        {
            get 
            { 
                return _originRelationshipType;
            } 
        }

        /// 
        /// Default hash algorithm 
        /// 
        ///  
        static public String DefaultHashAlgorithm 
        {
            get 
            {
                return _defaultHashAlgorithm;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Public Methods
        // 
        //------------------------------------------------------
        /// 
        /// Create a new PackageDigitalSignature manager
        ///  
        /// container to work with
        /// based on the default origin 
        /// package is null 
        public PackageDigitalSignatureManager(Package package)
        { 
            if (package == null)
                throw new ArgumentNullException("package");

            _parentWindow = IntPtr.Zero; 
            _container = package;
 
            // initialize the transform dictionary with defaults 
            _transformDictionary = new Dictionary(4);
            _transformDictionary[PackagingUtilities.RelationshipPartContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl;    // relationship parts 
            _transformDictionary[XmlDigitalSignatureProcessor.ContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl;          // xml signature
        }

        #region Sign 
        /// 
        /// Sign - prompts for certificate and embeds it 
        ///  
        /// list of parts to sign
        /// Set ParentWindow before this call if you want to make the certificate 
        /// selection dialog modal to a particular window.  Does not prompt for certificates if none could be located in the default certificate store.
        /// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
        public PackageDigitalSignature Sign(IEnumerable parts)
        { 
            X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
            if (certificate == null) 
                return null; 
            else
                return Sign(parts, certificate); 
        }

        /// 
        /// Sign - certificate provided by caller 
        /// 
        /// list of parts to sign 
        /// signer's certificate 
        public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate)
        { 
            // create unique signature name
            return Sign(parts, certificate, null);
        }
 
        /// 
        /// Sign - certificate provided by caller 
        ///  
        /// list of parts to sign - may be empty or null
        /// signer's certificate 
        /// relationshipSelectors that hold information about
        /// the relationships to be signed - may be empty or null
        /// one of parts or relationships must be non-null and contain at least a single entry
        public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate, IEnumerable relationshipSelectors) 
        {
            // use default signature Id 
            return Sign(parts, certificate, relationshipSelectors, XTable.Get(XTable.ID.OpcSignatureAttrValue)); 
        }
 
                /// 
        /// Sign - certificate provided by caller
        /// 
        /// list of parts to sign - may be empty or null 
        /// signer's certificate
        /// relationshipSelectors that hold information about 
        /// the relationships to be signed - may be empty or null 
        /// id for the new Signature - may be empty or null
        /// one of parts or relationships must be non-null and contain at least a single entry 
        public PackageDigitalSignature Sign(
            IEnumerable parts,
            X509Certificate certificate,
            IEnumerable relationshipSelectors, 
            String signatureId)
        { 
            // Cannot both be null - need to check here because the similar check in the super-overload cannot 
            // distinguish to this level.
            if (parts == null && relationshipSelectors == null) 
            {
                throw new ArgumentException(SR.Get(SRID.NothingToSign));
            }
 
            return Sign(parts, certificate, relationshipSelectors, signatureId, null, null);
        } 
 
        /// 
        /// Sign - caller specifies custom "Object" and/or SignedInfo "Reference" tags 
        /// 
        /// list of parts to sign - may be empty or null
        /// signer's certificate
        /// relationshipSelectors that hold information about 
        /// the relationships to be signed - may be empty or null
        /// id for the new Signature - may be empty or null 
        /// references to custom object tags.  The DigestMethod on each 
        /// Reference will be ignored.  The signature will use the globally defined HashAlgorithm
        /// obtained from the current value of the HashAlgorithm property. 
        /// objects (signed or not)
        /// Thrown if any TransformMapping
        /// defines an empty or null transform for the ContentType of any Part being signed or if an unknown
        /// transform is encountered. 
        /// Thrown if signatureId is non-null and violates the
        /// Xml Id schema (essentially - no leading digit is allowed). 
        /// One of parts, relationships, signatureObjects and objectReferences must be 
        /// non-null and contain at least a single entry.
        /// This and every other Sign overload makes use of the current state of the TransformMapping 
        /// dictionary which defines a Transform to apply based on ContentType.  The Opc specification
        /// only currently allows for two legal Transform algorithms: C14 and C14N.
        /// Note that the w3c Xml Signature standard does not allow for empty Manifest tags.
        /// Because the Opc specification requires the existence of a Package-specific Object 
        /// tag and further specifies that this Object tag contain a Manifest and SignatureProperties
        /// tags, it follows that this Manifest tag must include at least one Reference tag. 
        /// This means that every signature include at least one of a Part to sign (non-empty parts tag) 
        /// or a Relationship to sign (non-empty relationshipSelectors) even if such a signature
        /// is only destined to sign signatureObjects and/or objectReferences. 
        /// This overload provides support for generation of Xml signatures that require custom
        /// Object tags.  For any provided Object tag to be signed, a corresponding Reference
        /// tag must be provided with a Uri that targets the Object tag using local fragment
        /// syntax.  If the object had an ID of "myObject" the Uri on the Reference would 
        /// be "#myObject".  For unsigned objects, no reference is required.
        /// 
        ///     Critical: calls X509Certificate.Handle which LinkDemands 
        ///     PublicOK: we don't store or return the handle
        /// 
        [SecurityCritical]
        public PackageDigitalSignature Sign(
            IEnumerable parts,
            X509Certificate certificate, 
            IEnumerable relationshipSelectors,
            String signatureId, 
            IEnumerable signatureObjects, 
            IEnumerable objectReferences)
        { 
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotSignReadOnlyFile));

            VerifySignArguments(parts, certificate, relationshipSelectors, signatureId, signatureObjects, objectReferences); 

            // substitute default id if none given 
            if ((signatureId == null) || (signatureId == String.Empty)) 
            {
                signatureId = "packageSignature";   // default 
            }

            // Make sure the list reflects what's in the package.
            // Do this before adding the new signature part because we don't want it included until it 
            // is fully formed (and delaying the add saves us having to remove it in case there is an
            // error during the Sign call). 
            EnsureSignatures(); 

            Uri newSignaturePartName = GenerateSignaturePartName(); 
            if (_container.PartExists(newSignaturePartName))
                throw new ArgumentException(SR.Get(SRID.DuplicateSignature));

            // Pre-create origin part if it does not already exist. 
            // Do this before signing to allow for signing the package relationship part (because a Relationship
            // is added from the Package to the Origin part by this call) and the Origin Relationship part in case this is 
            // a Publishing signature and the caller wants the addition of more signatures to break this signature. 
            PackageRelationship relationshipToNewSignature = OriginPart.CreateRelationship(newSignaturePartName, TargetMode.Internal,
                    _originToSignatureRelationshipType); 
            _container.Flush();     // ensure the origin relationship part is persisted so that any signature will include this newest relationship

            VerifyPartsExist(parts);
 
            // sign the data and optionally embed the certificate
            bool embedCertificateInSignaturePart = (_certificateEmbeddingOption == CertificateEmbeddingOption.InSignaturePart); 
 
            // convert cert to version2 - more functionality
            X509Certificate2 exSigner = certificate as X509Certificate2; 
            if (exSigner == null)
                exSigner = new X509Certificate2(certificate.Handle);

            //PRESHARP: Parameter to this public method must be validated:  A null-dereference can occur here. 
            //      Parameter 'exSigner' to this public method must be validated:  A null-dereference can occur here.
            //This is a false positive as the checks above can gurantee no null dereference will occur 
#pragma warning disable 6506 

            PackageDigitalSignature signature = null; 
            PackagePart newSignaturePart = null;
            try
            {
                // create the new part 
                newSignaturePart = _container.CreatePart(newSignaturePartName, XmlDigitalSignatureProcessor.ContentType.ToString());
 
                // do the actual signing - only Xml signatures currently supported 
                signature = XmlDigitalSignatureProcessor.Sign(this, newSignaturePart, parts, relationshipSelectors, exSigner, signatureId, embedCertificateInSignaturePart,
                    signatureObjects, objectReferences); 
            }
            catch (InvalidOperationException)
            {
                // bad hash algorithm - revert changes 
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were 
                // existing before this sign method was called. So we want to leave those 
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part. 
                InternalRemoveSignature(newSignaturePartName, _signatures.Count);
                _container.Flush();    // actually persist the revert
                throw;
            } 
            catch (System.IO.IOException)
            { 
                // failure to open part - revert changes 
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were 
                // existing before this sign method was called. So we want to leave those
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part.
                InternalRemoveSignature(newSignaturePartName, _signatures.Count); 
                _container.Flush();    // actually persist the revert
                throw; 
            } 
            catch (System.Security.Cryptography.CryptographicException)
            { 
                // failure to sign - revert changes
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were
                // existing before this sign method was called. So we want to leave those 
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part. 
                InternalRemoveSignature(newSignaturePartName, _signatures.Count); 
                _container.Flush();    // actually persist the revert
                throw; 
            }

            // add to the list
            _signatures.Add(signature); 

            // embed certificate if called for 
            if (_certificateEmbeddingOption == CertificateEmbeddingOption.InCertificatePart) 
            {
                // create the cert part 
                // auto-generate a certificate name - will be the same for the same certificate
                Uri certificatePartName = PackUriHelper.CreatePartUri(new Uri(
                    CertificatePart.PartNamePrefix + exSigner.SerialNumber + CertificatePart.PartNameExtension, UriKind.Relative));
 
                // create the serialization helper class (side-effect of creating or opening the part)
                CertificatePart certPart = new CertificatePart(_container, certificatePartName); 
                certPart.SetCertificate(exSigner); 

                // establish a relationship 
                newSignaturePart.CreateRelationship(certificatePartName, TargetMode.Internal, CertificatePart.RelationshipType);
                signature.SetCertificatePart(certPart);
            }
#pragma warning restore 6506 

            _container.Flush(); 
 
            // return to caller in case they need it
            return signature; 
        }
        #endregion

        #region CounterSign 
        /// 
        /// CounterSign - prompts for certificate and embeds it based on current CertificateEmbeddingOption 
        ///  
        /// Set ParentWindow before this call if you want to make the certificate
        /// selection dialog modal to a particular window.  Does not present the dialog if no suitable certificate 
        /// could be found in the default certificate store.
        /// Signs all existing signature parts so that any change to these part(s) will invalidate the
        /// returned signature.
        /// Cannot CounterSign an unsigned package. 
        /// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
        public PackageDigitalSignature Countersign() 
        { 
            // Counter-sign makes no sense if we are not already signed
            // Check before asking for certificate 
            if (!IsSigned)
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));

            // prompt for certificate 
            X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
            if (certificate == null) 
                return null; 
            else
                return Countersign(certificate); 
        }

        /// 
        /// CounterSign - certificate provided 
        /// 
        /// signer's certificate 
        /// Cannot CounterSign an unsigned package. 
        /// certificate must be non-null.
        /// Signs all existing signature parts so that any change to these part(s) will invalidate the 
        /// returned signature.
        public PackageDigitalSignature Countersign(X509Certificate certificate)
        {
            if (certificate == null) 
                throw new ArgumentNullException("certificate");
 
            // Counter-sign makes no sense if we are not already signed 
            // Check before asking for certificate
            if (!IsSigned) 
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));

            // sign all existing signatures
            List signatures = new List(_signatures.Count); 
            for (int i = 0; i < _signatures.Count; i++)
            { 
                signatures.Add(_signatures[i].SignaturePart.Uri); 
            }
 
            // sign
            return Sign(signatures, certificate);
        }
 
        /// 
        /// CounterSign - signature part name(s) specified by caller 
        ///  
        /// signer's certificate
        /// signature parts to sign 
        /// Signs the given signature parts so that any change to these part(s) will invalidate the
        /// returned signature.
        /// Cannot CounterSign an unsigned package.
        /// signatures must be non-empty and cannot refer to parts other than signature parts. 
        /// Both arguments must be non-null.
        public PackageDigitalSignature Countersign(X509Certificate certificate, IEnumerable signatures) 
        { 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 

            if (signatures == null)
                throw new ArgumentNullException("signatures");
 
            // Counter-sign makes no sense if we are not already signed
            if (!IsSigned) 
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer)); 

            // Restrict signatures to be actual signature part references 
            foreach (Uri uri in signatures)
            {
                PackagePart part = _container.GetPart(uri);
                if (!part.ValidatedContentType.AreTypeAndSubTypeEqual(XmlDigitalSignatureProcessor.ContentType)) 
                    throw new ArgumentException(SR.Get(SRID.CanOnlyCounterSignSignatureParts, signatures));
            } 
 
            return Sign(signatures, certificate);
        } 
        #endregion

        /// 
        /// verify all signatures - calls verify on each signature 
        /// 
        /// true to exit on first failure - false to continue 
        /// register for invalid signature events 
        public VerifyResult VerifySignatures(bool exitOnFailure)
        { 
            VerifyResult result;
            EnsureSignatures();

            // signed? 
            if (_signatures.Count == 0)
                result = VerifyResult.NotSigned; 
            else 
            {
                // contract is to return a failure value, even if there are subsequent successes 
                // defaulting to success here simplifies the logic for this
                result = VerifyResult.Success;     // default
                for (int i = 0; i < _signatures.Count; i++)
                { 
                    VerifyResult temp = _signatures[i].Verify();
                    if (temp != VerifyResult.Success) 
                    { 
                        result = temp;  // note failure
 
                        if (InvalidSignatureEvent != null)
                            InvalidSignatureEvent(this, new SignatureVerificationEventArgs(_signatures[i], temp));

                        if (exitOnFailure) 
                            break;
                    } 
                } 
            }
 
            return result;
        }

        ///  
        /// Remove a signature
        ///  
        /// signature to remove 
        /// Caller should call Package.Flush() in order to persist changes.
        public void RemoveSignature(Uri signatureUri) 
        {
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile));
 
            if (signatureUri == null)
                throw new ArgumentNullException("signatureUri"); 
 
            // empty?
            if (!IsSigned)      // calls EnsureSignatures for us 
                return;

            // find the signature
            int index = GetSignatureIndex(signatureUri); 
            if (index < 0)
                return; 
 
            try
            { 
                Debug.Assert(index < _signatures.Count);

                //After this signature is removed the total number of signatures remaining will
                //be _signatures.Count - 1. If this count is zero, then additional clean up needs 
                //to be done, like removing the Origin part.
                InternalRemoveSignature(signatureUri, _signatures.Count - 1 /*since we are deleting one*/); 
 
                // invalidate the signature itself
                _signatures[index].Invalidate(); 
            }
            finally
            {
                _signatures.RemoveAt(index);    // ensure it is actually removed from the list 
            }
        } 
 
        /// 
        /// Remove all signatures based on this origin 
        /// 
        /// also removes all certificate parts and the signature origin.  Caller must call Flush() to persist changes.
        public void RemoveAllSignatures()
        { 
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile)); 
 
            EnsureSignatures();
 
            try
            {
                // Remove via known traversal - required to find all signatures (we may not know all signature content-types).
                for (int i = 0; i < _signatures.Count; i++) 
                {
                    PackagePart p = _signatures[i].SignaturePart; 
 
                    // Delete any Certificate part(s) targeted by this signature.  We know that all of the
                    // reference counts will reach zero because we are removing all signatures. 
                    foreach (PackageRelationship r in p.GetRelationshipsByType(CertificatePart.RelationshipType))
                    {
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            continue;   // fail silently
 
                        _container.DeletePart(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri));                 // will not throw if part not found 
                    }
 
                    // delete signature part
                    _container.DeletePart(p.Uri);

                    // invalidate the signature itself 
                    _signatures[i].Invalidate();
                } 
 
                DeleteOriginPart();
            } 
            finally
            {
                // update internal variables
                _signatures.Clear(); 
            }
        } 
 
        /// 
        /// Obtain the PackageDigitalSignature referred to by the given Uri 
        /// 
        /// ID obtained from a PackageDigitalSignature object
        /// null if signature not found
        public PackageDigitalSignature GetSignature(Uri signatureUri) 
        {
            if (signatureUri == null) 
                throw new ArgumentNullException("signatureUri"); 

            int index = GetSignatureIndex(signatureUri); 
            if (index < 0)
                return null;
            else
            { 
                Debug.Assert(index < _signatures.Count);
                return _signatures[index]; 
            } 
        }
 
        /// 
        /// Verify Certificate
        /// 
        /// certificate to inspect 
        /// certificate is invalid but the error code is not recognized
        /// the first error encountered when inspecting the certificate chain or NoError if the certificate is valid 
        /// 
        ///     Critical - The X509Chain.Build method has a LinkDemand for Unrestricted.
        ///     PublicOK – VerifyCertificate has LinkDemand. 
        ///
        [SecurityCritical]
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static X509ChainStatusFlags VerifyCertificate(X509Certificate certificate) 
        {
            if (certificate == null) 
                throw new ArgumentNullException("certificate"); 

            X509ChainStatusFlags status = X509ChainStatusFlags.NoError; 

            // build the certificate chain
            X509Chain chain = new X509Chain();
            bool valid = chain.Build(new X509Certificate2(certificate.Handle)); 

            // inspect the results 
            if (!valid) 
            {
                X509ChainStatus[] chainStatus = chain.ChainStatus; 
                for (int i = 0; i < chainStatus.Length; i++)
                {
                    status |= chainStatus[i].Status;
                } 
            }
 
            return status; 
        }
        #endregion 

        //------------------------------------------------------
        //
        //  Internal Properties 
        //
        //----------------------------------------------------- 
 
        /// 
        /// Get package - used by DigitalSignatureProcessors 
        /// 
        internal Package Package
        {
            get 
            {
                return _container; 
            } 
        }
 
        //------------------------------------------------------
        //
        //  Internal Methods
        // 
        //-----------------------------------------------------
        ///  
        /// PromptForSigningCertificate - invoked from Sign overloads if certificate is not provided by caller 
        /// 
        ///  
        /// null if user cancels or no certificate could be located
        ///
        ///     Critical: calls X509Certificate2UI.SelectFromCollection which LinkDemands
        ///     TreatAsSafe: UI can only display existing certificates, no spoofing 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        static internal X509Certificate PromptForSigningCertificate(IntPtr hwndParent) 
        {
            X509Certificate2 X509cert = null; 

            // look for appropriate certificates
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly); 
            X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
 
            // narrow down the choices 
            // timevalid
            collection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true); 

            // intended for signing (or no intent specified)
            collection = collection.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false);
 
            // remove certs that don't have private key
            // work backward so we don't disturb the enumeration 
            for (int i = collection.Count - 1; i >= 0; i--) 
            {
                if (!collection[i].HasPrivateKey) 
                {
                    collection.RemoveAt(i);
                }
            } 

            // any suitable certificates available? 
            if (collection.Count > 0) 
            {
                // ask user to select 
                collection = X509Certificate2UI.SelectFromCollection(collection, SR.Get(SRID.CertSelectionDialogTitle), SR.Get(SRID.CertSelectionDialogMessage), X509SelectionFlag.SingleSelection, hwndParent);
                if (collection.Count > 0)
                {
                    X509cert = collection[0];   // return the first one 
                }
            } 
 
            return X509cert;
        } 

        #region Private Members
        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 
        /// 
        /// Predicate for use with List.Exists() 
        /// 
        private class StringMatchPredicate
        {
            public StringMatchPredicate(String id) 
            {
                _id = id; 
            } 

            public bool Match(String id) 
            {
                return (String.CompareOrdinal(_id, id) == 0);
            }
 
            private string _id;
        } 
 
        /// 
        /// Verify Parts Exist before signing 
        /// 
        /// 
        /// This call must be done after the signature Origin has been created to allow for
        /// callers to sign an Origin (or it's relationship part) for the first signature in the package. 
        private void VerifyPartsExist(IEnumerable parts)
        { 
            // check for missing parts 
            if (parts != null)
            { 
                foreach (Uri partUri in parts)
                {
                    if (!_container.PartExists(partUri))
                    { 
                        // delete origin part if it was created and this is the first signature
                        if (_signatures.Count == 0) 
                            DeleteOriginPart(); 

                        throw new ArgumentException(SR.Get(SRID.PartToSignMissing), "parts"); 
                    }
                }
            }
 
        }
 
        ///  
        /// Verifies arguments to Sign() method - sub-function to reduce complexity in Sign() logic
        ///  
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        private void VerifySignArguments(IEnumerable parts, 
            X509Certificate certificate,
            IEnumerable relationshipSelectors, 
            String signatureId,
            IEnumerable signatureObjects,
            IEnumerable objectReferences)
        { 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 
 
            // Check for empty collections in order to provide negative feedback as soon as possible.
            if (EnumeratorEmptyCheck(parts) && EnumeratorEmptyCheck(relationshipSelectors) 
                && EnumeratorEmptyCheck(signatureObjects) && EnumeratorEmptyCheck(objectReferences))
                throw new ArgumentException(SR.Get(SRID.NothingToSign));

            // check for illegal and/or duplicate id's in signatureObjects 
            if (signatureObjects != null)
            { 
                List ids = new List(); 
                foreach (DataObject obj in signatureObjects)
                { 
                    // ensure they don't duplicate the reserved one
                    if (String.CompareOrdinal(obj.Id, XTable.Get(XTable.ID.OpcAttrValue)) == 0)
                        throw new ArgumentException(SR.Get(SRID.SignaturePackageObjectTagMustBeUnique), "signatureObjects");
 
                    // check for duplicates
                    //if (ids.Contains(obj.Id)) 
                    if (ids.Exists(new StringMatchPredicate(obj.Id).Match)) 
                        throw new ArgumentException(SR.Get(SRID.SignatureObjectIdMustBeUnique), "signatureObjects");
                    else 
                        ids.Add(obj.Id);
                }
            }
 
            // ensure id is legal Xml id
            if ((signatureId != null) && (signatureId != String.Empty)) 
            { 
                try
                { 
                    // An XSD ID is an NCName that is unique.
                    System.Xml.XmlConvert.VerifyNCName(signatureId);
                }
                catch (System.Xml.XmlException xmlException) 
                {
                    throw new ArgumentException(SR.Get(SRID.NotAValidXmlIdString, signatureId), "signatureId", xmlException); 
                } 
            }
        } 

        /// 
        /// Returns true if the given enumerator is null or empty
        ///  
        /// may be null
        /// true if enumerator is empty or null 
        private bool EnumeratorEmptyCheck(System.Collections.IEnumerable enumerable) 
        {
            if (enumerable == null) 
                return true;            // null means empty

            // see if it's really a collection as this is more efficient than enumerating
            System.Collections.ICollection collection = enumerable as System.Collections.ICollection; 
            if (collection != null)
            { 
                return (collection.Count == 0); 
            }
            else 
            {
                // not a collection - do things the hard way
                foreach (Object o in enumerable)
                { 
                    return false;   // if we get here - we're not empty
                } 
 
                return true;        // empty
            } 
        }

        /// 
        /// Remove a signature - helper method 
        /// 
        /// signature to remove 
        /// number of signatures that will remain 
        /// after the remove operation. If this count becomes zero, then we can remove the
        /// origin part also from the package as there will be no remaining signatures 
        /// in the package.
        /// Caller should call Package.Flush() in order to persist changes.
        private void InternalRemoveSignature(Uri signatureUri, int countOfSignaturesRemaining)
        { 
            Debug.Assert(signatureUri != null);
            Debug.Assert(countOfSignaturesRemaining >= 0); 
 
            // Remove origin if this operation will have removed the last signature in order to conform with Metro specification.
            // This will remove all relationships too so the code in the "else" clause becomes redundant and we can skip it. 
            if (countOfSignaturesRemaining == 0)
            {
                DeleteOriginPart();
            } 
            else    // there will be at least a single signature left after this remove, so we need to be more delicate in our surgery
            { 
                SafeVisitRelationships( 
                    OriginPart.GetRelationshipsByType(_originToSignatureRelationshipType),
                    DeleteRelationshipToSignature, signatureUri); 
            }

            // delete the cert (if any) if it's reference count will become zero
            SafeVisitRelationships(_container.GetPart(signatureUri).GetRelationshipsByType(CertificatePart.RelationshipType), 
                DeleteCertificateIfReferenceCountBecomesZeroVisitor);
 
            // delete the signature part 
            _container.DeletePart(signatureUri);
        } 

        // return true to continue
        private delegate bool RelationshipOperation(PackageRelationship r, Object context);
 
        /// 
        /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
        ///  
        /// collection of relationships to visit
        /// function to call with each relationship in the list 
        private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit)
        {
            SafeVisitRelationships(relationships, visit, null);
        } 

        ///  
        /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
        /// 
        /// collection of relationships to visit 
        /// function to call with each relationship in the list
        /// context object - may be null
        private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit, Object context)
        { 
            // make a local copy that will not be invalidated by any activity of the visitor function
            List relationshipsToVisit = new List(relationships); 
 
            // now invoke the delegate for each member
            for (int i = 0; i < relationshipsToVisit.Count; i++) 
            {
                // exit if visitor wants us to
                if (!visit(relationshipsToVisit[i], context))
                    break; 
            }
        } 
 
        /// 
        /// Removes the certificate associated with the given signature if removing the signature would leave the 
        /// certificate part orphaned.
        /// 
        private bool DeleteCertificateIfReferenceCountBecomesZeroVisitor(PackageRelationship r, Object context)
        { 
            // don't resolve if external
            if (r.TargetMode != TargetMode.Internal) 
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

            Uri certificatePartName = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri); 
            if (CertificatePartReferenceCount(certificatePartName) == 1)    // we are part of the calculation so one is the magic number
                _container.DeletePart(certificatePartName);                 // will not throw if part not found

            return true; 
        }
 
        ///  
        /// Deletes any relationship that is of the type that relates a Package to the Digital Signature Origin
        ///  
        /// 
        /// 
        /// 
        private bool DeleteRelationshipOfTypePackageToOriginVisitor(PackageRelationship r, Object context) 
        {
            Debug.Assert(Uri.Compare(r.SourceUri, 
                                     PackUriHelper.PackageRootUri, 
                                     UriComponents.SerializationInfoString,
                                     UriFormat.UriEscaped, 
                                     StringComparison.Ordinal) == 0,
                "Logic Error: This visitor should only be called with relationships from the Package itself");

            // don't resolve if external 
            if (r.TargetMode != TargetMode.Internal)
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 
 
            Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
            if (PackUriHelper.ComparePartUri(targetUri, _originPartName) == 0) 
                _container.DeleteRelationship(r.Id);

            return true;
        } 

        ///  
        /// Deletes any relationship to the given signature from the signature origin 
        /// 
        /// relationship from origin 
        /// signatureUri
        /// true
        private bool DeleteRelationshipToSignature(PackageRelationship r, Object signatureUri)
        { 
            Uri uri = signatureUri as Uri;
            Debug.Assert(uri != null, "Improper use of delegate - context must be Uri"); 
 
            // don't resolve if external
            if (r.TargetMode != TargetMode.Internal) 
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));

            if (PackUriHelper.ComparePartUri(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri), uri) == 0)
            { 
                OriginPart.DeleteRelationship(r.Id);    // don't break early in case there are redundant relationships
            } 
 
            return true;
        } 

        private void DeleteOriginPart()
        {
            try 
            {
                // remove all relationships of the type "package-to-signature-origin" 
                SafeVisitRelationships(_container.GetRelationshipsByType(_originRelationshipType), 
                    DeleteRelationshipOfTypePackageToOriginVisitor);
 
                _container.DeletePart(_originPartName);
            }
            finally
            { 
                // reset state variables
                _originPartExists = false; 
                _originSearchConducted = true; 
                _originPart = null;
            } 
        }

        /// 
        /// Lookup the index of the signature object in the _signatures array by the name of the part 
        /// 
        /// name of the signature part 
        /// zero-based index or -1 if not found 
        private int GetSignatureIndex(Uri uri)
        { 
            EnsureSignatures();
            for (int i = 0; i < _signatures.Count; i++)
            {
                if (PackUriHelper.ComparePartUri(uri, _signatures[i].SignaturePart.Uri) == 0) 
                    return i;
            } 
            return -1;      // not found 
        }
 

        /// 
        /// Counts the number of signatures using the given certificate
        ///  
        /// certificate to inspect
        private int CertificatePartReferenceCount(Uri certificatePartUri) 
        { 
            // count the number of signatures that reference this certificate part
            int count = 0; 
            for (int i = 0; i < _signatures.Count; i++)
            {
                // for each signature, follow it's certificate link (if there) and compare the Uri
                if (_signatures[i].GetCertificatePart() != null) 
                {
                    // same Uri? 
                    if (PackUriHelper.ComparePartUri(certificatePartUri, _signatures[i].GetCertificatePart().Uri) == 0) 
                        ++count;
                } 
            }

            return count;
        } 

        ///  
        /// Generate guid-based signature name to reduce chances of conflict in merging scenarios 
        /// 
        ///  
        private Uri GenerateSignaturePartName()
        {
            return PackUriHelper.CreatePartUri(new Uri(_defaultSignaturePartNamePrefix +
                Guid.NewGuid().ToString(_guidStorageFormatString, (IFormatProvider)null) + _defaultSignaturePartNameExtension, UriKind.Relative)); 
        }
 
        // load signatures from container 
        private void EnsureSignatures()
        { 
            if (_signatures == null)
            {
                _signatures = new List();
 
                // no signatures if origin not found
                if (OriginPartExists()) 
                { 
                    // find all signatures from this origin (if any)
                    PackageRelationshipCollection relationships = _originPart.GetRelationshipsByType( 
                        _originToSignatureRelationshipType);

                    foreach (PackageRelationship r in relationships)
                    { 
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

                        Uri signaturePartName = PackUriHelper.ResolvePartUri(_originPart.Uri, r.TargetUri); 

                        // throw if part does not exist
                        if (!_container.PartExists(signaturePartName))
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

                        PackagePart signaturePart = _container.GetPart(signaturePartName); 
 
                        // ignore future signature types that we do not recognize
                        if (signaturePart.ValidatedContentType.AreTypeAndSubTypeEqual 
                            (XmlDigitalSignatureProcessor.ContentType))
                        {
                            // parse it
                            PackageDigitalSignature signature = new PackageDigitalSignature(this, signaturePart); 

                            // add to the list 
                            _signatures.Add(signature); 
                        }
                    } 
                }
            }
        }
 
        /// 
        /// Looks for part name of Origin by searching from the container root and following the metro origin part relationship 
        ///  
        /// side effect of assigning the _originPartName and _originPart if found
        /// true if found 
        private bool OriginPartExists()
        {
            // only search once
            if (!_originSearchConducted) 
            {
                try 
                { 
                    Debug.Assert(!_originPartExists, "Logic Error: If OriginPartExists, OriginSearchConducted should be true.");
                    PackageRelationshipCollection containerRelationships = _container.GetRelationshipsByType(_originRelationshipType); 
                    foreach (PackageRelationship r in containerRelationships)
                    {
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
 
                        // resolve target (may be relative) 
                        Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
 
                        // if part does not exist - we throw
                        if (!_container.PartExists(targetUri))
                            throw new FileFormatException(SR.Get(SRID.SignatureOriginNotFound));
 
                        PackagePart p = _container.GetPart(targetUri);
 
                        // inspect content type - ignore things we don't understand 
                        if (p.ValidatedContentType.AreTypeAndSubTypeEqual(_originPartContentType))
                        { 
                            // throw if more than one relationship to an origin part that we recognize
                            if (_originPartExists)
                                throw new FileFormatException(SR.Get(SRID.MultipleSignatureOrigins));
 
                            // overwrite default if some container is using some other name
                            _originPartName = targetUri; 
                            _originPart = p; 
                            _originPartExists = true;
                        } 
                    }
                }
                finally
                { 
                    _originSearchConducted = true;
                } 
            } 
            return _originPartExists;
        } 

        //------------------------------------------------------
        //
        //  Private Properties 
        //
        //----------------------------------------------------- 
        private bool ReadOnly 
        {
            get 
            {
                return (_container.FileOpenAccess == FileAccess.Read);
            }
        } 

        private PackagePart OriginPart 
        { 
            get
            { 
                if (_originPart == null)
                {
                    if (!OriginPartExists())
                    { 
                        // add if not found
                        _originPart = _container.CreatePart(_originPartName, _originPartContentType.ToString()); 
                        _container.CreateRelationship(_originPartName, TargetMode.Internal, _originRelationshipType); 
                    }
                } 

                return _originPart;
            }
        } 

        //------------------------------------------------------ 
        // 
        //  Private Fields
        // 
        //------------------------------------------------------
        private CertificateEmbeddingOption      _certificateEmbeddingOption;
        private Package                         _container;
        private IntPtr                          _parentWindow; 
        private static Uri _defaultOriginPartName = PackUriHelper.CreatePartUri(new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative));
        private Uri                             _originPartName = _defaultOriginPartName; 
        private PackagePart                     _originPart; 
        private String                          _hashAlgorithmString = _defaultHashAlgorithm;
        private String                          _signatureTimeFormat = XmlSignatureProperties.DefaultDateTimeFormat; 
        private List   _signatures;
        private Dictionary      _transformDictionary;
        private bool                            _originSearchConducted;             // don't look more than once for Origin part
        private bool                            _originPartExists;                  // was the part found? 
        private ReadOnlyCollection _signatureList;         // lazy-init cached return value for Signatures property
 
        private static readonly ContentType _originPartContentType = new ContentType("application/vnd.openxmlformats-package.digital-signature-origin"); 

        private static readonly String _guidStorageFormatString = @"N";     // N - simple format without adornments 
        private static readonly String _defaultHashAlgorithm = SignedXml.XmlDsigSHA1Url;
        private static readonly String _originRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
        private static readonly String _originToSignatureRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
        private static readonly String _defaultSignaturePartNamePrefix = "/package/services/digital-signature/xml-signature/"; 
        private static readonly String _defaultSignaturePartNameExtension = ".psdsxs";
        #endregion Private Members 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This class provides api's to add/remove/verify signatures on an MMCF container. 
//
// History: 
//  03/22/2004: BruceMac: Initial Implementation
//
//-----------------------------------------------------------------------------
 
// Allow use of presharp warning numbers [6506] unknown to the compiler
#pragma warning disable 1634, 1691 
 
using System;
using System.Collections.Generic; 
using System.Windows;                                   // For Exception strings - SRID
using System.Text;                                      // for StringBuilder
using System.Diagnostics;                               // for Assert
using System.Security;                                  // for SecurityCritical tag 
using System.Security.Permissions;                      // for LinkDemand
using System.Security.Cryptography.Xml;                 // for SignedXml 
using System.Security.Cryptography.X509Certificates;    // for X509Certificate 
using MS.Internal.IO.Packaging;                         // for internal helpers
using System.Collections.ObjectModel;                   // for ReadOnlyCollection<> 
using MS.Internal;                                      // for ContentType
using MS.Internal.WindowsBase;

namespace System.IO.Packaging 
{
    ///  
    /// Options for storing the signing Certificate 
    /// 
    public enum CertificateEmbeddingOption : int 
    {
        /// 
        /// Embed certificate in its own PackagePart (or share if same cert already exists)
        ///  
        InCertificatePart = 0,      // embed the certificate in its own, possibly-shared part
        ///  
        /// Embed certificate within the signature PackagePart 
        /// 
        InSignaturePart = 1,        // embed the certificate within the signature 
        /// 
        /// Do not embed
        /// 
        NotEmbedded = 2,            // do not embed the certificate at all 
    }
 
    ///  
    /// Type of the handler that is invoked if signature validation is non-success.
    ///  
    /// signature
    /// event arguments - containing the result
    /// true to continue verifying other signatures, false to abandon effort
    public delegate void InvalidSignatureEventHandler(object sender, SignatureVerificationEventArgs e); 

    ///  
    /// Signature Verification Event Args - information about a verification event 
    /// 
    public class SignatureVerificationEventArgs : EventArgs 
    {
        //-----------------------------------------------------
        //
        //  Public Members 
        //
        //----------------------------------------------------- 
        ///  
        /// Signature being processed
        ///  
        public PackageDigitalSignature Signature
        {
            get
            { 
                return _signature;
            } 
        } 

        ///  
        /// Result of Verification
        /// 
        public VerifyResult VerifyResult
        { 
            get
            { 
                return _result; 
            }
        } 

        //------------------------------------------------------
        //
        //  Internal Members 
        //
        //----------------------------------------------------- 
        internal SignatureVerificationEventArgs(PackageDigitalSignature signature, 
            VerifyResult result)
        { 
            // verify arguments
            if (signature == null)
                throw new ArgumentNullException("signature");
 
            if (result < VerifyResult.Success || result > VerifyResult.NotSigned)
                throw new System.ArgumentOutOfRangeException("result"); 
 
            _signature = signature;
            _result = result; 
        }

        //------------------------------------------------------
        // 
        //  Private Members
        // 
        //------------------------------------------------------ 
        private PackageDigitalSignature                 _signature;
        private VerifyResult                            _result; 
    }

    /// 
    /// PackageDigitalSignatureManager 
    /// 
    public sealed class PackageDigitalSignatureManager 
    { 
        #region Public Members
        //----------------------------------------------------- 
        //
        //  Public Events
        //
        //------------------------------------------------------ 
        /// 
        /// Event to subscribe to for signature validation activities 
        ///  
        public event InvalidSignatureEventHandler InvalidSignatureEvent;
 
        //-----------------------------------------------------
        //
        //  Public Properties
        // 
        //-----------------------------------------------------
        ///  
        /// Does this container hold digital signatures? 
        /// 
        /// true if signatures exist 
        /// this does not evaluate the signatures - they may be invalid even if this returns true
        public bool IsSigned
        {
            get 
            {
                EnsureSignatures(); 
                return (_signatures.Count > 0); 
            }
        } 

        /// 
        /// Signatures in container
        ///  
        /// read only list of immutable signatures found in the container
        public ReadOnlyCollection Signatures 
        { 
            get
            { 
                // ensure signatures are loaded from origin
                EnsureSignatures();

                // Return a read-only collection referring to them. 
                // This list will be automatically updated when the underlying collection is changed.
                if (_signatureList == null) 
                    _signatureList = new ReadOnlyCollection(_signatures); 

                return _signatureList; 
            }
        }

        ///  
        /// ContentType - Transform mapping dictionary
        ///  
        /// Dictionary of transform Uri's indexed by ContentType. 
        /// Contains a single transform to be applied
        /// before hashing any Part encountered with that ContentType 
        public Dictionary TransformMapping
        {
            get
            { 
                return _transformDictionary;
            } 
        } 

        ///  
        /// Handle of parent window to use when displaying certificate selection dialog
        /// 
        /// 
        /// not necessary if certificates are provided in calls to sign 
        public IntPtr ParentWindow
        { 
            get 
            {
                return _parentWindow; 
            }
            set
            {
                _parentWindow = value; 
            }
        } 
 
        /// 
        /// Hashalgorithm to use when creating/verifying signatures 
        /// 
        /// 
        /// defaults to SHA1
        public String HashAlgorithm 
        {
            get 
            { 
                return _hashAlgorithmString;
            } 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value"); 

                if (value == String.Empty) 
                    throw new ArgumentException(SR.Get(SRID.UnsupportedHashAlgorithm), "value"); 

                _hashAlgorithmString = value; 
            }
        }

        ///  
        /// How to embed certificates when Signing
        ///  
        ///  
        public CertificateEmbeddingOption CertificateOption
        { 
            get
            {
                return _certificateEmbeddingOption;
            } 
            set
            { 
                if ((value < CertificateEmbeddingOption.InCertificatePart) || (value > CertificateEmbeddingOption.NotEmbedded)) 
                    throw new ArgumentOutOfRangeException("value");
 
                _certificateEmbeddingOption = value;
            }
        }
 
        /// 
        /// How to format the SignatureTime in new signatures 
        ///  
        /// Legal formats specified in Opc book and reproduced here:
        /// YYYY-MM-DDThh:mm:ss.sTZD 
        /// YYYY-MM-DDThh:mm:ssTZD
        /// YYYY-MM-DDThh:mmTZD
        /// YYYY-MM-DD
        /// YYYY-MM 
        /// YYYY
        /// 
        /// where: 
        /// Y = year, M = month integer (leading zero), D = day integer (leading zero),
        /// hh = 24hr clock hour 
        /// mm = minutes (leading zero)
        /// ss = seconds (leading zero)
        /// .s = tenths of a second
        ///  
        public String TimeFormat
        { 
            get 
            {
                return _signatureTimeFormat; 
            }
            set
            {
                if (value == null) 
                    throw new ArgumentNullException("value");
 
                if (XmlSignatureProperties.LegalFormat(value)) 
                    _signatureTimeFormat = value;
                else 
                    throw new FormatException(SR.Get(SRID.BadSignatureTimeFormatString));
            }
        }
 
        //-----------------------------------------------------
        // 
        //  Public Fields 
        //
        //------------------------------------------------------ 
        /// 
        /// Name of signature origin part
        /// 
        ///  
        /// This value may vary by Package because the name is not formally defined. While this
        /// implementation will generally use the same default value, the value returned by this property will reflect 
        /// whatever origin is already present in the current Package (if any) which may vary between implementations. 
        /// 
        public Uri SignatureOrigin 
        {
            get
            {
                OriginPartExists();           // force search for OriginPart in case it is different from default 
                return _originPartName;
            } 
        } 

        ///  
        /// Type of default signature origin relationship
        /// 
        /// 
        static public String SignatureOriginRelationshipType 
        {
            get 
            { 
                return _originRelationshipType;
            } 
        }

        /// 
        /// Default hash algorithm 
        /// 
        ///  
        static public String DefaultHashAlgorithm 
        {
            get 
            {
                return _defaultHashAlgorithm;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Public Methods
        // 
        //------------------------------------------------------
        /// 
        /// Create a new PackageDigitalSignature manager
        ///  
        /// container to work with
        /// based on the default origin 
        /// package is null 
        public PackageDigitalSignatureManager(Package package)
        { 
            if (package == null)
                throw new ArgumentNullException("package");

            _parentWindow = IntPtr.Zero; 
            _container = package;
 
            // initialize the transform dictionary with defaults 
            _transformDictionary = new Dictionary(4);
            _transformDictionary[PackagingUtilities.RelationshipPartContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl;    // relationship parts 
            _transformDictionary[XmlDigitalSignatureProcessor.ContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl;          // xml signature
        }

        #region Sign 
        /// 
        /// Sign - prompts for certificate and embeds it 
        ///  
        /// list of parts to sign
        /// Set ParentWindow before this call if you want to make the certificate 
        /// selection dialog modal to a particular window.  Does not prompt for certificates if none could be located in the default certificate store.
        /// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
        public PackageDigitalSignature Sign(IEnumerable parts)
        { 
            X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
            if (certificate == null) 
                return null; 
            else
                return Sign(parts, certificate); 
        }

        /// 
        /// Sign - certificate provided by caller 
        /// 
        /// list of parts to sign 
        /// signer's certificate 
        public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate)
        { 
            // create unique signature name
            return Sign(parts, certificate, null);
        }
 
        /// 
        /// Sign - certificate provided by caller 
        ///  
        /// list of parts to sign - may be empty or null
        /// signer's certificate 
        /// relationshipSelectors that hold information about
        /// the relationships to be signed - may be empty or null
        /// one of parts or relationships must be non-null and contain at least a single entry
        public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate, IEnumerable relationshipSelectors) 
        {
            // use default signature Id 
            return Sign(parts, certificate, relationshipSelectors, XTable.Get(XTable.ID.OpcSignatureAttrValue)); 
        }
 
                /// 
        /// Sign - certificate provided by caller
        /// 
        /// list of parts to sign - may be empty or null 
        /// signer's certificate
        /// relationshipSelectors that hold information about 
        /// the relationships to be signed - may be empty or null 
        /// id for the new Signature - may be empty or null
        /// one of parts or relationships must be non-null and contain at least a single entry 
        public PackageDigitalSignature Sign(
            IEnumerable parts,
            X509Certificate certificate,
            IEnumerable relationshipSelectors, 
            String signatureId)
        { 
            // Cannot both be null - need to check here because the similar check in the super-overload cannot 
            // distinguish to this level.
            if (parts == null && relationshipSelectors == null) 
            {
                throw new ArgumentException(SR.Get(SRID.NothingToSign));
            }
 
            return Sign(parts, certificate, relationshipSelectors, signatureId, null, null);
        } 
 
        /// 
        /// Sign - caller specifies custom "Object" and/or SignedInfo "Reference" tags 
        /// 
        /// list of parts to sign - may be empty or null
        /// signer's certificate
        /// relationshipSelectors that hold information about 
        /// the relationships to be signed - may be empty or null
        /// id for the new Signature - may be empty or null 
        /// references to custom object tags.  The DigestMethod on each 
        /// Reference will be ignored.  The signature will use the globally defined HashAlgorithm
        /// obtained from the current value of the HashAlgorithm property. 
        /// objects (signed or not)
        /// Thrown if any TransformMapping
        /// defines an empty or null transform for the ContentType of any Part being signed or if an unknown
        /// transform is encountered. 
        /// Thrown if signatureId is non-null and violates the
        /// Xml Id schema (essentially - no leading digit is allowed). 
        /// One of parts, relationships, signatureObjects and objectReferences must be 
        /// non-null and contain at least a single entry.
        /// This and every other Sign overload makes use of the current state of the TransformMapping 
        /// dictionary which defines a Transform to apply based on ContentType.  The Opc specification
        /// only currently allows for two legal Transform algorithms: C14 and C14N.
        /// Note that the w3c Xml Signature standard does not allow for empty Manifest tags.
        /// Because the Opc specification requires the existence of a Package-specific Object 
        /// tag and further specifies that this Object tag contain a Manifest and SignatureProperties
        /// tags, it follows that this Manifest tag must include at least one Reference tag. 
        /// This means that every signature include at least one of a Part to sign (non-empty parts tag) 
        /// or a Relationship to sign (non-empty relationshipSelectors) even if such a signature
        /// is only destined to sign signatureObjects and/or objectReferences. 
        /// This overload provides support for generation of Xml signatures that require custom
        /// Object tags.  For any provided Object tag to be signed, a corresponding Reference
        /// tag must be provided with a Uri that targets the Object tag using local fragment
        /// syntax.  If the object had an ID of "myObject" the Uri on the Reference would 
        /// be "#myObject".  For unsigned objects, no reference is required.
        /// 
        ///     Critical: calls X509Certificate.Handle which LinkDemands 
        ///     PublicOK: we don't store or return the handle
        /// 
        [SecurityCritical]
        public PackageDigitalSignature Sign(
            IEnumerable parts,
            X509Certificate certificate, 
            IEnumerable relationshipSelectors,
            String signatureId, 
            IEnumerable signatureObjects, 
            IEnumerable objectReferences)
        { 
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotSignReadOnlyFile));

            VerifySignArguments(parts, certificate, relationshipSelectors, signatureId, signatureObjects, objectReferences); 

            // substitute default id if none given 
            if ((signatureId == null) || (signatureId == String.Empty)) 
            {
                signatureId = "packageSignature";   // default 
            }

            // Make sure the list reflects what's in the package.
            // Do this before adding the new signature part because we don't want it included until it 
            // is fully formed (and delaying the add saves us having to remove it in case there is an
            // error during the Sign call). 
            EnsureSignatures(); 

            Uri newSignaturePartName = GenerateSignaturePartName(); 
            if (_container.PartExists(newSignaturePartName))
                throw new ArgumentException(SR.Get(SRID.DuplicateSignature));

            // Pre-create origin part if it does not already exist. 
            // Do this before signing to allow for signing the package relationship part (because a Relationship
            // is added from the Package to the Origin part by this call) and the Origin Relationship part in case this is 
            // a Publishing signature and the caller wants the addition of more signatures to break this signature. 
            PackageRelationship relationshipToNewSignature = OriginPart.CreateRelationship(newSignaturePartName, TargetMode.Internal,
                    _originToSignatureRelationshipType); 
            _container.Flush();     // ensure the origin relationship part is persisted so that any signature will include this newest relationship

            VerifyPartsExist(parts);
 
            // sign the data and optionally embed the certificate
            bool embedCertificateInSignaturePart = (_certificateEmbeddingOption == CertificateEmbeddingOption.InSignaturePart); 
 
            // convert cert to version2 - more functionality
            X509Certificate2 exSigner = certificate as X509Certificate2; 
            if (exSigner == null)
                exSigner = new X509Certificate2(certificate.Handle);

            //PRESHARP: Parameter to this public method must be validated:  A null-dereference can occur here. 
            //      Parameter 'exSigner' to this public method must be validated:  A null-dereference can occur here.
            //This is a false positive as the checks above can gurantee no null dereference will occur 
#pragma warning disable 6506 

            PackageDigitalSignature signature = null; 
            PackagePart newSignaturePart = null;
            try
            {
                // create the new part 
                newSignaturePart = _container.CreatePart(newSignaturePartName, XmlDigitalSignatureProcessor.ContentType.ToString());
 
                // do the actual signing - only Xml signatures currently supported 
                signature = XmlDigitalSignatureProcessor.Sign(this, newSignaturePart, parts, relationshipSelectors, exSigner, signatureId, embedCertificateInSignaturePart,
                    signatureObjects, objectReferences); 
            }
            catch (InvalidOperationException)
            {
                // bad hash algorithm - revert changes 
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were 
                // existing before this sign method was called. So we want to leave those 
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part. 
                InternalRemoveSignature(newSignaturePartName, _signatures.Count);
                _container.Flush();    // actually persist the revert
                throw;
            } 
            catch (System.IO.IOException)
            { 
                // failure to open part - revert changes 
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were 
                // existing before this sign method was called. So we want to leave those
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part.
                InternalRemoveSignature(newSignaturePartName, _signatures.Count); 
                _container.Flush();    // actually persist the revert
                throw; 
            } 
            catch (System.Security.Cryptography.CryptographicException)
            { 
                // failure to sign - revert changes
                // guarantees proper cleanup including removal of Origin if appropriate
                // Note: _signatures.Count reflects the number of signatures that were
                // existing before this sign method was called. So we want to leave those 
                // untouched and clean up what we added in this method prior to the
                // exception. If the count is zero, we will also delete the origin part. 
                InternalRemoveSignature(newSignaturePartName, _signatures.Count); 
                _container.Flush();    // actually persist the revert
                throw; 
            }

            // add to the list
            _signatures.Add(signature); 

            // embed certificate if called for 
            if (_certificateEmbeddingOption == CertificateEmbeddingOption.InCertificatePart) 
            {
                // create the cert part 
                // auto-generate a certificate name - will be the same for the same certificate
                Uri certificatePartName = PackUriHelper.CreatePartUri(new Uri(
                    CertificatePart.PartNamePrefix + exSigner.SerialNumber + CertificatePart.PartNameExtension, UriKind.Relative));
 
                // create the serialization helper class (side-effect of creating or opening the part)
                CertificatePart certPart = new CertificatePart(_container, certificatePartName); 
                certPart.SetCertificate(exSigner); 

                // establish a relationship 
                newSignaturePart.CreateRelationship(certificatePartName, TargetMode.Internal, CertificatePart.RelationshipType);
                signature.SetCertificatePart(certPart);
            }
#pragma warning restore 6506 

            _container.Flush(); 
 
            // return to caller in case they need it
            return signature; 
        }
        #endregion

        #region CounterSign 
        /// 
        /// CounterSign - prompts for certificate and embeds it based on current CertificateEmbeddingOption 
        ///  
        /// Set ParentWindow before this call if you want to make the certificate
        /// selection dialog modal to a particular window.  Does not present the dialog if no suitable certificate 
        /// could be found in the default certificate store.
        /// Signs all existing signature parts so that any change to these part(s) will invalidate the
        /// returned signature.
        /// Cannot CounterSign an unsigned package. 
        /// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
        public PackageDigitalSignature Countersign() 
        { 
            // Counter-sign makes no sense if we are not already signed
            // Check before asking for certificate 
            if (!IsSigned)
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));

            // prompt for certificate 
            X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
            if (certificate == null) 
                return null; 
            else
                return Countersign(certificate); 
        }

        /// 
        /// CounterSign - certificate provided 
        /// 
        /// signer's certificate 
        /// Cannot CounterSign an unsigned package. 
        /// certificate must be non-null.
        /// Signs all existing signature parts so that any change to these part(s) will invalidate the 
        /// returned signature.
        public PackageDigitalSignature Countersign(X509Certificate certificate)
        {
            if (certificate == null) 
                throw new ArgumentNullException("certificate");
 
            // Counter-sign makes no sense if we are not already signed 
            // Check before asking for certificate
            if (!IsSigned) 
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));

            // sign all existing signatures
            List signatures = new List(_signatures.Count); 
            for (int i = 0; i < _signatures.Count; i++)
            { 
                signatures.Add(_signatures[i].SignaturePart.Uri); 
            }
 
            // sign
            return Sign(signatures, certificate);
        }
 
        /// 
        /// CounterSign - signature part name(s) specified by caller 
        ///  
        /// signer's certificate
        /// signature parts to sign 
        /// Signs the given signature parts so that any change to these part(s) will invalidate the
        /// returned signature.
        /// Cannot CounterSign an unsigned package.
        /// signatures must be non-empty and cannot refer to parts other than signature parts. 
        /// Both arguments must be non-null.
        public PackageDigitalSignature Countersign(X509Certificate certificate, IEnumerable signatures) 
        { 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 

            if (signatures == null)
                throw new ArgumentNullException("signatures");
 
            // Counter-sign makes no sense if we are not already signed
            if (!IsSigned) 
                throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer)); 

            // Restrict signatures to be actual signature part references 
            foreach (Uri uri in signatures)
            {
                PackagePart part = _container.GetPart(uri);
                if (!part.ValidatedContentType.AreTypeAndSubTypeEqual(XmlDigitalSignatureProcessor.ContentType)) 
                    throw new ArgumentException(SR.Get(SRID.CanOnlyCounterSignSignatureParts, signatures));
            } 
 
            return Sign(signatures, certificate);
        } 
        #endregion

        /// 
        /// verify all signatures - calls verify on each signature 
        /// 
        /// true to exit on first failure - false to continue 
        /// register for invalid signature events 
        public VerifyResult VerifySignatures(bool exitOnFailure)
        { 
            VerifyResult result;
            EnsureSignatures();

            // signed? 
            if (_signatures.Count == 0)
                result = VerifyResult.NotSigned; 
            else 
            {
                // contract is to return a failure value, even if there are subsequent successes 
                // defaulting to success here simplifies the logic for this
                result = VerifyResult.Success;     // default
                for (int i = 0; i < _signatures.Count; i++)
                { 
                    VerifyResult temp = _signatures[i].Verify();
                    if (temp != VerifyResult.Success) 
                    { 
                        result = temp;  // note failure
 
                        if (InvalidSignatureEvent != null)
                            InvalidSignatureEvent(this, new SignatureVerificationEventArgs(_signatures[i], temp));

                        if (exitOnFailure) 
                            break;
                    } 
                } 
            }
 
            return result;
        }

        ///  
        /// Remove a signature
        ///  
        /// signature to remove 
        /// Caller should call Package.Flush() in order to persist changes.
        public void RemoveSignature(Uri signatureUri) 
        {
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile));
 
            if (signatureUri == null)
                throw new ArgumentNullException("signatureUri"); 
 
            // empty?
            if (!IsSigned)      // calls EnsureSignatures for us 
                return;

            // find the signature
            int index = GetSignatureIndex(signatureUri); 
            if (index < 0)
                return; 
 
            try
            { 
                Debug.Assert(index < _signatures.Count);

                //After this signature is removed the total number of signatures remaining will
                //be _signatures.Count - 1. If this count is zero, then additional clean up needs 
                //to be done, like removing the Origin part.
                InternalRemoveSignature(signatureUri, _signatures.Count - 1 /*since we are deleting one*/); 
 
                // invalidate the signature itself
                _signatures[index].Invalidate(); 
            }
            finally
            {
                _signatures.RemoveAt(index);    // ensure it is actually removed from the list 
            }
        } 
 
        /// 
        /// Remove all signatures based on this origin 
        /// 
        /// also removes all certificate parts and the signature origin.  Caller must call Flush() to persist changes.
        public void RemoveAllSignatures()
        { 
            if (ReadOnly)
                throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile)); 
 
            EnsureSignatures();
 
            try
            {
                // Remove via known traversal - required to find all signatures (we may not know all signature content-types).
                for (int i = 0; i < _signatures.Count; i++) 
                {
                    PackagePart p = _signatures[i].SignaturePart; 
 
                    // Delete any Certificate part(s) targeted by this signature.  We know that all of the
                    // reference counts will reach zero because we are removing all signatures. 
                    foreach (PackageRelationship r in p.GetRelationshipsByType(CertificatePart.RelationshipType))
                    {
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            continue;   // fail silently
 
                        _container.DeletePart(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri));                 // will not throw if part not found 
                    }
 
                    // delete signature part
                    _container.DeletePart(p.Uri);

                    // invalidate the signature itself 
                    _signatures[i].Invalidate();
                } 
 
                DeleteOriginPart();
            } 
            finally
            {
                // update internal variables
                _signatures.Clear(); 
            }
        } 
 
        /// 
        /// Obtain the PackageDigitalSignature referred to by the given Uri 
        /// 
        /// ID obtained from a PackageDigitalSignature object
        /// null if signature not found
        public PackageDigitalSignature GetSignature(Uri signatureUri) 
        {
            if (signatureUri == null) 
                throw new ArgumentNullException("signatureUri"); 

            int index = GetSignatureIndex(signatureUri); 
            if (index < 0)
                return null;
            else
            { 
                Debug.Assert(index < _signatures.Count);
                return _signatures[index]; 
            } 
        }
 
        /// 
        /// Verify Certificate
        /// 
        /// certificate to inspect 
        /// certificate is invalid but the error code is not recognized
        /// the first error encountered when inspecting the certificate chain or NoError if the certificate is valid 
        /// 
        ///     Critical - The X509Chain.Build method has a LinkDemand for Unrestricted.
        ///     PublicOK – VerifyCertificate has LinkDemand. 
        ///
        [SecurityCritical]
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static X509ChainStatusFlags VerifyCertificate(X509Certificate certificate) 
        {
            if (certificate == null) 
                throw new ArgumentNullException("certificate"); 

            X509ChainStatusFlags status = X509ChainStatusFlags.NoError; 

            // build the certificate chain
            X509Chain chain = new X509Chain();
            bool valid = chain.Build(new X509Certificate2(certificate.Handle)); 

            // inspect the results 
            if (!valid) 
            {
                X509ChainStatus[] chainStatus = chain.ChainStatus; 
                for (int i = 0; i < chainStatus.Length; i++)
                {
                    status |= chainStatus[i].Status;
                } 
            }
 
            return status; 
        }
        #endregion 

        //------------------------------------------------------
        //
        //  Internal Properties 
        //
        //----------------------------------------------------- 
 
        /// 
        /// Get package - used by DigitalSignatureProcessors 
        /// 
        internal Package Package
        {
            get 
            {
                return _container; 
            } 
        }
 
        //------------------------------------------------------
        //
        //  Internal Methods
        // 
        //-----------------------------------------------------
        ///  
        /// PromptForSigningCertificate - invoked from Sign overloads if certificate is not provided by caller 
        /// 
        ///  
        /// null if user cancels or no certificate could be located
        ///
        ///     Critical: calls X509Certificate2UI.SelectFromCollection which LinkDemands
        ///     TreatAsSafe: UI can only display existing certificates, no spoofing 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        static internal X509Certificate PromptForSigningCertificate(IntPtr hwndParent) 
        {
            X509Certificate2 X509cert = null; 

            // look for appropriate certificates
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly); 
            X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
 
            // narrow down the choices 
            // timevalid
            collection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true); 

            // intended for signing (or no intent specified)
            collection = collection.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false);
 
            // remove certs that don't have private key
            // work backward so we don't disturb the enumeration 
            for (int i = collection.Count - 1; i >= 0; i--) 
            {
                if (!collection[i].HasPrivateKey) 
                {
                    collection.RemoveAt(i);
                }
            } 

            // any suitable certificates available? 
            if (collection.Count > 0) 
            {
                // ask user to select 
                collection = X509Certificate2UI.SelectFromCollection(collection, SR.Get(SRID.CertSelectionDialogTitle), SR.Get(SRID.CertSelectionDialogMessage), X509SelectionFlag.SingleSelection, hwndParent);
                if (collection.Count > 0)
                {
                    X509cert = collection[0];   // return the first one 
                }
            } 
 
            return X509cert;
        } 

        #region Private Members
        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 
        /// 
        /// Predicate for use with List.Exists() 
        /// 
        private class StringMatchPredicate
        {
            public StringMatchPredicate(String id) 
            {
                _id = id; 
            } 

            public bool Match(String id) 
            {
                return (String.CompareOrdinal(_id, id) == 0);
            }
 
            private string _id;
        } 
 
        /// 
        /// Verify Parts Exist before signing 
        /// 
        /// 
        /// This call must be done after the signature Origin has been created to allow for
        /// callers to sign an Origin (or it's relationship part) for the first signature in the package. 
        private void VerifyPartsExist(IEnumerable parts)
        { 
            // check for missing parts 
            if (parts != null)
            { 
                foreach (Uri partUri in parts)
                {
                    if (!_container.PartExists(partUri))
                    { 
                        // delete origin part if it was created and this is the first signature
                        if (_signatures.Count == 0) 
                            DeleteOriginPart(); 

                        throw new ArgumentException(SR.Get(SRID.PartToSignMissing), "parts"); 
                    }
                }
            }
 
        }
 
        ///  
        /// Verifies arguments to Sign() method - sub-function to reduce complexity in Sign() logic
        ///  
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        private void VerifySignArguments(IEnumerable parts, 
            X509Certificate certificate,
            IEnumerable relationshipSelectors, 
            String signatureId,
            IEnumerable signatureObjects,
            IEnumerable objectReferences)
        { 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 
 
            // Check for empty collections in order to provide negative feedback as soon as possible.
            if (EnumeratorEmptyCheck(parts) && EnumeratorEmptyCheck(relationshipSelectors) 
                && EnumeratorEmptyCheck(signatureObjects) && EnumeratorEmptyCheck(objectReferences))
                throw new ArgumentException(SR.Get(SRID.NothingToSign));

            // check for illegal and/or duplicate id's in signatureObjects 
            if (signatureObjects != null)
            { 
                List ids = new List(); 
                foreach (DataObject obj in signatureObjects)
                { 
                    // ensure they don't duplicate the reserved one
                    if (String.CompareOrdinal(obj.Id, XTable.Get(XTable.ID.OpcAttrValue)) == 0)
                        throw new ArgumentException(SR.Get(SRID.SignaturePackageObjectTagMustBeUnique), "signatureObjects");
 
                    // check for duplicates
                    //if (ids.Contains(obj.Id)) 
                    if (ids.Exists(new StringMatchPredicate(obj.Id).Match)) 
                        throw new ArgumentException(SR.Get(SRID.SignatureObjectIdMustBeUnique), "signatureObjects");
                    else 
                        ids.Add(obj.Id);
                }
            }
 
            // ensure id is legal Xml id
            if ((signatureId != null) && (signatureId != String.Empty)) 
            { 
                try
                { 
                    // An XSD ID is an NCName that is unique.
                    System.Xml.XmlConvert.VerifyNCName(signatureId);
                }
                catch (System.Xml.XmlException xmlException) 
                {
                    throw new ArgumentException(SR.Get(SRID.NotAValidXmlIdString, signatureId), "signatureId", xmlException); 
                } 
            }
        } 

        /// 
        /// Returns true if the given enumerator is null or empty
        ///  
        /// may be null
        /// true if enumerator is empty or null 
        private bool EnumeratorEmptyCheck(System.Collections.IEnumerable enumerable) 
        {
            if (enumerable == null) 
                return true;            // null means empty

            // see if it's really a collection as this is more efficient than enumerating
            System.Collections.ICollection collection = enumerable as System.Collections.ICollection; 
            if (collection != null)
            { 
                return (collection.Count == 0); 
            }
            else 
            {
                // not a collection - do things the hard way
                foreach (Object o in enumerable)
                { 
                    return false;   // if we get here - we're not empty
                } 
 
                return true;        // empty
            } 
        }

        /// 
        /// Remove a signature - helper method 
        /// 
        /// signature to remove 
        /// number of signatures that will remain 
        /// after the remove operation. If this count becomes zero, then we can remove the
        /// origin part also from the package as there will be no remaining signatures 
        /// in the package.
        /// Caller should call Package.Flush() in order to persist changes.
        private void InternalRemoveSignature(Uri signatureUri, int countOfSignaturesRemaining)
        { 
            Debug.Assert(signatureUri != null);
            Debug.Assert(countOfSignaturesRemaining >= 0); 
 
            // Remove origin if this operation will have removed the last signature in order to conform with Metro specification.
            // This will remove all relationships too so the code in the "else" clause becomes redundant and we can skip it. 
            if (countOfSignaturesRemaining == 0)
            {
                DeleteOriginPart();
            } 
            else    // there will be at least a single signature left after this remove, so we need to be more delicate in our surgery
            { 
                SafeVisitRelationships( 
                    OriginPart.GetRelationshipsByType(_originToSignatureRelationshipType),
                    DeleteRelationshipToSignature, signatureUri); 
            }

            // delete the cert (if any) if it's reference count will become zero
            SafeVisitRelationships(_container.GetPart(signatureUri).GetRelationshipsByType(CertificatePart.RelationshipType), 
                DeleteCertificateIfReferenceCountBecomesZeroVisitor);
 
            // delete the signature part 
            _container.DeletePart(signatureUri);
        } 

        // return true to continue
        private delegate bool RelationshipOperation(PackageRelationship r, Object context);
 
        /// 
        /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
        ///  
        /// collection of relationships to visit
        /// function to call with each relationship in the list 
        private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit)
        {
            SafeVisitRelationships(relationships, visit, null);
        } 

        ///  
        /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
        /// 
        /// collection of relationships to visit 
        /// function to call with each relationship in the list
        /// context object - may be null
        private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit, Object context)
        { 
            // make a local copy that will not be invalidated by any activity of the visitor function
            List relationshipsToVisit = new List(relationships); 
 
            // now invoke the delegate for each member
            for (int i = 0; i < relationshipsToVisit.Count; i++) 
            {
                // exit if visitor wants us to
                if (!visit(relationshipsToVisit[i], context))
                    break; 
            }
        } 
 
        /// 
        /// Removes the certificate associated with the given signature if removing the signature would leave the 
        /// certificate part orphaned.
        /// 
        private bool DeleteCertificateIfReferenceCountBecomesZeroVisitor(PackageRelationship r, Object context)
        { 
            // don't resolve if external
            if (r.TargetMode != TargetMode.Internal) 
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

            Uri certificatePartName = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri); 
            if (CertificatePartReferenceCount(certificatePartName) == 1)    // we are part of the calculation so one is the magic number
                _container.DeletePart(certificatePartName);                 // will not throw if part not found

            return true; 
        }
 
        ///  
        /// Deletes any relationship that is of the type that relates a Package to the Digital Signature Origin
        ///  
        /// 
        /// 
        /// 
        private bool DeleteRelationshipOfTypePackageToOriginVisitor(PackageRelationship r, Object context) 
        {
            Debug.Assert(Uri.Compare(r.SourceUri, 
                                     PackUriHelper.PackageRootUri, 
                                     UriComponents.SerializationInfoString,
                                     UriFormat.UriEscaped, 
                                     StringComparison.Ordinal) == 0,
                "Logic Error: This visitor should only be called with relationships from the Package itself");

            // don't resolve if external 
            if (r.TargetMode != TargetMode.Internal)
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 
 
            Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
            if (PackUriHelper.ComparePartUri(targetUri, _originPartName) == 0) 
                _container.DeleteRelationship(r.Id);

            return true;
        } 

        ///  
        /// Deletes any relationship to the given signature from the signature origin 
        /// 
        /// relationship from origin 
        /// signatureUri
        /// true
        private bool DeleteRelationshipToSignature(PackageRelationship r, Object signatureUri)
        { 
            Uri uri = signatureUri as Uri;
            Debug.Assert(uri != null, "Improper use of delegate - context must be Uri"); 
 
            // don't resolve if external
            if (r.TargetMode != TargetMode.Internal) 
                throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));

            if (PackUriHelper.ComparePartUri(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri), uri) == 0)
            { 
                OriginPart.DeleteRelationship(r.Id);    // don't break early in case there are redundant relationships
            } 
 
            return true;
        } 

        private void DeleteOriginPart()
        {
            try 
            {
                // remove all relationships of the type "package-to-signature-origin" 
                SafeVisitRelationships(_container.GetRelationshipsByType(_originRelationshipType), 
                    DeleteRelationshipOfTypePackageToOriginVisitor);
 
                _container.DeletePart(_originPartName);
            }
            finally
            { 
                // reset state variables
                _originPartExists = false; 
                _originSearchConducted = true; 
                _originPart = null;
            } 
        }

        /// 
        /// Lookup the index of the signature object in the _signatures array by the name of the part 
        /// 
        /// name of the signature part 
        /// zero-based index or -1 if not found 
        private int GetSignatureIndex(Uri uri)
        { 
            EnsureSignatures();
            for (int i = 0; i < _signatures.Count; i++)
            {
                if (PackUriHelper.ComparePartUri(uri, _signatures[i].SignaturePart.Uri) == 0) 
                    return i;
            } 
            return -1;      // not found 
        }
 

        /// 
        /// Counts the number of signatures using the given certificate
        ///  
        /// certificate to inspect
        private int CertificatePartReferenceCount(Uri certificatePartUri) 
        { 
            // count the number of signatures that reference this certificate part
            int count = 0; 
            for (int i = 0; i < _signatures.Count; i++)
            {
                // for each signature, follow it's certificate link (if there) and compare the Uri
                if (_signatures[i].GetCertificatePart() != null) 
                {
                    // same Uri? 
                    if (PackUriHelper.ComparePartUri(certificatePartUri, _signatures[i].GetCertificatePart().Uri) == 0) 
                        ++count;
                } 
            }

            return count;
        } 

        ///  
        /// Generate guid-based signature name to reduce chances of conflict in merging scenarios 
        /// 
        ///  
        private Uri GenerateSignaturePartName()
        {
            return PackUriHelper.CreatePartUri(new Uri(_defaultSignaturePartNamePrefix +
                Guid.NewGuid().ToString(_guidStorageFormatString, (IFormatProvider)null) + _defaultSignaturePartNameExtension, UriKind.Relative)); 
        }
 
        // load signatures from container 
        private void EnsureSignatures()
        { 
            if (_signatures == null)
            {
                _signatures = new List();
 
                // no signatures if origin not found
                if (OriginPartExists()) 
                { 
                    // find all signatures from this origin (if any)
                    PackageRelationshipCollection relationships = _originPart.GetRelationshipsByType( 
                        _originToSignatureRelationshipType);

                    foreach (PackageRelationship r in relationships)
                    { 
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

                        Uri signaturePartName = PackUriHelper.ResolvePartUri(_originPart.Uri, r.TargetUri); 

                        // throw if part does not exist
                        if (!_container.PartExists(signaturePartName))
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption)); 

                        PackagePart signaturePart = _container.GetPart(signaturePartName); 
 
                        // ignore future signature types that we do not recognize
                        if (signaturePart.ValidatedContentType.AreTypeAndSubTypeEqual 
                            (XmlDigitalSignatureProcessor.ContentType))
                        {
                            // parse it
                            PackageDigitalSignature signature = new PackageDigitalSignature(this, signaturePart); 

                            // add to the list 
                            _signatures.Add(signature); 
                        }
                    } 
                }
            }
        }
 
        /// 
        /// Looks for part name of Origin by searching from the container root and following the metro origin part relationship 
        ///  
        /// side effect of assigning the _originPartName and _originPart if found
        /// true if found 
        private bool OriginPartExists()
        {
            // only search once
            if (!_originSearchConducted) 
            {
                try 
                { 
                    Debug.Assert(!_originPartExists, "Logic Error: If OriginPartExists, OriginSearchConducted should be true.");
                    PackageRelationshipCollection containerRelationships = _container.GetRelationshipsByType(_originRelationshipType); 
                    foreach (PackageRelationship r in containerRelationships)
                    {
                        // don't resolve if external
                        if (r.TargetMode != TargetMode.Internal) 
                            throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
 
                        // resolve target (may be relative) 
                        Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
 
                        // if part does not exist - we throw
                        if (!_container.PartExists(targetUri))
                            throw new FileFormatException(SR.Get(SRID.SignatureOriginNotFound));
 
                        PackagePart p = _container.GetPart(targetUri);
 
                        // inspect content type - ignore things we don't understand 
                        if (p.ValidatedContentType.AreTypeAndSubTypeEqual(_originPartContentType))
                        { 
                            // throw if more than one relationship to an origin part that we recognize
                            if (_originPartExists)
                                throw new FileFormatException(SR.Get(SRID.MultipleSignatureOrigins));
 
                            // overwrite default if some container is using some other name
                            _originPartName = targetUri; 
                            _originPart = p; 
                            _originPartExists = true;
                        } 
                    }
                }
                finally
                { 
                    _originSearchConducted = true;
                } 
            } 
            return _originPartExists;
        } 

        //------------------------------------------------------
        //
        //  Private Properties 
        //
        //----------------------------------------------------- 
        private bool ReadOnly 
        {
            get 
            {
                return (_container.FileOpenAccess == FileAccess.Read);
            }
        } 

        private PackagePart OriginPart 
        { 
            get
            { 
                if (_originPart == null)
                {
                    if (!OriginPartExists())
                    { 
                        // add if not found
                        _originPart = _container.CreatePart(_originPartName, _originPartContentType.ToString()); 
                        _container.CreateRelationship(_originPartName, TargetMode.Internal, _originRelationshipType); 
                    }
                } 

                return _originPart;
            }
        } 

        //------------------------------------------------------ 
        // 
        //  Private Fields
        // 
        //------------------------------------------------------
        private CertificateEmbeddingOption      _certificateEmbeddingOption;
        private Package                         _container;
        private IntPtr                          _parentWindow; 
        private static Uri _defaultOriginPartName = PackUriHelper.CreatePartUri(new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative));
        private Uri                             _originPartName = _defaultOriginPartName; 
        private PackagePart                     _originPart; 
        private String                          _hashAlgorithmString = _defaultHashAlgorithm;
        private String                          _signatureTimeFormat = XmlSignatureProperties.DefaultDateTimeFormat; 
        private List   _signatures;
        private Dictionary      _transformDictionary;
        private bool                            _originSearchConducted;             // don't look more than once for Origin part
        private bool                            _originPartExists;                  // was the part found? 
        private ReadOnlyCollection _signatureList;         // lazy-init cached return value for Signatures property
 
        private static readonly ContentType _originPartContentType = new ContentType("application/vnd.openxmlformats-package.digital-signature-origin"); 

        private static readonly String _guidStorageFormatString = @"N";     // N - simple format without adornments 
        private static readonly String _defaultHashAlgorithm = SignedXml.XmlDsigSHA1Url;
        private static readonly String _originRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
        private static readonly String _originToSignatureRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
        private static readonly String _defaultSignaturePartNamePrefix = "/package/services/digital-signature/xml-signature/"; 
        private static readonly String _defaultSignaturePartNameExtension = ".psdsxs";
        #endregion Private Members 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK