Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / System / IO / Packaging / PackageDigitalSignatureManager.cs / 1305600 / PackageDigitalSignatureManager.cs
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// This class provides api's to add/remove/verify signatures on an MMCF container.
//
// History:
// 03/22/2004: BruceMac: Initial Implementation
//
//-----------------------------------------------------------------------------
// Allow use of presharp warning numbers [6506] unknown to the compiler
#pragma warning disable 1634, 1691
using System;
using System.Collections.Generic;
using System.Windows; // For Exception strings - SRID
using System.Text; // for StringBuilder
using System.Diagnostics; // for Assert
using System.Security; // for SecurityCritical tag
using System.Security.Permissions; // for LinkDemand
using System.Security.Cryptography.Xml; // for SignedXml
using System.Security.Cryptography.X509Certificates; // for X509Certificate
using MS.Internal.IO.Packaging; // for internal helpers
using System.Collections.ObjectModel; // for ReadOnlyCollection<>
using MS.Internal; // for ContentType
using MS.Internal.WindowsBase;
namespace System.IO.Packaging
{
///
/// Options for storing the signing Certificate
///
public enum CertificateEmbeddingOption : int
{
///
/// Embed certificate in its own PackagePart (or share if same cert already exists)
///
InCertificatePart = 0, // embed the certificate in its own, possibly-shared part
///
/// Embed certificate within the signature PackagePart
///
InSignaturePart = 1, // embed the certificate within the signature
///
/// Do not embed
///
NotEmbedded = 2, // do not embed the certificate at all
}
///
/// Type of the handler that is invoked if signature validation is non-success.
///
/// signature
/// event arguments - containing the result
/// true to continue verifying other signatures, false to abandon effort
public delegate void InvalidSignatureEventHandler(object sender, SignatureVerificationEventArgs e);
///
/// Signature Verification Event Args - information about a verification event
///
public class SignatureVerificationEventArgs : EventArgs
{
//-----------------------------------------------------
//
// Public Members
//
//-----------------------------------------------------
///
/// Signature being processed
///
public PackageDigitalSignature Signature
{
get
{
return _signature;
}
}
///
/// Result of Verification
///
public VerifyResult VerifyResult
{
get
{
return _result;
}
}
//------------------------------------------------------
//
// Internal Members
//
//-----------------------------------------------------
internal SignatureVerificationEventArgs(PackageDigitalSignature signature,
VerifyResult result)
{
// verify arguments
if (signature == null)
throw new ArgumentNullException("signature");
if (result < VerifyResult.Success || result > VerifyResult.NotSigned)
throw new System.ArgumentOutOfRangeException("result");
_signature = signature;
_result = result;
}
//------------------------------------------------------
//
// Private Members
//
//------------------------------------------------------
private PackageDigitalSignature _signature;
private VerifyResult _result;
}
///
/// PackageDigitalSignatureManager
///
public sealed class PackageDigitalSignatureManager
{
#region Public Members
//-----------------------------------------------------
//
// Public Events
//
//------------------------------------------------------
///
/// Event to subscribe to for signature validation activities
///
public event InvalidSignatureEventHandler InvalidSignatureEvent;
//-----------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
///
/// Does this container hold digital signatures?
///
/// true if signatures exist
/// this does not evaluate the signatures - they may be invalid even if this returns true
public bool IsSigned
{
get
{
EnsureSignatures();
return (_signatures.Count > 0);
}
}
///
/// Signatures in container
///
/// read only list of immutable signatures found in the container
public ReadOnlyCollection Signatures
{
get
{
// ensure signatures are loaded from origin
EnsureSignatures();
// Return a read-only collection referring to them.
// This list will be automatically updated when the underlying collection is changed.
if (_signatureList == null)
_signatureList = new ReadOnlyCollection(_signatures);
return _signatureList;
}
}
///
/// ContentType - Transform mapping dictionary
///
/// Dictionary of transform Uri's indexed by ContentType.
/// Contains a single transform to be applied
/// before hashing any Part encountered with that ContentType
public Dictionary TransformMapping
{
get
{
return _transformDictionary;
}
}
///
/// Handle of parent window to use when displaying certificate selection dialog
///
///
/// not necessary if certificates are provided in calls to sign
public IntPtr ParentWindow
{
get
{
return _parentWindow;
}
set
{
_parentWindow = value;
}
}
///
/// Hashalgorithm to use when creating/verifying signatures
///
///
/// defaults to SHA1
public String HashAlgorithm
{
get
{
return _hashAlgorithmString;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (value == String.Empty)
throw new ArgumentException(SR.Get(SRID.UnsupportedHashAlgorithm), "value");
_hashAlgorithmString = value;
}
}
///
/// How to embed certificates when Signing
///
///
public CertificateEmbeddingOption CertificateOption
{
get
{
return _certificateEmbeddingOption;
}
set
{
if ((value < CertificateEmbeddingOption.InCertificatePart) || (value > CertificateEmbeddingOption.NotEmbedded))
throw new ArgumentOutOfRangeException("value");
_certificateEmbeddingOption = value;
}
}
///
/// How to format the SignatureTime in new signatures
///
/// Legal formats specified in Opc book and reproduced here:
/// YYYY-MM-DDThh:mm:ss.sTZD
/// YYYY-MM-DDThh:mm:ssTZD
/// YYYY-MM-DDThh:mmTZD
/// YYYY-MM-DD
/// YYYY-MM
/// YYYY
///
/// where:
/// Y = year, M = month integer (leading zero), D = day integer (leading zero),
/// hh = 24hr clock hour
/// mm = minutes (leading zero)
/// ss = seconds (leading zero)
/// .s = tenths of a second
///
public String TimeFormat
{
get
{
return _signatureTimeFormat;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (XmlSignatureProperties.LegalFormat(value))
_signatureTimeFormat = value;
else
throw new FormatException(SR.Get(SRID.BadSignatureTimeFormatString));
}
}
//-----------------------------------------------------
//
// Public Fields
//
//------------------------------------------------------
///
/// Name of signature origin part
///
///
/// This value may vary by Package because the name is not formally defined. While this
/// implementation will generally use the same default value, the value returned by this property will reflect
/// whatever origin is already present in the current Package (if any) which may vary between implementations.
///
public Uri SignatureOrigin
{
get
{
OriginPartExists(); // force search for OriginPart in case it is different from default
return _originPartName;
}
}
///
/// Type of default signature origin relationship
///
///
static public String SignatureOriginRelationshipType
{
get
{
return _originRelationshipType;
}
}
///
/// Default hash algorithm
///
///
static public String DefaultHashAlgorithm
{
get
{
return _defaultHashAlgorithm;
}
}
//-----------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
///
/// Create a new PackageDigitalSignature manager
///
/// container to work with
/// based on the default origin
/// package is null
public PackageDigitalSignatureManager(Package package)
{
if (package == null)
throw new ArgumentNullException("package");
_parentWindow = IntPtr.Zero;
_container = package;
// initialize the transform dictionary with defaults
_transformDictionary = new Dictionary(4);
_transformDictionary[PackagingUtilities.RelationshipPartContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl; // relationship parts
_transformDictionary[XmlDigitalSignatureProcessor.ContentType.ToString()] = SignedXml.XmlDsigC14NTransformUrl; // xml signature
}
#region Sign
///
/// Sign - prompts for certificate and embeds it
///
/// list of parts to sign
/// Set ParentWindow before this call if you want to make the certificate
/// selection dialog modal to a particular window. Does not prompt for certificates if none could be located in the default certificate store.
/// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
public PackageDigitalSignature Sign(IEnumerable parts)
{
X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
if (certificate == null)
return null;
else
return Sign(parts, certificate);
}
///
/// Sign - certificate provided by caller
///
/// list of parts to sign
/// signer's certificate
public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate)
{
// create unique signature name
return Sign(parts, certificate, null);
}
///
/// Sign - certificate provided by caller
///
/// list of parts to sign - may be empty or null
/// signer's certificate
/// relationshipSelectors that hold information about
/// the relationships to be signed - may be empty or null
/// one of parts or relationships must be non-null and contain at least a single entry
public PackageDigitalSignature Sign(IEnumerable parts, X509Certificate certificate, IEnumerable relationshipSelectors)
{
// use default signature Id
return Sign(parts, certificate, relationshipSelectors, XTable.Get(XTable.ID.OpcSignatureAttrValue));
}
///
/// Sign - certificate provided by caller
///
/// list of parts to sign - may be empty or null
/// signer's certificate
/// relationshipSelectors that hold information about
/// the relationships to be signed - may be empty or null
/// id for the new Signature - may be empty or null
/// one of parts or relationships must be non-null and contain at least a single entry
public PackageDigitalSignature Sign(
IEnumerable parts,
X509Certificate certificate,
IEnumerable relationshipSelectors,
String signatureId)
{
// Cannot both be null - need to check here because the similar check in the super-overload cannot
// distinguish to this level.
if (parts == null && relationshipSelectors == null)
{
throw new ArgumentException(SR.Get(SRID.NothingToSign));
}
return Sign(parts, certificate, relationshipSelectors, signatureId, null, null);
}
///
/// Sign - caller specifies custom "Object" and/or SignedInfo "Reference" tags
///
/// list of parts to sign - may be empty or null
/// signer's certificate
/// relationshipSelectors that hold information about
/// the relationships to be signed - may be empty or null
/// id for the new Signature - may be empty or null
/// references to custom object tags. The DigestMethod on each
/// Reference will be ignored. The signature will use the globally defined HashAlgorithm
/// obtained from the current value of the HashAlgorithm property.
/// objects (signed or not)
/// Thrown if any TransformMapping
/// defines an empty or null transform for the ContentType of any Part being signed or if an unknown
/// transform is encountered.
/// Thrown if signatureId is non-null and violates the
/// Xml Id schema (essentially - no leading digit is allowed).
/// One of parts, relationships, signatureObjects and objectReferences must be
/// non-null and contain at least a single entry.
/// This and every other Sign overload makes use of the current state of the TransformMapping
/// dictionary which defines a Transform to apply based on ContentType. The Opc specification
/// only currently allows for two legal Transform algorithms: C14 and C14N.
/// Note that the w3c Xml Signature standard does not allow for empty Manifest tags.
/// Because the Opc specification requires the existence of a Package-specific Object
/// tag and further specifies that this Object tag contain a Manifest and SignatureProperties
/// tags, it follows that this Manifest tag must include at least one Reference tag.
/// This means that every signature include at least one of a Part to sign (non-empty parts tag)
/// or a Relationship to sign (non-empty relationshipSelectors) even if such a signature
/// is only destined to sign signatureObjects and/or objectReferences.
/// This overload provides support for generation of Xml signatures that require custom
/// Object tags. For any provided Object tag to be signed, a corresponding Reference
/// tag must be provided with a Uri that targets the Object tag using local fragment
/// syntax. If the object had an ID of "myObject" the Uri on the Reference would
/// be "#myObject". For unsigned objects, no reference is required.
///
/// Critical: calls X509Certificate.Handle which LinkDemands
/// PublicOK: we don't store or return the handle
///
[SecurityCritical]
public PackageDigitalSignature Sign(
IEnumerable parts,
X509Certificate certificate,
IEnumerable relationshipSelectors,
String signatureId,
IEnumerable signatureObjects,
IEnumerable objectReferences)
{
if (ReadOnly)
throw new InvalidOperationException(SR.Get(SRID.CannotSignReadOnlyFile));
VerifySignArguments(parts, certificate, relationshipSelectors, signatureId, signatureObjects, objectReferences);
// substitute default id if none given
if ((signatureId == null) || (signatureId == String.Empty))
{
signatureId = "packageSignature"; // default
}
// Make sure the list reflects what's in the package.
// Do this before adding the new signature part because we don't want it included until it
// is fully formed (and delaying the add saves us having to remove it in case there is an
// error during the Sign call).
EnsureSignatures();
Uri newSignaturePartName = GenerateSignaturePartName();
if (_container.PartExists(newSignaturePartName))
throw new ArgumentException(SR.Get(SRID.DuplicateSignature));
// Pre-create origin part if it does not already exist.
// Do this before signing to allow for signing the package relationship part (because a Relationship
// is added from the Package to the Origin part by this call) and the Origin Relationship part in case this is
// a Publishing signature and the caller wants the addition of more signatures to break this signature.
PackageRelationship relationshipToNewSignature = OriginPart.CreateRelationship(newSignaturePartName, TargetMode.Internal,
_originToSignatureRelationshipType);
_container.Flush(); // ensure the origin relationship part is persisted so that any signature will include this newest relationship
VerifyPartsExist(parts);
// sign the data and optionally embed the certificate
bool embedCertificateInSignaturePart = (_certificateEmbeddingOption == CertificateEmbeddingOption.InSignaturePart);
// convert cert to version2 - more functionality
X509Certificate2 exSigner = certificate as X509Certificate2;
if (exSigner == null)
exSigner = new X509Certificate2(certificate.Handle);
//PRESHARP: Parameter to this public method must be validated: A null-dereference can occur here.
// Parameter 'exSigner' to this public method must be validated: A null-dereference can occur here.
//This is a false positive as the checks above can gurantee no null dereference will occur
#pragma warning disable 6506
PackageDigitalSignature signature = null;
PackagePart newSignaturePart = null;
try
{
// create the new part
newSignaturePart = _container.CreatePart(newSignaturePartName, XmlDigitalSignatureProcessor.ContentType.ToString());
// do the actual signing - only Xml signatures currently supported
signature = XmlDigitalSignatureProcessor.Sign(this, newSignaturePart, parts, relationshipSelectors, exSigner, signatureId, embedCertificateInSignaturePart,
signatureObjects, objectReferences);
}
catch (InvalidOperationException)
{
// bad hash algorithm - revert changes
// guarantees proper cleanup including removal of Origin if appropriate
// Note: _signatures.Count reflects the number of signatures that were
// existing before this sign method was called. So we want to leave those
// untouched and clean up what we added in this method prior to the
// exception. If the count is zero, we will also delete the origin part.
InternalRemoveSignature(newSignaturePartName, _signatures.Count);
_container.Flush(); // actually persist the revert
throw;
}
catch (System.IO.IOException)
{
// failure to open part - revert changes
// guarantees proper cleanup including removal of Origin if appropriate
// Note: _signatures.Count reflects the number of signatures that were
// existing before this sign method was called. So we want to leave those
// untouched and clean up what we added in this method prior to the
// exception. If the count is zero, we will also delete the origin part.
InternalRemoveSignature(newSignaturePartName, _signatures.Count);
_container.Flush(); // actually persist the revert
throw;
}
catch (System.Security.Cryptography.CryptographicException)
{
// failure to sign - revert changes
// guarantees proper cleanup including removal of Origin if appropriate
// Note: _signatures.Count reflects the number of signatures that were
// existing before this sign method was called. So we want to leave those
// untouched and clean up what we added in this method prior to the
// exception. If the count is zero, we will also delete the origin part.
InternalRemoveSignature(newSignaturePartName, _signatures.Count);
_container.Flush(); // actually persist the revert
throw;
}
// add to the list
_signatures.Add(signature);
// embed certificate if called for
if (_certificateEmbeddingOption == CertificateEmbeddingOption.InCertificatePart)
{
// create the cert part
// auto-generate a certificate name - will be the same for the same certificate
Uri certificatePartName = PackUriHelper.CreatePartUri(new Uri(
CertificatePart.PartNamePrefix + exSigner.SerialNumber + CertificatePart.PartNameExtension, UriKind.Relative));
// create the serialization helper class (side-effect of creating or opening the part)
CertificatePart certPart = new CertificatePart(_container, certificatePartName);
certPart.SetCertificate(exSigner);
// establish a relationship
newSignaturePart.CreateRelationship(certificatePartName, TargetMode.Internal, CertificatePart.RelationshipType);
signature.SetCertificatePart(certPart);
}
#pragma warning restore 6506
_container.Flush();
// return to caller in case they need it
return signature;
}
#endregion
#region CounterSign
///
/// CounterSign - prompts for certificate and embeds it based on current CertificateEmbeddingOption
///
/// Set ParentWindow before this call if you want to make the certificate
/// selection dialog modal to a particular window. Does not present the dialog if no suitable certificate
/// could be found in the default certificate store.
/// Signs all existing signature parts so that any change to these part(s) will invalidate the
/// returned signature.
/// Cannot CounterSign an unsigned package.
/// null if no certificate could be located, or if the user cancels from the certificate selection dialog.
public PackageDigitalSignature Countersign()
{
// Counter-sign makes no sense if we are not already signed
// Check before asking for certificate
if (!IsSigned)
throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));
// prompt for certificate
X509Certificate certificate = PromptForSigningCertificate(ParentWindow);
if (certificate == null)
return null;
else
return Countersign(certificate);
}
///
/// CounterSign - certificate provided
///
/// signer's certificate
/// Cannot CounterSign an unsigned package.
/// certificate must be non-null.
/// Signs all existing signature parts so that any change to these part(s) will invalidate the
/// returned signature.
public PackageDigitalSignature Countersign(X509Certificate certificate)
{
if (certificate == null)
throw new ArgumentNullException("certificate");
// Counter-sign makes no sense if we are not already signed
// Check before asking for certificate
if (!IsSigned)
throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));
// sign all existing signatures
List signatures = new List(_signatures.Count);
for (int i = 0; i < _signatures.Count; i++)
{
signatures.Add(_signatures[i].SignaturePart.Uri);
}
// sign
return Sign(signatures, certificate);
}
///
/// CounterSign - signature part name(s) specified by caller
///
/// signer's certificate
/// signature parts to sign
/// Signs the given signature parts so that any change to these part(s) will invalidate the
/// returned signature.
/// Cannot CounterSign an unsigned package.
/// signatures must be non-empty and cannot refer to parts other than signature parts.
/// Both arguments must be non-null.
public PackageDigitalSignature Countersign(X509Certificate certificate, IEnumerable signatures)
{
if (certificate == null)
throw new ArgumentNullException("certificate");
if (signatures == null)
throw new ArgumentNullException("signatures");
// Counter-sign makes no sense if we are not already signed
if (!IsSigned)
throw new InvalidOperationException(SR.Get(SRID.NoCounterSignUnsignedContainer));
// Restrict signatures to be actual signature part references
foreach (Uri uri in signatures)
{
PackagePart part = _container.GetPart(uri);
if (!part.ValidatedContentType.AreTypeAndSubTypeEqual(XmlDigitalSignatureProcessor.ContentType))
throw new ArgumentException(SR.Get(SRID.CanOnlyCounterSignSignatureParts, signatures));
}
return Sign(signatures, certificate);
}
#endregion
///
/// verify all signatures - calls verify on each signature
///
/// true to exit on first failure - false to continue
/// register for invalid signature events
public VerifyResult VerifySignatures(bool exitOnFailure)
{
VerifyResult result;
EnsureSignatures();
// signed?
if (_signatures.Count == 0)
result = VerifyResult.NotSigned;
else
{
// contract is to return a failure value, even if there are subsequent successes
// defaulting to success here simplifies the logic for this
result = VerifyResult.Success; // default
for (int i = 0; i < _signatures.Count; i++)
{
VerifyResult temp = _signatures[i].Verify();
if (temp != VerifyResult.Success)
{
result = temp; // note failure
if (InvalidSignatureEvent != null)
InvalidSignatureEvent(this, new SignatureVerificationEventArgs(_signatures[i], temp));
if (exitOnFailure)
break;
}
}
}
return result;
}
///
/// Remove a signature
///
/// signature to remove
/// Caller should call Package.Flush() in order to persist changes.
public void RemoveSignature(Uri signatureUri)
{
if (ReadOnly)
throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile));
if (signatureUri == null)
throw new ArgumentNullException("signatureUri");
// empty?
if (!IsSigned) // calls EnsureSignatures for us
return;
// find the signature
int index = GetSignatureIndex(signatureUri);
if (index < 0)
return;
try
{
Debug.Assert(index < _signatures.Count);
//After this signature is removed the total number of signatures remaining will
//be _signatures.Count - 1. If this count is zero, then additional clean up needs
//to be done, like removing the Origin part.
InternalRemoveSignature(signatureUri, _signatures.Count - 1 /*since we are deleting one*/);
// invalidate the signature itself
_signatures[index].Invalidate();
}
finally
{
_signatures.RemoveAt(index); // ensure it is actually removed from the list
}
}
///
/// Remove all signatures based on this origin
///
/// also removes all certificate parts and the signature origin. Caller must call Flush() to persist changes.
public void RemoveAllSignatures()
{
if (ReadOnly)
throw new InvalidOperationException(SR.Get(SRID.CannotRemoveSignatureFromReadOnlyFile));
EnsureSignatures();
try
{
// Remove via known traversal - required to find all signatures (we may not know all signature content-types).
for (int i = 0; i < _signatures.Count; i++)
{
PackagePart p = _signatures[i].SignaturePart;
// Delete any Certificate part(s) targeted by this signature. We know that all of the
// reference counts will reach zero because we are removing all signatures.
foreach (PackageRelationship r in p.GetRelationshipsByType(CertificatePart.RelationshipType))
{
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
continue; // fail silently
_container.DeletePart(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri)); // will not throw if part not found
}
// delete signature part
_container.DeletePart(p.Uri);
// invalidate the signature itself
_signatures[i].Invalidate();
}
DeleteOriginPart();
}
finally
{
// update internal variables
_signatures.Clear();
}
}
///
/// Obtain the PackageDigitalSignature referred to by the given Uri
///
/// ID obtained from a PackageDigitalSignature object
/// null if signature not found
public PackageDigitalSignature GetSignature(Uri signatureUri)
{
if (signatureUri == null)
throw new ArgumentNullException("signatureUri");
int index = GetSignatureIndex(signatureUri);
if (index < 0)
return null;
else
{
Debug.Assert(index < _signatures.Count);
return _signatures[index];
}
}
///
/// Verify Certificate
///
/// certificate to inspect
/// certificate is invalid but the error code is not recognized
/// the first error encountered when inspecting the certificate chain or NoError if the certificate is valid
///
/// Critical - The X509Chain.Build method has a LinkDemand for Unrestricted.
/// PublicOK � VerifyCertificate has LinkDemand.
///
[SecurityCritical]
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
public static X509ChainStatusFlags VerifyCertificate(X509Certificate certificate)
{
if (certificate == null)
throw new ArgumentNullException("certificate");
X509ChainStatusFlags status = X509ChainStatusFlags.NoError;
// build the certificate chain
X509Chain chain = new X509Chain();
bool valid = chain.Build(new X509Certificate2(certificate.Handle));
// inspect the results
if (!valid)
{
X509ChainStatus[] chainStatus = chain.ChainStatus;
for (int i = 0; i < chainStatus.Length; i++)
{
status |= chainStatus[i].Status;
}
}
return status;
}
#endregion
//------------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
///
/// Get package - used by DigitalSignatureProcessors
///
internal Package Package
{
get
{
return _container;
}
}
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
///
/// PromptForSigningCertificate - invoked from Sign overloads if certificate is not provided by caller
///
///
/// null if user cancels or no certificate could be located
///
/// Critical: calls X509Certificate2UI.SelectFromCollection which LinkDemands
/// TreatAsSafe: UI can only display existing certificates, no spoofing
///
[SecurityCritical, SecurityTreatAsSafe]
static internal X509Certificate PromptForSigningCertificate(IntPtr hwndParent)
{
X509Certificate2 X509cert = null;
// look for appropriate certificates
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
// narrow down the choices
// timevalid
collection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
// intended for signing (or no intent specified)
collection = collection.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false);
// remove certs that don't have private key
// work backward so we don't disturb the enumeration
for (int i = collection.Count - 1; i >= 0; i--)
{
if (!collection[i].HasPrivateKey)
{
collection.RemoveAt(i);
}
}
// any suitable certificates available?
if (collection.Count > 0)
{
// ask user to select
collection = X509Certificate2UI.SelectFromCollection(collection, SR.Get(SRID.CertSelectionDialogTitle), SR.Get(SRID.CertSelectionDialogMessage), X509SelectionFlag.SingleSelection, hwndParent);
if (collection.Count > 0)
{
X509cert = collection[0]; // return the first one
}
}
return X509cert;
}
#region Private Members
//-----------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
///
/// Predicate for use with List.Exists()
///
private class StringMatchPredicate
{
public StringMatchPredicate(String id)
{
_id = id;
}
public bool Match(String id)
{
return (String.CompareOrdinal(_id, id) == 0);
}
private string _id;
}
///
/// Verify Parts Exist before signing
///
///
/// This call must be done after the signature Origin has been created to allow for
/// callers to sign an Origin (or it's relationship part) for the first signature in the package.
private void VerifyPartsExist(IEnumerable parts)
{
// check for missing parts
if (parts != null)
{
foreach (Uri partUri in parts)
{
if (!_container.PartExists(partUri))
{
// delete origin part if it was created and this is the first signature
if (_signatures.Count == 0)
DeleteOriginPart();
throw new ArgumentException(SR.Get(SRID.PartToSignMissing), "parts");
}
}
}
}
///
/// Verifies arguments to Sign() method - sub-function to reduce complexity in Sign() logic
///
///
///
///
///
///
///
private void VerifySignArguments(IEnumerable parts,
X509Certificate certificate,
IEnumerable relationshipSelectors,
String signatureId,
IEnumerable signatureObjects,
IEnumerable objectReferences)
{
if (certificate == null)
throw new ArgumentNullException("certificate");
// Check for empty collections in order to provide negative feedback as soon as possible.
if (EnumeratorEmptyCheck(parts) && EnumeratorEmptyCheck(relationshipSelectors)
&& EnumeratorEmptyCheck(signatureObjects) && EnumeratorEmptyCheck(objectReferences))
throw new ArgumentException(SR.Get(SRID.NothingToSign));
// check for illegal and/or duplicate id's in signatureObjects
if (signatureObjects != null)
{
List ids = new List();
foreach (DataObject obj in signatureObjects)
{
// ensure they don't duplicate the reserved one
if (String.CompareOrdinal(obj.Id, XTable.Get(XTable.ID.OpcAttrValue)) == 0)
throw new ArgumentException(SR.Get(SRID.SignaturePackageObjectTagMustBeUnique), "signatureObjects");
// check for duplicates
//if (ids.Contains(obj.Id))
if (ids.Exists(new StringMatchPredicate(obj.Id).Match))
throw new ArgumentException(SR.Get(SRID.SignatureObjectIdMustBeUnique), "signatureObjects");
else
ids.Add(obj.Id);
}
}
// ensure id is legal Xml id
if ((signatureId != null) && (signatureId != String.Empty))
{
try
{
// An XSD ID is an NCName that is unique.
System.Xml.XmlConvert.VerifyNCName(signatureId);
}
catch (System.Xml.XmlException xmlException)
{
throw new ArgumentException(SR.Get(SRID.NotAValidXmlIdString, signatureId), "signatureId", xmlException);
}
}
}
///
/// Returns true if the given enumerator is null or empty
///
/// may be null
/// true if enumerator is empty or null
private bool EnumeratorEmptyCheck(System.Collections.IEnumerable enumerable)
{
if (enumerable == null)
return true; // null means empty
// see if it's really a collection as this is more efficient than enumerating
System.Collections.ICollection collection = enumerable as System.Collections.ICollection;
if (collection != null)
{
return (collection.Count == 0);
}
else
{
// not a collection - do things the hard way
foreach (Object o in enumerable)
{
return false; // if we get here - we're not empty
}
return true; // empty
}
}
///
/// Remove a signature - helper method
///
/// signature to remove
/// number of signatures that will remain
/// after the remove operation. If this count becomes zero, then we can remove the
/// origin part also from the package as there will be no remaining signatures
/// in the package.
/// Caller should call Package.Flush() in order to persist changes.
private void InternalRemoveSignature(Uri signatureUri, int countOfSignaturesRemaining)
{
Debug.Assert(signatureUri != null);
Debug.Assert(countOfSignaturesRemaining >= 0);
// Remove origin if this operation will have removed the last signature in order to conform with Metro specification.
// This will remove all relationships too so the code in the "else" clause becomes redundant and we can skip it.
if (countOfSignaturesRemaining == 0)
{
DeleteOriginPart();
}
else // there will be at least a single signature left after this remove, so we need to be more delicate in our surgery
{
SafeVisitRelationships(
OriginPart.GetRelationshipsByType(_originToSignatureRelationshipType),
DeleteRelationshipToSignature, signatureUri);
}
// delete the cert (if any) if it's reference count will become zero
SafeVisitRelationships(_container.GetPart(signatureUri).GetRelationshipsByType(CertificatePart.RelationshipType),
DeleteCertificateIfReferenceCountBecomesZeroVisitor);
// delete the signature part
_container.DeletePart(signatureUri);
}
// return true to continue
private delegate bool RelationshipOperation(PackageRelationship r, Object context);
///
/// Visit relationships without disturbing the PackageRelationshipCollection iterator
///
/// collection of relationships to visit
/// function to call with each relationship in the list
private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit)
{
SafeVisitRelationships(relationships, visit, null);
}
///
/// Visit relationships without disturbing the PackageRelationshipCollection iterator
///
/// collection of relationships to visit
/// function to call with each relationship in the list
/// context object - may be null
private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit, Object context)
{
// make a local copy that will not be invalidated by any activity of the visitor function
List relationshipsToVisit = new List(relationships);
// now invoke the delegate for each member
for (int i = 0; i < relationshipsToVisit.Count; i++)
{
// exit if visitor wants us to
if (!visit(relationshipsToVisit[i], context))
break;
}
}
///
/// Removes the certificate associated with the given signature if removing the signature would leave the
/// certificate part orphaned.
///
private bool DeleteCertificateIfReferenceCountBecomesZeroVisitor(PackageRelationship r, Object context)
{
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
Uri certificatePartName = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
if (CertificatePartReferenceCount(certificatePartName) == 1) // we are part of the calculation so one is the magic number
_container.DeletePart(certificatePartName); // will not throw if part not found
return true;
}
///
/// Deletes any relationship that is of the type that relates a Package to the Digital Signature Origin
///
///
///
///
private bool DeleteRelationshipOfTypePackageToOriginVisitor(PackageRelationship r, Object context)
{
Debug.Assert(Uri.Compare(r.SourceUri,
PackUriHelper.PackageRootUri,
UriComponents.SerializationInfoString,
UriFormat.UriEscaped,
StringComparison.Ordinal) == 0,
"Logic Error: This visitor should only be called with relationships from the Package itself");
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
if (PackUriHelper.ComparePartUri(targetUri, _originPartName) == 0)
_container.DeleteRelationship(r.Id);
return true;
}
///
/// Deletes any relationship to the given signature from the signature origin
///
/// relationship from origin
/// signatureUri
/// true
private bool DeleteRelationshipToSignature(PackageRelationship r, Object signatureUri)
{
Uri uri = signatureUri as Uri;
Debug.Assert(uri != null, "Improper use of delegate - context must be Uri");
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
if (PackUriHelper.ComparePartUri(PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri), uri) == 0)
{
OriginPart.DeleteRelationship(r.Id); // don't break early in case there are redundant relationships
}
return true;
}
private void DeleteOriginPart()
{
try
{
// remove all relationships of the type "package-to-signature-origin"
SafeVisitRelationships(_container.GetRelationshipsByType(_originRelationshipType),
DeleteRelationshipOfTypePackageToOriginVisitor);
_container.DeletePart(_originPartName);
}
finally
{
// reset state variables
_originPartExists = false;
_originSearchConducted = true;
_originPart = null;
}
}
///
/// Lookup the index of the signature object in the _signatures array by the name of the part
///
/// name of the signature part
/// zero-based index or -1 if not found
private int GetSignatureIndex(Uri uri)
{
EnsureSignatures();
for (int i = 0; i < _signatures.Count; i++)
{
if (PackUriHelper.ComparePartUri(uri, _signatures[i].SignaturePart.Uri) == 0)
return i;
}
return -1; // not found
}
///
/// Counts the number of signatures using the given certificate
///
/// certificate to inspect
private int CertificatePartReferenceCount(Uri certificatePartUri)
{
// count the number of signatures that reference this certificate part
int count = 0;
for (int i = 0; i < _signatures.Count; i++)
{
// for each signature, follow it's certificate link (if there) and compare the Uri
if (_signatures[i].GetCertificatePart() != null)
{
// same Uri?
if (PackUriHelper.ComparePartUri(certificatePartUri, _signatures[i].GetCertificatePart().Uri) == 0)
++count;
}
}
return count;
}
///
/// Generate guid-based signature name to reduce chances of conflict in merging scenarios
///
///
private Uri GenerateSignaturePartName()
{
return PackUriHelper.CreatePartUri(new Uri(_defaultSignaturePartNamePrefix +
Guid.NewGuid().ToString(_guidStorageFormatString, (IFormatProvider)null) + _defaultSignaturePartNameExtension, UriKind.Relative));
}
// load signatures from container
private void EnsureSignatures()
{
if (_signatures == null)
{
_signatures = new List();
// no signatures if origin not found
if (OriginPartExists())
{
// find all signatures from this origin (if any)
PackageRelationshipCollection relationships = _originPart.GetRelationshipsByType(
_originToSignatureRelationshipType);
foreach (PackageRelationship r in relationships)
{
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
Uri signaturePartName = PackUriHelper.ResolvePartUri(_originPart.Uri, r.TargetUri);
// throw if part does not exist
if (!_container.PartExists(signaturePartName))
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
PackagePart signaturePart = _container.GetPart(signaturePartName);
// ignore future signature types that we do not recognize
if (signaturePart.ValidatedContentType.AreTypeAndSubTypeEqual
(XmlDigitalSignatureProcessor.ContentType))
{
// parse it
PackageDigitalSignature signature = new PackageDigitalSignature(this, signaturePart);
// add to the list
_signatures.Add(signature);
}
}
}
}
}
///
/// Looks for part name of Origin by searching from the container root and following the metro origin part relationship
///
/// side effect of assigning the _originPartName and _originPart if found
/// true if found
private bool OriginPartExists()
{
// only search once
if (!_originSearchConducted)
{
try
{
Debug.Assert(!_originPartExists, "Logic Error: If OriginPartExists, OriginSearchConducted should be true.");
PackageRelationshipCollection containerRelationships = _container.GetRelationshipsByType(_originRelationshipType);
foreach (PackageRelationship r in containerRelationships)
{
// don't resolve if external
if (r.TargetMode != TargetMode.Internal)
throw new FileFormatException(SR.Get(SRID.PackageSignatureCorruption));
// resolve target (may be relative)
Uri targetUri = PackUriHelper.ResolvePartUri(r.SourceUri, r.TargetUri);
// if part does not exist - we throw
if (!_container.PartExists(targetUri))
throw new FileFormatException(SR.Get(SRID.SignatureOriginNotFound));
PackagePart p = _container.GetPart(targetUri);
// inspect content type - ignore things we don't understand
if (p.ValidatedContentType.AreTypeAndSubTypeEqual(_originPartContentType))
{
// throw if more than one relationship to an origin part that we recognize
if (_originPartExists)
throw new FileFormatException(SR.Get(SRID.MultipleSignatureOrigins));
// overwrite default if some container is using some other name
_originPartName = targetUri;
_originPart = p;
_originPartExists = true;
}
}
}
finally
{
_originSearchConducted = true;
}
}
return _originPartExists;
}
//------------------------------------------------------
//
// Private Properties
//
//-----------------------------------------------------
private bool ReadOnly
{
get
{
return (_container.FileOpenAccess == FileAccess.Read);
}
}
private PackagePart OriginPart
{
get
{
if (_originPart == null)
{
if (!OriginPartExists())
{
// add if not found
_originPart = _container.CreatePart(_originPartName, _originPartContentType.ToString());
_container.CreateRelationship(_originPartName, TargetMode.Internal, _originRelationshipType);
}
}
return _originPart;
}
}
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
private CertificateEmbeddingOption _certificateEmbeddingOption;
private Package _container;
private IntPtr _parentWindow;
private static Uri _defaultOriginPartName = PackUriHelper.CreatePartUri(new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative));
private Uri _originPartName = _defaultOriginPartName;
private PackagePart _originPart;
private String _hashAlgorithmString = _defaultHashAlgorithm;
private String _signatureTimeFormat = XmlSignatureProperties.DefaultDateTimeFormat;
private List _signatures;
private Dictionary _transformDictionary;
private bool _originSearchConducted; // don't look more than once for Origin part
private bool _originPartExists; // was the part found?
private ReadOnlyCollection _signatureList; // lazy-init cached return value for Signatures property
private static readonly ContentType _originPartContentType = new ContentType("application/vnd.openxmlformats-package.digital-signature-origin");
private static readonly String _guidStorageFormatString = @"N"; // N - simple format without adornments
private static readonly String _defaultHashAlgorithm = SignedXml.XmlDsigSHA1Url;
private static readonly String _originRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
private static readonly String _originToSignatureRelationshipType = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
private static readonly String _defaultSignaturePartNamePrefix = "/package/services/digital-signature/xml-signature/";
private static readonly String _defaultSignaturePartNameExtension = ".psdsxs";
#endregion Private Members
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PanelStyle.cs
- Color.cs
- _OverlappedAsyncResult.cs
- Crypto.cs
- OrderedDictionaryStateHelper.cs
- FormParameter.cs
- ApplicationGesture.cs
- TimeoutValidationAttribute.cs
- HttpTransportElement.cs
- ExeConfigurationFileMap.cs
- TextureBrush.cs
- PenThread.cs
- ImageCreator.cs
- ToolboxItemSnapLineBehavior.cs
- DbTransaction.cs
- OperatingSystem.cs
- NativeMethods.cs
- ZipArchive.cs
- Operator.cs
- SocketCache.cs
- StateMachineSubscription.cs
- SetterTriggerConditionValueConverter.cs
- TaiwanCalendar.cs
- ServicePointManagerElement.cs
- formatter.cs
- DragEventArgs.cs
- QueryReaderSettings.cs
- DbMetaDataFactory.cs
- TabRenderer.cs
- NavigationExpr.cs
- FlowchartDesigner.Helpers.cs
- ExtensibleClassFactory.cs
- SqlDataSourceCustomCommandPanel.cs
- XmlSchemaAnnotation.cs
- HtmlElementCollection.cs
- Int32AnimationUsingKeyFrames.cs
- DocumentPageHost.cs
- FontUnit.cs
- MarkupExtensionSerializer.cs
- SortedDictionary.cs
- SqlBooleanMismatchVisitor.cs
- Char.cs
- SiteMapNodeItemEventArgs.cs
- SplitterCancelEvent.cs
- InheritanceRules.cs
- COM2TypeInfoProcessor.cs
- EventProxy.cs
- HierarchicalDataSourceControl.cs
- ResourceDisplayNameAttribute.cs
- QuaternionValueSerializer.cs
- SafeHandles.cs
- DetailsView.cs
- ReferenceEqualityComparer.cs
- TemplateNameScope.cs
- ImagingCache.cs
- WriteFileContext.cs
- DiscoveryDocumentReference.cs
- ResolvedKeyFrameEntry.cs
- MethodBuilder.cs
- _Win32.cs
- XmlSecureResolver.cs
- CellTreeNode.cs
- xmlsaver.cs
- Invariant.cs
- DocumentGridContextMenu.cs
- formatter.cs
- UnmanagedMemoryAccessor.cs
- XPathDocumentBuilder.cs
- PageRanges.cs
- CmsUtils.cs
- OrderedDictionary.cs
- RegexMatchCollection.cs
- XMLDiffLoader.cs
- Attributes.cs
- _ReceiveMessageOverlappedAsyncResult.cs
- StreamGeometry.cs
- SafeSystemMetrics.cs
- ProviderCommandInfoUtils.cs
- Int32KeyFrameCollection.cs
- InkCollectionBehavior.cs
- DeflateStream.cs
- PageBuildProvider.cs
- WizardStepBase.cs
- SystemFonts.cs
- ObjectDataSourceSelectingEventArgs.cs
- COM2ColorConverter.cs
- DataGridItem.cs
- XamlFxTrace.cs
- WhitespaceRuleReader.cs
- WebPart.cs
- Vector3DAnimation.cs
- FilterEventArgs.cs
- EventRoute.cs
- OleDbParameter.cs
- TypeName.cs
- DataContractSet.cs
- FormattedTextSymbols.cs
- RemoteArgument.cs
- MouseOverProperty.cs
- DataStorage.cs