PackageDigitalSignatureManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / System / IO / Packaging / PackageDigitalSignatureManager.cs / 1 / 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

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

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