|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.IdentityModel
{
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
sealed class SignedXml : ISignatureValueSecurityElement
{
internal const string DefaultPrefix = XmlSignatureStrings.Prefix;
SecurityTokenSerializer tokenSerializer;
readonly Signature signature;
TransformFactory transformFactory;
DictionaryManager dictionaryManager;
public SignedXml(DictionaryManager dictionaryManager, SecurityTokenSerializer tokenSerializer)
: this(new StandardSignedInfo(dictionaryManager), dictionaryManager, tokenSerializer)
{
}
internal SignedXml(SignedInfo signedInfo, DictionaryManager dictionaryManager, SecurityTokenSerializer tokenSerializer)
{
if (signedInfo == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("signedInfo"));
}
if (dictionaryManager == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dictionaryManager");
}
if (tokenSerializer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenSerializer");
}
this.transformFactory = StandardTransformFactory.Instance;
this.tokenSerializer = tokenSerializer;
this.signature = new Signature(this, signedInfo);
this.dictionaryManager = dictionaryManager;
}
public bool HasId
{
get { return true; }
}
public string Id
{
get { return this.signature.Id; }
set { this.signature.Id = value; }
}
public SecurityTokenSerializer SecurityTokenSerializer
{
get { return this.tokenSerializer; }
}
public Signature Signature
{
get { return this.signature; }
}
public TransformFactory TransformFactory
{
get { return this.transformFactory; }
set { this.transformFactory = value; }
}
void ComputeSignature(HashAlgorithm hash, AsymmetricSignatureFormatter formatter, string signatureMethod)
{
this.Signature.SignedInfo.ComputeReferenceDigests();
this.Signature.SignedInfo.ComputeHash(hash);
byte[] signature;
if (SecurityUtils.RequiresFipsCompliance && signatureMethod == SecurityAlgorithms.RsaSha256Signature)
{
// This is to avoid the RSAPKCS1SignatureFormatter.CreateSignature from using SHA256Managed (non-FIPS-Compliant).
// Hence we precompute the hash using SHA256CSP (FIPS compliant) and pass it to method.
// NOTE: RSAPKCS1SignatureFormatter does not understand SHA256CSP inherently and hence this workaround.
formatter.SetHashAlgorithm("SHA256");
signature = formatter.CreateSignature(hash.Hash);
}
else
{
signature = formatter.CreateSignature(hash);
}
this.Signature.SetSignatureValue(signature);
}
void ComputeSignature(KeyedHashAlgorithm hash)
{
this.Signature.SignedInfo.ComputeReferenceDigests();
this.Signature.SignedInfo.ComputeHash(hash);
byte[] signature = hash.Hash;
this.Signature.SetSignatureValue(signature);
}
public void ComputeSignature(SecurityKey signingKey)
{
string signatureMethod = this.Signature.SignedInfo.SignatureMethod;
SymmetricSecurityKey symmetricKey = signingKey as SymmetricSecurityKey;
if (symmetricKey != null)
{
using (KeyedHashAlgorithm algorithm = symmetricKey.GetKeyedHashAlgorithm(signatureMethod))
{
if (algorithm == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.UnableToCreateKeyedHashAlgorithm, symmetricKey, signatureMethod)));
}
ComputeSignature(algorithm);
}
}
else
{
AsymmetricSecurityKey asymmetricKey = signingKey as AsymmetricSecurityKey;
if (asymmetricKey == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.UnknownICryptoType, signingKey)));
}
using (HashAlgorithm hash = asymmetricKey.GetHashAlgorithmForSignature(signatureMethod))
{
if (hash == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.UnableToCreateHashAlgorithmFromAsymmetricCrypto, signatureMethod, asymmetricKey)));
}
AsymmetricSignatureFormatter formatter = asymmetricKey.GetSignatureFormatter(signatureMethod);
if (formatter == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.UnableToCreateSignatureFormatterFromAsymmetricCrypto, signatureMethod, asymmetricKey)));
}
ComputeSignature(hash, formatter, signatureMethod);
}
}
}
public void CompleteSignatureVerification()
{
this.Signature.SignedInfo.EnsureAllReferencesVerified();
}
public void EnsureDigestValidity(string id, object resolvedXmlSource)
{
this.Signature.SignedInfo.EnsureDigestValidity(id, resolvedXmlSource);
}
public bool EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource)
{
return this.Signature.SignedInfo.EnsureDigestValidityIfIdMatches(id, resolvedXmlSource);
}
public byte[] GetSignatureValue()
{
return this.Signature.GetSignatureBytes();
}
public void ReadFrom(XmlReader reader)
{
ReadFrom(XmlDictionaryReader.CreateDictionaryReader(reader));
}
public void ReadFrom(XmlDictionaryReader reader)
{
this.signature.ReadFrom(reader, this.dictionaryManager);
}
void VerifySignature(KeyedHashAlgorithm hash)
{
this.Signature.SignedInfo.ComputeHash(hash);
if (!CryptoHelper.FixedTimeEquals(hash.Hash, GetSignatureValue()))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.SignatureVerificationFailed)));
}
}
void VerifySignature(HashAlgorithm hash, AsymmetricSignatureDeformatter deformatter, string signatureMethod)
{
this.Signature.SignedInfo.ComputeHash(hash);
bool result;
if (SecurityUtils.RequiresFipsCompliance && signatureMethod == SecurityAlgorithms.RsaSha256Signature)
{
// This is to avoid the RSAPKCS1SignatureFormatter.VerifySignature from using SHA256Managed (non-FIPS-Compliant).
// Hence we precompute the hash using SHA256CSP (FIPS compliant) and pass it to method.
// NOTE: RSAPKCS1SignatureFormatter does not understand SHA256CSP inherently and hence this workaround.
deformatter.SetHashAlgorithm("SHA256");
result = deformatter.VerifySignature(hash.Hash, GetSignatureValue());
}
else
{
result = deformatter.VerifySignature(hash, GetSignatureValue());
}
if (!result)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.SignatureVerificationFailed)));
}
}
public void StartSignatureVerification(SecurityKey verificationKey)
{
string signatureMethod = this.Signature.SignedInfo.SignatureMethod;
SymmetricSecurityKey symmetricKey = verificationKey as SymmetricSecurityKey;
if (symmetricKey != null)
{
using (KeyedHashAlgorithm hash = symmetricKey.GetKeyedHashAlgorithm(signatureMethod))
{
if (hash == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.UnableToCreateKeyedHashAlgorithmFromSymmetricCrypto, signatureMethod, symmetricKey)));
}
VerifySignature(hash);
}
}
else
{
AsymmetricSecurityKey asymmetricKey = verificationKey as AsymmetricSecurityKey;
if (asymmetricKey == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UnknownICryptoType, verificationKey)));
}
using (HashAlgorithm hash = asymmetricKey.GetHashAlgorithmForSignature(signatureMethod))
{
if (hash == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.UnableToCreateHashAlgorithmFromAsymmetricCrypto, signatureMethod, asymmetricKey)));
}
AsymmetricSignatureDeformatter deformatter = asymmetricKey.GetSignatureDeformatter(signatureMethod);
if (deformatter == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.UnableToCreateSignatureDeformatterFromAsymmetricCrypto, signatureMethod, asymmetricKey)));
}
VerifySignature(hash, deformatter, signatureMethod);
}
}
}
public void WriteTo(XmlDictionaryWriter writer)
{
this.WriteTo(writer, this.dictionaryManager);
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
this.signature.WriteTo(writer, dictionaryManager);
}
}
sealed class Signature
{
SignedXml signedXml;
string id;
SecurityKeyIdentifier keyIdentifier;
string prefix = SignedXml.DefaultPrefix;
readonly SignatureValueElement signatureValueElement = new SignatureValueElement();
readonly SignedInfo signedInfo;
public Signature(SignedXml signedXml, SignedInfo signedInfo)
{
this.signedXml = signedXml;
this.signedInfo = signedInfo;
}
public string Id
{
get { return this.id; }
set { this.id = value; }
}
public SecurityKeyIdentifier KeyIdentifier
{
get { return this.keyIdentifier; }
set { this.keyIdentifier = value; }
}
public SignedInfo SignedInfo
{
get { return this.signedInfo; }
}
public ISignatureValueSecurityElement SignatureValue
{
get { return this.signatureValueElement; }
}
public byte[] GetSignatureBytes()
{
return this.signatureValueElement.Value;
}
public void ReadFrom(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.Signature, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
this.Id = reader.GetAttribute(dictionaryManager.UtilityDictionary.IdAttribute, null);
reader.Read();
this.signedInfo.ReadFrom(reader, signedXml.TransformFactory, dictionaryManager);
this.signatureValueElement.ReadFrom(reader, dictionaryManager);
if (signedXml.SecurityTokenSerializer.CanReadKeyIdentifier(reader))
{
this.keyIdentifier = signedXml.SecurityTokenSerializer.ReadKeyIdentifier(reader);
}
reader.ReadEndElement(); // Signature
}
public void SetSignatureValue(byte[] signatureValue)
{
this.signatureValueElement.Value = signatureValue;
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, dictionaryManager.XmlSignatureDictionary.Signature, dictionaryManager.XmlSignatureDictionary.Namespace);
if (this.id != null)
{
writer.WriteAttributeString(dictionaryManager.UtilityDictionary.IdAttribute, null, this.id);
}
this.signedInfo.WriteTo(writer, dictionaryManager);
this.signatureValueElement.WriteTo(writer, dictionaryManager);
if (this.keyIdentifier != null)
{
this.signedXml.SecurityTokenSerializer.WriteKeyIdentifier(writer, this.keyIdentifier);
}
writer.WriteEndElement(); // Signature
}
sealed class SignatureValueElement : ISignatureValueSecurityElement
{
string id;
string prefix = SignedXml.DefaultPrefix;
byte[] signatureValue;
string signatureText;
public bool HasId
{
get { return true; }
}
public string Id
{
get { return this.id; }
set { this.id = value; }
}
internal byte[] Value
{
get { return this.signatureValue; }
set
{
this.signatureValue = value;
this.signatureText = null;
}
}
public void ReadFrom(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.SignatureValue, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
this.Id = reader.GetAttribute(UtilityStrings.IdAttribute, null);
reader.Read();
this.signatureText = reader.ReadString();
this.signatureValue = System.Convert.FromBase64String(signatureText.Trim());
reader.ReadEndElement(); // SignatureValue
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, dictionaryManager.XmlSignatureDictionary.SignatureValue, dictionaryManager.XmlSignatureDictionary.Namespace);
if (this.id != null)
{
writer.WriteAttributeString(dictionaryManager.UtilityDictionary.IdAttribute, null, this.id);
}
if (this.signatureText != null)
{
writer.WriteString(this.signatureText);
}
else
{
writer.WriteBase64(this.signatureValue, 0, this.signatureValue.Length);
}
writer.WriteEndElement(); // SignatureValue
}
byte[] ISignatureValueSecurityElement.GetSignatureValue()
{
return this.Value;
}
}
}
internal interface ISignatureReaderProvider
{
XmlDictionaryReader GetReader(object callbackContext);
}
abstract class SignedInfo : ISecurityElement
{
readonly ExclusiveCanonicalizationTransform canonicalizationMethodElement = new ExclusiveCanonicalizationTransform(true);
string id;
ElementWithAlgorithmAttribute signatureMethodElement;
SignatureResourcePool resourcePool;
DictionaryManager dictionaryManager;
MemoryStream canonicalStream;
ISignatureReaderProvider readerProvider;
object signatureReaderProviderCallbackContext;
bool sendSide = true;
protected SignedInfo(DictionaryManager dictionaryManager)
{
if (dictionaryManager == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dictionaryManager");
this.signatureMethodElement = new ElementWithAlgorithmAttribute(dictionaryManager.XmlSignatureDictionary.SignatureMethod);
this.dictionaryManager = dictionaryManager;
}
protected DictionaryManager DictionaryManager
{
get { return this.dictionaryManager; }
}
protected MemoryStream CanonicalStream
{
get { return this.canonicalStream; }
set { this.canonicalStream = value; }
}
protected bool SendSide
{
get { return this.sendSide; }
set { this.sendSide = value; }
}
public ISignatureReaderProvider ReaderProvider
{
get { return this.readerProvider; }
set { this.readerProvider = value; }
}
public object SignatureReaderProviderCallbackContext
{
get { return this.signatureReaderProviderCallbackContext; }
set { this.signatureReaderProviderCallbackContext = value; }
}
public string CanonicalizationMethod
{
get { return this.canonicalizationMethodElement.Algorithm; }
set
{
if (value != this.canonicalizationMethodElement.Algorithm)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedTransformAlgorithm)));
}
}
}
public XmlDictionaryString CanonicalizationMethodDictionaryString
{
set
{
if (value != null && value.Value != this.canonicalizationMethodElement.Algorithm)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedTransformAlgorithm)));
}
}
}
public bool HasId
{
get { return true; }
}
public string Id
{
get { return this.id; }
set { this.id = value; }
}
public abstract int ReferenceCount
{
get;
}
public string SignatureMethod
{
get { return this.signatureMethodElement.Algorithm; }
set { this.signatureMethodElement.Algorithm = value; }
}
public XmlDictionaryString SignatureMethodDictionaryString
{
get { return this.signatureMethodElement.AlgorithmDictionaryString; }
set { this.signatureMethodElement.AlgorithmDictionaryString = value; }
}
public SignatureResourcePool ResourcePool
{
get
{
if (this.resourcePool == null)
{
this.resourcePool = new SignatureResourcePool();
}
return this.resourcePool;
}
set
{
this.resourcePool = value;
}
}
public void ComputeHash(HashAlgorithm algorithm)
{
if ((this.CanonicalizationMethod != SecurityAlgorithms.ExclusiveC14n) && (this.CanonicalizationMethod != SecurityAlgorithms.ExclusiveC14nWithComments))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedTransformAlgorithm)));
}
HashStream hashStream = this.ResourcePool.TakeHashStream(algorithm);
ComputeHash(hashStream);
hashStream.FlushHash();
}
protected virtual void ComputeHash(HashStream hashStream)
{
if (this.sendSide)
{
XmlDictionaryWriter utf8Writer = this.ResourcePool.TakeUtf8Writer();
utf8Writer.StartCanonicalization(hashStream, false, null);
WriteTo(utf8Writer, this.dictionaryManager);
utf8Writer.EndCanonicalization();
}
else if (this.canonicalStream != null)
{
this.canonicalStream.WriteTo(hashStream);
}
else
{
if (this.readerProvider == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.InclusiveNamespacePrefixRequiresSignatureReader)));
XmlDictionaryReader signatureReader = this.readerProvider.GetReader(this.signatureReaderProviderCallbackContext);
DiagnosticUtility.DebugAssert(signatureReader != null, "Require a Signature reader to validate signature.");
if (!signatureReader.CanCanonicalize)
{
MemoryStream stream = new MemoryStream();
XmlDictionaryWriter bufferingWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, this.DictionaryManager.ParentDictionary);
string[] inclusivePrefix = GetInclusivePrefixes();
if (inclusivePrefix != null)
{
bufferingWriter.WriteStartElement("a");
for (int i = 0; i < inclusivePrefix.Length; ++i)
{
string ns = GetNamespaceForInclusivePrefix(inclusivePrefix[i]);
if (ns != null)
{
bufferingWriter.WriteXmlnsAttribute(inclusivePrefix[i], ns);
}
}
}
signatureReader.MoveToContent();
bufferingWriter.WriteNode(signatureReader, false);
if (inclusivePrefix != null)
bufferingWriter.WriteEndElement();
bufferingWriter.Flush();
byte[] buffer = stream.ToArray();
int bufferLength = (int)stream.Length;
bufferingWriter.Close();
signatureReader.Close();
// Create a reader around the buffering Stream.
signatureReader = XmlDictionaryReader.CreateBinaryReader(buffer, 0, bufferLength, this.DictionaryManager.ParentDictionary, XmlDictionaryReaderQuotas.Max);
if (inclusivePrefix != null)
signatureReader.ReadStartElement("a");
}
signatureReader.ReadStartElement(dictionaryManager.XmlSignatureDictionary.Signature, dictionaryManager.XmlSignatureDictionary.Namespace);
signatureReader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.SignedInfo, dictionaryManager.XmlSignatureDictionary.Namespace);
signatureReader.StartCanonicalization(hashStream, false, GetInclusivePrefixes());
signatureReader.Skip();
signatureReader.EndCanonicalization();
signatureReader.Close();
}
}
public abstract void ComputeReferenceDigests();
protected string[] GetInclusivePrefixes()
{
return this.canonicalizationMethodElement.GetInclusivePrefixes();
}
protected virtual string GetNamespaceForInclusivePrefix(string prefix)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
}
public abstract void EnsureAllReferencesVerified();
public void EnsureDigestValidity(string id, object resolvedXmlSource)
{
if (!EnsureDigestValidityIfIdMatches(id, resolvedXmlSource))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.RequiredTargetNotSigned, id)));
}
}
public abstract bool EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource);
public virtual bool HasUnverifiedReference(string id)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
}
protected void ReadCanonicalizationMethod(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
// we will ignore any comments in the SignedInfo elemnt when verifying signature
this.canonicalizationMethodElement.ReadFrom(reader, dictionaryManager, false);
}
public abstract void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager);
protected void ReadSignatureMethod(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
this.signatureMethodElement.ReadFrom(reader, dictionaryManager);
}
protected void WriteCanonicalizationMethod(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
this.canonicalizationMethodElement.WriteTo(writer, dictionaryManager);
}
protected void WriteSignatureMethod(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
this.signatureMethodElement.WriteTo(writer, dictionaryManager);
}
public abstract void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager);
}
// whitespace preservation convention: ws1 immediately inside open tag; ws2 immediately after end tag.
class StandardSignedInfo : SignedInfo
{
string prefix = SignedXml.DefaultPrefix;
List<Reference> references;
Dictionary<string, string> context;
public StandardSignedInfo(DictionaryManager dictionaryManager)
: base(dictionaryManager)
{
this.references = new List<Reference>();
}
public override int ReferenceCount
{
get { return this.references.Count; }
}
public Reference this[int index]
{
get { return this.references[index]; }
}
public void AddReference(Reference reference)
{
if (!LocalAppContextSwitches.AllowUnlimitedXmlReferences)
{
long maximumNumberOfReferences = SecurityUtils.GetMaxXmlReferencesPerSignedInfo();
if (ReferenceCount > maximumNumberOfReferences)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException());
}
reference.ResourcePool = this.ResourcePool;
this.references.Add(reference);
}
public override void EnsureAllReferencesVerified()
{
for (int i = 0; i < this.references.Count; i++)
{
if (!this.references[i].Verified)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CryptographicException(SR.GetString(SR.UnableToResolveReferenceUriForSignature, this.references[i].Uri)));
}
}
}
public override bool EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource)
{
for (int i = 0; i < this.references.Count; i++)
{
if (this.references[i].EnsureDigestValidityIfIdMatches(id, resolvedXmlSource))
{
return true;
}
}
return false;
}
public override bool HasUnverifiedReference(string id)
{
for (int i = 0; i < this.references.Count; i++)
{
if (!this.references[i].Verified && this.references[i].ExtractReferredId() == id)
{
return true;
}
}
return false;
}
public override void ComputeReferenceDigests()
{
if (this.references.Count == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.AtLeastOneReferenceRequired)));
}
for (int i = 0; i < this.references.Count; i++)
{
this.references[i].ComputeAndSetDigest();
}
}
public override void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager)
{
this.SendSide = false;
if (reader.CanCanonicalize)
{
this.CanonicalStream = new MemoryStream();
reader.StartCanonicalization(this.CanonicalStream, false, null);
}
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.SignedInfo, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
this.Id = reader.GetAttribute(dictionaryManager.UtilityDictionary.IdAttribute, null);
reader.Read();
ReadCanonicalizationMethod(reader, dictionaryManager);
ReadSignatureMethod(reader, dictionaryManager);
while (reader.IsStartElement(dictionaryManager.XmlSignatureDictionary.Reference, dictionaryManager.XmlSignatureDictionary.Namespace))
{
Reference reference = new Reference(dictionaryManager);
reference.ReadFrom(reader, transformFactory, dictionaryManager);
AddReference(reference);
}
reader.ReadEndElement(); // SignedInfo
if (reader.CanCanonicalize)
reader.EndCanonicalization();
string[] inclusivePrefixes = GetInclusivePrefixes();
if (inclusivePrefixes != null)
{
// Clear the canonicalized stream. We cannot use this while inclusive prefixes are
// specified.
this.CanonicalStream = null;
this.context = new Dictionary<string, string>(inclusivePrefixes.Length);
for (int i = 0; i < inclusivePrefixes.Length; i++)
{
this.context.Add(inclusivePrefixes[i], reader.LookupNamespace(inclusivePrefixes[i]));
}
}
}
public override void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, dictionaryManager.XmlSignatureDictionary.SignedInfo, dictionaryManager.XmlSignatureDictionary.Namespace);
if (this.Id != null)
{
writer.WriteAttributeString(dictionaryManager.UtilityDictionary.IdAttribute, null, this.Id);
}
WriteCanonicalizationMethod(writer, dictionaryManager);
WriteSignatureMethod(writer, dictionaryManager);
for (int i = 0; i < this.references.Count; i++)
{
this.references[i].WriteTo(writer, dictionaryManager);
}
writer.WriteEndElement(); // SignedInfo
}
protected override string GetNamespaceForInclusivePrefix(string prefix)
{
if (this.context == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
if (prefix == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("prefix");
return context[prefix];
}
protected string Prefix
{
get { return prefix; }
set { prefix = value; }
}
protected Dictionary<string, string> Context
{
get { return context; }
set { context = value; }
}
}
sealed class WifSignedInfo : StandardSignedInfo, IDisposable
{
MemoryStream _bufferedStream;
string _defaultNamespace = String.Empty;
bool _disposed;
public WifSignedInfo(DictionaryManager dictionaryManager)
: base(dictionaryManager)
{
}
~WifSignedInfo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
//
// Free all of our managed resources
//
if (_bufferedStream != null)
{
_bufferedStream.Close();
_bufferedStream = null;
}
}
// Free native resources, if any.
_disposed = true;
}
protected override void ComputeHash(HashStream hashStream)
{
if (SendSide)
{
using (XmlDictionaryWriter utf8Writer = XmlDictionaryWriter.CreateTextWriter(Stream.Null, Encoding.UTF8, false))
{
utf8Writer.StartCanonicalization(hashStream, false, null);
WriteTo(utf8Writer, DictionaryManager);
utf8Writer.EndCanonicalization();
}
}
else if (CanonicalStream != null)
{
CanonicalStream.WriteTo(hashStream);
}
else
{
_bufferedStream.Position = 0;
// We are creating a XmlDictionaryReader with a hard-coded Max XmlDictionaryReaderQuotas. This is a reader that we
// are creating over an already buffered content. The content was initially read off user provided XmlDictionaryReader
// with the correct quotas and hence we know the data is valid.
// Note: signedinfoReader will close _bufferedStream on Dispose.
using (XmlDictionaryReader signedinfoReader = XmlDictionaryReader.CreateTextReader(_bufferedStream, XmlDictionaryReaderQuotas.Max))
{
signedinfoReader.MoveToContent();
using (XmlDictionaryWriter bufferingWriter = XmlDictionaryWriter.CreateTextWriter(Stream.Null, Encoding.UTF8, false))
{
bufferingWriter.WriteStartElement("a", _defaultNamespace);
string[] inclusivePrefix = GetInclusivePrefixes();
for (int i = 0; i < inclusivePrefix.Length; ++i)
{
string ns = GetNamespaceForInclusivePrefix(inclusivePrefix[i]);
if (ns != null)
{
bufferingWriter.WriteXmlnsAttribute(inclusivePrefix[i], ns);
}
}
bufferingWriter.StartCanonicalization(hashStream, false, inclusivePrefix);
bufferingWriter.WriteNode(signedinfoReader, false);
bufferingWriter.EndCanonicalization();
bufferingWriter.WriteEndElement();
}
}
}
}
public override void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Namespace);
SendSide = false;
_defaultNamespace = reader.LookupNamespace(String.Empty);
_bufferedStream = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.NewLineHandling = NewLineHandling.None;
using (XmlWriter bufferWriter = XmlTextWriter.Create(_bufferedStream, settings))
{
bufferWriter.WriteNode(reader, true);
bufferWriter.Flush();
}
_bufferedStream.Position = 0;
//
// We are creating a XmlDictionaryReader with a hard-coded Max XmlDictionaryReaderQuotas. This is a reader that we
// are creating over an already buffered content. The content was initially read off user provided XmlDictionaryReader
// with the correct quotas and hence we know the data is valid.
// Note: effectiveReader will close _bufferedStream on Dispose.
//
using (XmlDictionaryReader effectiveReader = XmlDictionaryReader.CreateTextReader(_bufferedStream, XmlDictionaryReaderQuotas.Max))
{
CanonicalStream = new MemoryStream();
effectiveReader.StartCanonicalization(CanonicalStream, false, null);
effectiveReader.MoveToStartElement(XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Namespace);
Prefix = effectiveReader.Prefix;
Id = effectiveReader.GetAttribute(WSSecurityUtilityConstants.Attributes.Id, null);
effectiveReader.Read();
ReadCanonicalizationMethod(effectiveReader, DictionaryManager);
ReadSignatureMethod(effectiveReader, DictionaryManager);
while (effectiveReader.IsStartElement(XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Namespace))
{
Reference reference = new Reference(DictionaryManager);
reference.ReadFrom(effectiveReader, transformFactory, DictionaryManager);
AddReference(reference);
}
effectiveReader.ReadEndElement();
effectiveReader.EndCanonicalization();
}
string[] inclusivePrefixes = GetInclusivePrefixes();
if (inclusivePrefixes != null)
{
// Clear the canonicalized stream. We cannot use this while inclusive prefixes are
// specified.
CanonicalStream = null;
Context = new Dictionary<string, string>(inclusivePrefixes.Length);
for (int i = 0; i < inclusivePrefixes.Length; i++)
{
Context.Add(inclusivePrefixes[i], reader.LookupNamespace(inclusivePrefixes[i]));
}
}
}
}
sealed class Reference
{
ElementWithAlgorithmAttribute digestMethodElement;
DigestValueElement digestValueElement = new DigestValueElement();
string id;
string prefix = SignedXml.DefaultPrefix;
object resolvedXmlSource;
readonly TransformChain transformChain = new TransformChain();
string type;
string uri;
SignatureResourcePool resourcePool;
bool verified;
string referredId;
DictionaryManager dictionaryManager;
public Reference(DictionaryManager dictionaryManager)
: this(dictionaryManager, null)
{
}
public Reference(DictionaryManager dictionaryManager, string uri)
: this(dictionaryManager, uri, null)
{
}
public Reference(DictionaryManager dictionaryManager, string uri, object resolvedXmlSource)
{
if (dictionaryManager == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dictionaryManager");
this.dictionaryManager = dictionaryManager;
this.digestMethodElement = new ElementWithAlgorithmAttribute(dictionaryManager.XmlSignatureDictionary.DigestMethod);
this.uri = uri;
this.resolvedXmlSource = resolvedXmlSource;
}
public string DigestMethod
{
get { return this.digestMethodElement.Algorithm; }
set { this.digestMethodElement.Algorithm = value; }
}
public XmlDictionaryString DigestMethodDictionaryString
{
get { return this.digestMethodElement.AlgorithmDictionaryString; }
set { this.digestMethodElement.AlgorithmDictionaryString = value; }
}
public string Id
{
get { return this.id; }
set { this.id = value; }
}
public SignatureResourcePool ResourcePool
{
get { return this.resourcePool; }
set { this.resourcePool = value; }
}
public TransformChain TransformChain
{
get { return this.transformChain; }
}
public int TransformCount
{
get { return this.transformChain.TransformCount; }
}
public string Type
{
get { return this.type; }
set { this.type = value; }
}
public string Uri
{
get { return this.uri; }
set { this.uri = value; }
}
public bool Verified
{
get { return this.verified; }
}
public void AddTransform(Transform transform)
{
this.transformChain.Add(transform);
}
public void EnsureDigestValidity(string id, byte[] computedDigest)
{
if (!EnsureDigestValidityIfIdMatches(id, computedDigest))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.RequiredTargetNotSigned, id)));
}
}
public void EnsureDigestValidity(string id, object resolvedXmlSource)
{
if (!EnsureDigestValidityIfIdMatches(id, resolvedXmlSource))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.RequiredTargetNotSigned, id)));
}
}
public bool EnsureDigestValidityIfIdMatches(string id, byte[] computedDigest)
{
if (this.verified || id != ExtractReferredId())
{
return false;
}
if (!CryptoHelper.FixedTimeEquals(computedDigest, GetDigestValue()))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CryptographicException(SR.GetString(SR.DigestVerificationFailedForReference, this.uri)));
}
this.verified = true;
return true;
}
public bool EnsureDigestValidityIfIdMatches(string id, object resolvedXmlSource)
{
if (this.verified)
{
return false;
}
// During StrTransform the extractedReferredId on the reference will point to STR and hence will not be
// equal to the referred element ie security token Id.
if (id != ExtractReferredId() && !this.IsStrTranform())
{
return false;
}
this.resolvedXmlSource = resolvedXmlSource;
if (!CheckDigest())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CryptographicException(SR.GetString(SR.DigestVerificationFailedForReference, this.uri)));
}
this.verified = true;
return true;
}
public bool IsStrTranform()
{
return this.TransformChain.TransformCount == 1 && this.TransformChain[0].Algorithm == SecurityAlgorithms.StrTransform;
}
public string ExtractReferredId()
{
if (this.referredId == null)
{
if (StringComparer.OrdinalIgnoreCase.Equals(uri, String.Empty))
{
return String.Empty;
}
if (this.uri == null || this.uri.Length < 2 || this.uri[0] != '#')
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CryptographicException(SR.GetString(SR.UnableToResolveReferenceUriForSignature, this.uri)));
}
this.referredId = this.uri.Substring(1);
}
return this.referredId;
}
/// <summary>
/// We look at the URI reference to decide if we should preserve comments while canonicalization.
/// Only when the reference is xpointer(/) or xpointer(id(SomeId)) do we preserve comments during canonicalization
/// of the reference element for computing the digest.
/// </summary>
/// <param name="uri">The Uri reference </param>
/// <returns>true if comments should be preserved.</returns>
private static bool ShouldPreserveComments(string uri)
{
bool preserveComments = false;
if (!String.IsNullOrEmpty(uri))
{
//removes the hash
string idref = uri.Substring(1);
if (idref == "xpointer(/)")
{
preserveComments = true;
}
else if (idref.StartsWith("xpointer(id(", StringComparison.Ordinal) && (idref.IndexOf(")", StringComparison.Ordinal) > 0))
{
// Dealing with XPointer of type #xpointer(id("ID")). Other XPointer support isn't handled here and is anyway optional
preserveComments = true;
}
}
return preserveComments;
}
public bool CheckDigest()
{
byte[] computedDigest = ComputeDigest();
bool result = CryptoHelper.FixedTimeEquals(computedDigest, GetDigestValue());
#if LOG_DIGESTS
Console.WriteLine(">>> Checking digest for reference '{0}', result {1}", uri, result);
Console.WriteLine(" Computed digest {0}", Convert.ToBase64String(computedDigest));
Console.WriteLine(" Received digest {0}", Convert.ToBase64String(GetDigestValue()));
#endif
return result;
}
public void ComputeAndSetDigest()
{
this.digestValueElement.Value = ComputeDigest();
}
public byte[] ComputeDigest()
{
if (this.transformChain.TransformCount == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.EmptyTransformChainNotSupported)));
}
if (this.resolvedXmlSource == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.UnableToResolveReferenceUriForSignature, this.uri)));
}
return this.transformChain.TransformToDigest(this.resolvedXmlSource, this.ResourcePool, this.DigestMethod, this.dictionaryManager);
}
public byte[] GetDigestValue()
{
return this.digestValueElement.Value;
}
public void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.Reference, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
this.Id = reader.GetAttribute(UtilityStrings.IdAttribute, null);
this.Uri = reader.GetAttribute(dictionaryManager.XmlSignatureDictionary.URI, null);
this.Type = reader.GetAttribute(dictionaryManager.XmlSignatureDictionary.Type, null);
reader.Read();
if (reader.IsStartElement(dictionaryManager.XmlSignatureDictionary.Transforms, dictionaryManager.XmlSignatureDictionary.Namespace))
{
this.transformChain.ReadFrom(reader, transformFactory, dictionaryManager, ShouldPreserveComments(this.Uri));
}
this.digestMethodElement.ReadFrom(reader, dictionaryManager);
this.digestValueElement.ReadFrom(reader, dictionaryManager);
reader.MoveToContent();
reader.ReadEndElement(); // Reference
}
public void SetResolvedXmlSource(object resolvedXmlSource)
{
this.resolvedXmlSource = resolvedXmlSource;
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, dictionaryManager.XmlSignatureDictionary.Reference, dictionaryManager.XmlSignatureDictionary.Namespace);
if (this.id != null)
{
writer.WriteAttributeString(dictionaryManager.UtilityDictionary.IdAttribute, null, this.id);
}
if (this.uri != null)
{
writer.WriteAttributeString(dictionaryManager.XmlSignatureDictionary.URI, null, this.uri);
}
if (this.type != null)
{
writer.WriteAttributeString(dictionaryManager.XmlSignatureDictionary.Type, null, this.type);
}
if (this.transformChain.TransformCount > 0)
{
this.transformChain.WriteTo(writer, dictionaryManager);
}
this.digestMethodElement.WriteTo(writer, dictionaryManager);
this.digestValueElement.WriteTo(writer, dictionaryManager);
writer.WriteEndElement(); // Reference
}
struct DigestValueElement
{
byte[] digestValue;
string digestText;
string prefix;
internal byte[] Value
{
get { return this.digestValue; }
set
{
this.digestValue = value;
this.digestText = null;
}
}
public void ReadFrom(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.DigestValue, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
reader.Read();
reader.MoveToContent();
this.digestText = reader.ReadString();
this.digestValue = System.Convert.FromBase64String(digestText.Trim());
reader.MoveToContent();
reader.ReadEndElement(); // DigestValue
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix ?? XmlSignatureStrings.Prefix, dictionaryManager.XmlSignatureDictionary.DigestValue, dictionaryManager.XmlSignatureDictionary.Namespace);
if (this.digestText != null)
{
writer.WriteString(this.digestText);
}
else
{
writer.WriteBase64(this.digestValue, 0, this.digestValue.Length);
}
writer.WriteEndElement(); // DigestValue
}
}
}
sealed class TransformChain
{
string prefix = SignedXml.DefaultPrefix;
MostlySingletonList<Transform> transforms;
public TransformChain()
{
}
public int TransformCount
{
get { return this.transforms.Count; }
}
public Transform this[int index]
{
get
{
return this.transforms[index];
}
}
public bool NeedsInclusiveContext
{
get
{
for (int i = 0; i < this.TransformCount; i++)
{
if (this[i].NeedsInclusiveContext)
{
return true;
}
}
return false;
}
}
public void Add(Transform transform)
{
if (!LocalAppContextSwitches.AllowUnlimitedXmlTransforms)
{
long maximumTransforms = SecurityUtils.GetMaxXmlTransformsPerReference();
if (TransformCount > maximumTransforms)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException());
}
this.transforms.Add(transform);
}
public void ReadFrom(XmlDictionaryReader reader, TransformFactory transformFactory, DictionaryManager dictionaryManager, bool preserveComments)
{
reader.MoveToStartElement(dictionaryManager.XmlSignatureDictionary.Transforms, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
reader.Read();
while (reader.IsStartElement(dictionaryManager.XmlSignatureDictionary.Transform, dictionaryManager.XmlSignatureDictionary.Namespace))
{
string transformAlgorithmUri = reader.GetAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
Transform transform = transformFactory.CreateTransform(transformAlgorithmUri);
transform.ReadFrom(reader, dictionaryManager, preserveComments);
Add(transform);
}
reader.MoveToContent();
reader.ReadEndElement(); // Transforms
if (this.TransformCount == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.AtLeastOneTransformRequired)));
}
}
public byte[] TransformToDigest(object data, SignatureResourcePool resourcePool, string digestMethod, DictionaryManager dictionaryManager)
{
DiagnosticUtility.DebugAssert(TransformCount > 0, "");
for (int i = 0; i < this.TransformCount - 1; i++)
{
data = this[i].Process(data, resourcePool, dictionaryManager);
}
return this[this.TransformCount - 1].ProcessAndDigest(data, resourcePool, digestMethod, dictionaryManager);
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, dictionaryManager.XmlSignatureDictionary.Transforms, dictionaryManager.XmlSignatureDictionary.Namespace);
for (int i = 0; i < this.TransformCount; i++)
{
this[i].WriteTo(writer, dictionaryManager);
}
writer.WriteEndElement(); // Transforms
}
}
struct ElementWithAlgorithmAttribute
{
readonly XmlDictionaryString elementName;
string algorithm;
XmlDictionaryString algorithmDictionaryString;
string prefix;
public ElementWithAlgorithmAttribute(XmlDictionaryString elementName)
{
if (elementName == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("elementName"));
}
this.elementName = elementName;
this.algorithm = null;
this.algorithmDictionaryString = null;
this.prefix = SignedXml.DefaultPrefix;
}
public string Algorithm
{
get { return this.algorithm; }
set { this.algorithm = value; }
}
public XmlDictionaryString AlgorithmDictionaryString
{
get { return this.algorithmDictionaryString; }
set { this.algorithmDictionaryString = value; }
}
public void ReadFrom(XmlDictionaryReader reader, DictionaryManager dictionaryManager)
{
reader.MoveToStartElement(this.elementName, dictionaryManager.XmlSignatureDictionary.Namespace);
this.prefix = reader.Prefix;
bool isEmptyElement = reader.IsEmptyElement;
this.algorithm = reader.GetAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
if (this.algorithm == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(
SR.GetString(SR.RequiredAttributeMissing, dictionaryManager.XmlSignatureDictionary.Algorithm, this.elementName)));
}
reader.Read();
reader.MoveToContent();
if (!isEmptyElement)
{
reader.MoveToContent();
reader.ReadEndElement();
}
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
writer.WriteStartElement(this.prefix, this.elementName, dictionaryManager.XmlSignatureDictionary.Namespace);
writer.WriteStartAttribute(dictionaryManager.XmlSignatureDictionary.Algorithm, null);
if (this.algorithmDictionaryString != null)
{
writer.WriteString(this.algorithmDictionaryString);
}
else
{
writer.WriteString(this.algorithm);
}
writer.WriteEndAttribute();
writer.WriteEndElement();
}
}
}
|