908 lines
40 KiB
C#
908 lines
40 KiB
C#
|
//----------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Security
|
||
|
{
|
||
|
using System.IdentityModel.Tokens;
|
||
|
using System.IO;
|
||
|
using System.Runtime;
|
||
|
using System.Security.Cryptography;
|
||
|
using System.ServiceModel;
|
||
|
using System.ServiceModel.Channels;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Diagnostics;
|
||
|
using System.Xml;
|
||
|
using ExclusiveCanonicalizationTransform = System.IdentityModel.ExclusiveCanonicalizationTransform;
|
||
|
using HashStream = System.IdentityModel.HashStream;
|
||
|
using IPrefixGenerator = System.IdentityModel.IPrefixGenerator;
|
||
|
using ISecurityElement = System.IdentityModel.ISecurityElement;
|
||
|
using ISignatureValueSecurityElement = System.IdentityModel.ISignatureValueSecurityElement;
|
||
|
using PreDigestedSignedInfo = System.IdentityModel.PreDigestedSignedInfo;
|
||
|
using Reference = System.IdentityModel.Reference;
|
||
|
using SignedInfo = System.IdentityModel.SignedInfo;
|
||
|
using SignedXml = System.IdentityModel.SignedXml;
|
||
|
using StandardSignedInfo = System.IdentityModel.StandardSignedInfo;
|
||
|
using System.ServiceModel.Security.Tokens;
|
||
|
|
||
|
class WSSecurityOneDotZeroSendSecurityHeader : SendSecurityHeader
|
||
|
{
|
||
|
HashStream hashStream;
|
||
|
PreDigestedSignedInfo signedInfo;
|
||
|
SignedXml signedXml;
|
||
|
SecurityKey signatureKey;
|
||
|
MessagePartSpecification effectiveSignatureParts;
|
||
|
|
||
|
SymmetricAlgorithm encryptingSymmetricAlgorithm;
|
||
|
ReferenceList referenceList;
|
||
|
SecurityKeyIdentifier encryptionKeyIdentifier;
|
||
|
|
||
|
bool hasSignedEncryptedMessagePart;
|
||
|
|
||
|
// For Transport Secrity we have to sign the 'To' header with the
|
||
|
// supporting tokens.
|
||
|
byte[] toHeaderHash = null;
|
||
|
string toHeaderId = null;
|
||
|
|
||
|
public WSSecurityOneDotZeroSendSecurityHeader(Message message, string actor, bool mustUnderstand, bool relay,
|
||
|
SecurityStandardsManager standardsManager,
|
||
|
SecurityAlgorithmSuite algorithmSuite,
|
||
|
MessageDirection direction)
|
||
|
: base(message, actor, mustUnderstand, relay, standardsManager, algorithmSuite, direction)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected string EncryptionAlgorithm
|
||
|
{
|
||
|
get { return this.AlgorithmSuite.DefaultEncryptionAlgorithm; }
|
||
|
}
|
||
|
|
||
|
protected XmlDictionaryString EncryptionAlgorithmDictionaryString
|
||
|
{
|
||
|
get { return this.AlgorithmSuite.DefaultEncryptionAlgorithmDictionaryString; }
|
||
|
}
|
||
|
|
||
|
protected override bool HasSignedEncryptedMessagePart
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.hasSignedEncryptedMessagePart;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddEncryptionReference(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, bool sign,
|
||
|
out MemoryStream plainTextStream, out string encryptedDataId)
|
||
|
{
|
||
|
plainTextStream = new MemoryStream();
|
||
|
XmlDictionaryWriter encryptingWriter = XmlDictionaryWriter.CreateTextWriter(plainTextStream);
|
||
|
if (sign)
|
||
|
{
|
||
|
AddSignatureReference(header, headerId, prefixGenerator, encryptingWriter);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
header.WriteHeader(encryptingWriter, this.Version);
|
||
|
encryptingWriter.Flush();
|
||
|
}
|
||
|
encryptedDataId = this.GenerateId();
|
||
|
referenceList.AddReferredId(encryptedDataId);
|
||
|
}
|
||
|
|
||
|
void AddSignatureReference(SecurityToken token, int position, SecurityTokenAttachmentMode mode)
|
||
|
{
|
||
|
SecurityKeyIdentifierClause keyIdentifierClause = null;
|
||
|
bool strTransformEnabled = this.ShouldUseStrTransformForToken(token, position, mode, out keyIdentifierClause);
|
||
|
AddTokenSignatureReference(token, keyIdentifierClause, strTransformEnabled);
|
||
|
}
|
||
|
|
||
|
void AddPrimaryTokenSignatureReference(SecurityToken token, SecurityTokenParameters securityTokenParameters)
|
||
|
{
|
||
|
// Currently we only support signing the primary token if the primary token is an issued token and protectTokens knob is set to true.
|
||
|
// We will get rid of the below check when we support all token types.
|
||
|
IssuedSecurityTokenParameters istp = securityTokenParameters as IssuedSecurityTokenParameters;
|
||
|
if (istp == null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool strTransformEnabled = istp != null && istp.UseStrTransform;
|
||
|
SecurityKeyIdentifierClause keyIdentifierClause = null;
|
||
|
// Only if the primary token is included in the message that we sign it because WCF at present does not resolve externally referenced tokens.
|
||
|
// This means in the server's response
|
||
|
if (ShouldSerializeToken(securityTokenParameters, this.MessageDirection))
|
||
|
{
|
||
|
if (strTransformEnabled)
|
||
|
{
|
||
|
keyIdentifierClause = securityTokenParameters.CreateKeyIdentifierClause(token, GetTokenReferenceStyle(securityTokenParameters));
|
||
|
}
|
||
|
AddTokenSignatureReference(token, keyIdentifierClause, strTransformEnabled);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Given a token and useStarTransform value this method adds apporopriate reference accordingly.
|
||
|
// 1. If strTransform is disabled, it adds a reference to the token's id.
|
||
|
// 2. Else if strtransform is enabled it adds a reference the security token's keyIdentifier's id.
|
||
|
void AddTokenSignatureReference(SecurityToken token, SecurityKeyIdentifierClause keyIdentifierClause, bool strTransformEnabled)
|
||
|
{
|
||
|
if (!strTransformEnabled && token.Id == null)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ElementToSignMustHaveId)), this.Message);
|
||
|
}
|
||
|
|
||
|
HashStream hashStream = TakeHashStream();
|
||
|
XmlDictionaryWriter utf8Writer = TakeUtf8Writer();
|
||
|
utf8Writer.StartCanonicalization(hashStream, false, null);
|
||
|
this.StandardsManager.SecurityTokenSerializer.WriteToken(utf8Writer, token);
|
||
|
utf8Writer.EndCanonicalization();
|
||
|
|
||
|
if (strTransformEnabled)
|
||
|
{
|
||
|
if (keyIdentifierClause != null)
|
||
|
{
|
||
|
if (String.IsNullOrEmpty(keyIdentifierClause.Id))
|
||
|
keyIdentifierClause.Id = SecurityUniqueId.Create().Value;
|
||
|
this.ElementContainer.MapSecurityTokenToStrClause(token, keyIdentifierClause);
|
||
|
this.signedInfo.AddReference(keyIdentifierClause.Id, hashStream.FlushHashAndGetValue(), true);
|
||
|
}
|
||
|
else
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
|
||
|
}
|
||
|
else
|
||
|
this.signedInfo.AddReference(token.Id, hashStream.FlushHashAndGetValue());
|
||
|
}
|
||
|
|
||
|
void AddSignatureReference(SendSecurityHeaderElement[] elements)
|
||
|
{
|
||
|
if (elements != null)
|
||
|
{
|
||
|
for (int i = 0; i < elements.Length; ++i)
|
||
|
{
|
||
|
SecurityKeyIdentifierClause keyIdentifierClause = null;
|
||
|
TokenElement signedEncryptedTokenElement = elements[i].Item as TokenElement;
|
||
|
|
||
|
// signedEncryptedTokenElement can either be a TokenElement ( in SignThenEncrypt case) or EncryptedData ( in !SignThenEncryptCase)
|
||
|
// STR-Transform does not make sense in !SignThenEncrypt case .
|
||
|
// note: signedEncryptedTokenElement can also be SignatureConfirmation but we do not care about it here.
|
||
|
bool useStrTransform = signedEncryptedTokenElement != null
|
||
|
&& SignThenEncrypt
|
||
|
&& this.ShouldUseStrTransformForToken(signedEncryptedTokenElement.Token,
|
||
|
i,
|
||
|
SecurityTokenAttachmentMode.SignedEncrypted,
|
||
|
out keyIdentifierClause);
|
||
|
|
||
|
if (!useStrTransform && elements[i].Id == null)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ElementToSignMustHaveId)), this.Message);
|
||
|
}
|
||
|
|
||
|
HashStream hashStream = TakeHashStream();
|
||
|
XmlDictionaryWriter utf8Writer = TakeUtf8Writer();
|
||
|
utf8Writer.StartCanonicalization(hashStream, false, null);
|
||
|
elements[i].Item.WriteTo(utf8Writer, ServiceModelDictionaryManager.Instance);
|
||
|
utf8Writer.EndCanonicalization();
|
||
|
|
||
|
if (useStrTransform)
|
||
|
{
|
||
|
if (keyIdentifierClause != null)
|
||
|
{
|
||
|
if (String.IsNullOrEmpty(keyIdentifierClause.Id))
|
||
|
keyIdentifierClause.Id = SecurityUniqueId.Create().Value;
|
||
|
|
||
|
this.ElementContainer.MapSecurityTokenToStrClause(signedEncryptedTokenElement.Token, keyIdentifierClause);
|
||
|
this.signedInfo.AddReference(keyIdentifierClause.Id, hashStream.FlushHashAndGetValue(), true);
|
||
|
}
|
||
|
else
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
|
||
|
}
|
||
|
else
|
||
|
this.signedInfo.AddReference(elements[i].Id, hashStream.FlushHashAndGetValue());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddSignatureReference(SecurityToken[] tokens, SecurityTokenAttachmentMode mode)
|
||
|
{
|
||
|
if (tokens != null)
|
||
|
{
|
||
|
for (int i = 0; i < tokens.Length; ++i)
|
||
|
{
|
||
|
AddSignatureReference(tokens[i], i, mode);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string GetSignatureHash(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, XmlDictionaryWriter writer, out byte[] hash)
|
||
|
{
|
||
|
HashStream hashStream = TakeHashStream();
|
||
|
XmlDictionaryWriter effectiveWriter;
|
||
|
XmlBuffer canonicalBuffer = null;
|
||
|
|
||
|
if (writer.CanCanonicalize)
|
||
|
{
|
||
|
effectiveWriter = writer;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
canonicalBuffer = new XmlBuffer(int.MaxValue);
|
||
|
effectiveWriter = canonicalBuffer.OpenSection(XmlDictionaryReaderQuotas.Max);
|
||
|
}
|
||
|
|
||
|
effectiveWriter.StartCanonicalization(hashStream, false, null);
|
||
|
|
||
|
header.WriteStartHeader(effectiveWriter, this.Version);
|
||
|
if (headerId == null)
|
||
|
{
|
||
|
headerId = GenerateId();
|
||
|
this.StandardsManager.IdManager.WriteIdAttribute(effectiveWriter, headerId);
|
||
|
}
|
||
|
header.WriteHeaderContents(effectiveWriter, this.Version);
|
||
|
effectiveWriter.WriteEndElement();
|
||
|
effectiveWriter.EndCanonicalization();
|
||
|
effectiveWriter.Flush();
|
||
|
|
||
|
if (!ReferenceEquals(effectiveWriter, writer))
|
||
|
{
|
||
|
Fx.Assert(canonicalBuffer != null, "Canonical buffer cannot be null.");
|
||
|
canonicalBuffer.CloseSection();
|
||
|
canonicalBuffer.Close();
|
||
|
XmlDictionaryReader dicReader = canonicalBuffer.GetReader(0);
|
||
|
writer.WriteNode(dicReader, false);
|
||
|
dicReader.Close();
|
||
|
}
|
||
|
|
||
|
hash = hashStream.FlushHashAndGetValue();
|
||
|
|
||
|
return headerId;
|
||
|
}
|
||
|
|
||
|
void AddSignatureReference(MessageHeader header, string headerId, IPrefixGenerator prefixGenerator, XmlDictionaryWriter writer)
|
||
|
{
|
||
|
byte[] hashValue;
|
||
|
headerId = GetSignatureHash(header, headerId, prefixGenerator, writer, out hashValue);
|
||
|
this.signedInfo.AddReference(headerId, hashValue);
|
||
|
}
|
||
|
|
||
|
void ApplySecurityAndWriteHeader(MessageHeader header, string headerId, XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
|
||
|
{
|
||
|
if (!this.RequireMessageProtection && this.ShouldSignToHeader)
|
||
|
{
|
||
|
if ((header.Name == XD.AddressingDictionary.To.Value) &&
|
||
|
(header.Namespace == this.Message.Version.Addressing.Namespace))
|
||
|
{
|
||
|
if (this.toHeaderHash == null)
|
||
|
{
|
||
|
byte[] headerHash;
|
||
|
headerId = GetSignatureHash(header, headerId, prefixGenerator, writer, out headerHash);
|
||
|
this.toHeaderHash = headerHash;
|
||
|
this.toHeaderId = headerId;
|
||
|
}
|
||
|
else
|
||
|
// More than one 'To' header is specified in the message.
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TransportSecuredMessageHasMoreThanOneToHeader)));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MessagePartProtectionMode protectionMode = GetProtectionMode(header);
|
||
|
MemoryStream plainTextStream;
|
||
|
string encryptedDataId;
|
||
|
switch (protectionMode)
|
||
|
{
|
||
|
case MessagePartProtectionMode.None:
|
||
|
header.WriteHeader(writer, this.Version);
|
||
|
return;
|
||
|
case MessagePartProtectionMode.Sign:
|
||
|
AddSignatureReference(header, headerId, prefixGenerator, writer);
|
||
|
return;
|
||
|
case MessagePartProtectionMode.SignThenEncrypt:
|
||
|
AddEncryptionReference(header, headerId, prefixGenerator, true, out plainTextStream, out encryptedDataId);
|
||
|
EncryptAndWriteHeader(header, encryptedDataId, plainTextStream, writer);
|
||
|
this.hasSignedEncryptedMessagePart = true;
|
||
|
return;
|
||
|
case MessagePartProtectionMode.Encrypt:
|
||
|
AddEncryptionReference(header, headerId, prefixGenerator, false, out plainTextStream, out encryptedDataId);
|
||
|
EncryptAndWriteHeader(header, encryptedDataId, plainTextStream, writer);
|
||
|
return;
|
||
|
case MessagePartProtectionMode.EncryptThenSign:
|
||
|
AddEncryptionReference(header, headerId, prefixGenerator, false, out plainTextStream, out encryptedDataId);
|
||
|
EncryptedHeader encryptedHeader = EncryptHeader(
|
||
|
header, this.encryptingSymmetricAlgorithm, this.encryptionKeyIdentifier, this.Version, encryptedDataId, plainTextStream);
|
||
|
AddSignatureReference(encryptedHeader, encryptedDataId, prefixGenerator, writer);
|
||
|
return;
|
||
|
default:
|
||
|
Fx.Assert("Invalid MessagePartProtectionMode");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void ApplySecurityAndWriteHeaders(MessageHeaders headers, XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
|
||
|
{
|
||
|
string[] headerIds;
|
||
|
if (this.RequireMessageProtection || this.ShouldSignToHeader)
|
||
|
{
|
||
|
headerIds = headers.GetHeaderAttributes(UtilityStrings.IdAttribute,
|
||
|
this.StandardsManager.IdManager.DefaultIdNamespaceUri);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
headerIds = null;
|
||
|
}
|
||
|
for (int i = 0; i < headers.Count; i++)
|
||
|
{
|
||
|
MessageHeader header = headers.GetMessageHeader(i);
|
||
|
if (this.Version.Addressing == AddressingVersion.None && header.Namespace == AddressingVersion.None.Namespace)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (header != this)
|
||
|
{
|
||
|
ApplySecurityAndWriteHeader(header, headerIds == null ? null : headerIds[i], writer, prefixGenerator);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool CanCanonicalizeAndFragment(XmlDictionaryWriter writer)
|
||
|
{
|
||
|
if (!writer.CanCanonicalize)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
IFragmentCapableXmlDictionaryWriter fragmentingWriter = writer as IFragmentCapableXmlDictionaryWriter;
|
||
|
return fragmentingWriter != null && fragmentingWriter.CanFragment;
|
||
|
}
|
||
|
|
||
|
public override void ApplyBodySecurity(XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator)
|
||
|
{
|
||
|
SecurityAppliedMessage message = this.SecurityAppliedMessage;
|
||
|
EncryptedData encryptedData;
|
||
|
HashStream hashStream;
|
||
|
switch (message.BodyProtectionMode)
|
||
|
{
|
||
|
case MessagePartProtectionMode.None:
|
||
|
return;
|
||
|
case MessagePartProtectionMode.Sign:
|
||
|
hashStream = TakeHashStream();
|
||
|
if (CanCanonicalizeAndFragment(writer))
|
||
|
{
|
||
|
message.WriteBodyToSignWithFragments(hashStream, false, null, writer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
message.WriteBodyToSign(hashStream);
|
||
|
}
|
||
|
this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
|
||
|
return;
|
||
|
case MessagePartProtectionMode.SignThenEncrypt:
|
||
|
hashStream = TakeHashStream();
|
||
|
encryptedData = CreateEncryptedDataForBody();
|
||
|
if (CanCanonicalizeAndFragment(writer))
|
||
|
{
|
||
|
message.WriteBodyToSignThenEncryptWithFragments(hashStream, false, null, encryptedData, this.encryptingSymmetricAlgorithm, writer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
message.WriteBodyToSignThenEncrypt(hashStream, encryptedData, this.encryptingSymmetricAlgorithm);
|
||
|
}
|
||
|
this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
|
||
|
this.referenceList.AddReferredId(encryptedData.Id);
|
||
|
this.hasSignedEncryptedMessagePart = true;
|
||
|
return;
|
||
|
case MessagePartProtectionMode.Encrypt:
|
||
|
encryptedData = CreateEncryptedDataForBody();
|
||
|
message.WriteBodyToEncrypt(encryptedData, this.encryptingSymmetricAlgorithm);
|
||
|
this.referenceList.AddReferredId(encryptedData.Id);
|
||
|
return;
|
||
|
case MessagePartProtectionMode.EncryptThenSign:
|
||
|
hashStream = TakeHashStream();
|
||
|
encryptedData = CreateEncryptedDataForBody();
|
||
|
message.WriteBodyToEncryptThenSign(hashStream, encryptedData, this.encryptingSymmetricAlgorithm);
|
||
|
this.signedInfo.AddReference(message.BodyId, hashStream.FlushHashAndGetValue());
|
||
|
this.referenceList.AddReferredId(encryptedData.Id);
|
||
|
return;
|
||
|
default:
|
||
|
Fx.Assert("Invalid MessagePartProtectionMode");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected static MemoryStream CaptureToken(SecurityToken token, SecurityStandardsManager serializer)
|
||
|
{
|
||
|
MemoryStream stream = new MemoryStream();
|
||
|
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
|
||
|
serializer.SecurityTokenSerializer.WriteToken(writer, token);
|
||
|
writer.Flush();
|
||
|
stream.Seek(0, SeekOrigin.Begin);
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
protected static MemoryStream CaptureSecurityElement(ISecurityElement element)
|
||
|
{
|
||
|
MemoryStream stream = new MemoryStream();
|
||
|
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
|
||
|
element.WriteTo(writer, ServiceModelDictionaryManager.Instance);
|
||
|
writer.Flush();
|
||
|
stream.Seek(0, SeekOrigin.Begin);
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
protected override ISecurityElement CompleteEncryptionCore(
|
||
|
SendSecurityHeaderElement primarySignature,
|
||
|
SendSecurityHeaderElement[] basicTokens,
|
||
|
SendSecurityHeaderElement[] signatureConfirmations,
|
||
|
SendSecurityHeaderElement[] endorsingSignatures)
|
||
|
{
|
||
|
if (this.referenceList == null)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (primarySignature != null && primarySignature.Item != null && primarySignature.MarkedForEncryption)
|
||
|
{
|
||
|
EncryptElement(primarySignature);
|
||
|
}
|
||
|
|
||
|
if (basicTokens != null)
|
||
|
{
|
||
|
for (int i = 0; i < basicTokens.Length; ++i)
|
||
|
{
|
||
|
if (basicTokens[i].MarkedForEncryption)
|
||
|
EncryptElement(basicTokens[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (signatureConfirmations != null)
|
||
|
{
|
||
|
for (int i = 0; i < signatureConfirmations.Length; ++i)
|
||
|
{
|
||
|
if (signatureConfirmations[i].MarkedForEncryption)
|
||
|
EncryptElement(signatureConfirmations[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (endorsingSignatures != null)
|
||
|
{
|
||
|
for (int i = 0; i < endorsingSignatures.Length; ++i)
|
||
|
{
|
||
|
if (endorsingSignatures[i].MarkedForEncryption)
|
||
|
EncryptElement(endorsingSignatures[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
return this.referenceList.DataReferenceCount > 0 ? this.referenceList : null;
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
this.referenceList = null;
|
||
|
this.encryptingSymmetricAlgorithm = null;
|
||
|
this.encryptionKeyIdentifier = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override ISignatureValueSecurityElement CompletePrimarySignatureCore(
|
||
|
SendSecurityHeaderElement[] signatureConfirmations,
|
||
|
SecurityToken[] signedEndorsingTokens,
|
||
|
SecurityToken[] signedTokens,
|
||
|
SendSecurityHeaderElement[] basicTokens, bool isPrimarySignature)
|
||
|
{
|
||
|
if (this.signedXml == null)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
SecurityTimestamp timestamp = this.Timestamp;
|
||
|
if (timestamp != null)
|
||
|
{
|
||
|
if (timestamp.Id == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TimestampToSignHasNoId)));
|
||
|
}
|
||
|
HashStream hashStream = TakeHashStream();
|
||
|
this.StandardsManager.WSUtilitySpecificationVersion.WriteTimestampCanonicalForm(
|
||
|
hashStream, timestamp, this.signedInfo.ResourcePool.TakeEncodingBuffer());
|
||
|
signedInfo.AddReference(timestamp.Id, hashStream.FlushHashAndGetValue());
|
||
|
}
|
||
|
|
||
|
if ((this.ShouldSignToHeader) && (this.signatureKey is AsymmetricSecurityKey) && (this.Version.Addressing != AddressingVersion.None))
|
||
|
{
|
||
|
if (this.toHeaderHash != null)
|
||
|
signedInfo.AddReference(this.toHeaderId, this.toHeaderHash);
|
||
|
else
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TransportSecurityRequireToHeader)));
|
||
|
}
|
||
|
|
||
|
AddSignatureReference(signatureConfirmations);
|
||
|
if (isPrimarySignature && this.ShouldProtectTokens)
|
||
|
{
|
||
|
AddPrimaryTokenSignatureReference(this.ElementContainer.SourceSigningToken, this.SigningTokenParameters);
|
||
|
}
|
||
|
|
||
|
if (this.RequireMessageProtection)
|
||
|
{
|
||
|
AddSignatureReference(signedEndorsingTokens, SecurityTokenAttachmentMode.SignedEndorsing);
|
||
|
AddSignatureReference(signedTokens, SecurityTokenAttachmentMode.Signed);
|
||
|
AddSignatureReference(basicTokens);
|
||
|
}
|
||
|
|
||
|
if (this.signedInfo.ReferenceCount == 0)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoPartsOfMessageMatchedPartsToSign)), this.Message);
|
||
|
}
|
||
|
try
|
||
|
{
|
||
|
this.signedXml.ComputeSignature(this.signatureKey);
|
||
|
return this.signedXml;
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
this.hashStream = null;
|
||
|
this.signedInfo = null;
|
||
|
this.signedXml = null;
|
||
|
this.signatureKey = null;
|
||
|
this.effectiveSignatureParts = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EncryptedData CreateEncryptedData()
|
||
|
{
|
||
|
EncryptedData encryptedData = new EncryptedData();
|
||
|
encryptedData.SecurityTokenSerializer = this.StandardsManager.SecurityTokenSerializer;
|
||
|
encryptedData.KeyIdentifier = this.encryptionKeyIdentifier;
|
||
|
encryptedData.EncryptionMethod = this.EncryptionAlgorithm;
|
||
|
encryptedData.EncryptionMethodDictionaryString = this.EncryptionAlgorithmDictionaryString;
|
||
|
return encryptedData;
|
||
|
}
|
||
|
|
||
|
EncryptedData CreateEncryptedData(MemoryStream stream, string id, bool typeElement)
|
||
|
{
|
||
|
EncryptedData encryptedData = CreateEncryptedData();
|
||
|
encryptedData.Id = id;
|
||
|
encryptedData.SetUpEncryption(this.encryptingSymmetricAlgorithm, new ArraySegment<byte>(stream.GetBuffer(), 0, (int) stream.Length));
|
||
|
if (typeElement)
|
||
|
{
|
||
|
encryptedData.Type = EncryptedData.ElementType;
|
||
|
}
|
||
|
return encryptedData;
|
||
|
}
|
||
|
|
||
|
EncryptedData CreateEncryptedDataForBody()
|
||
|
{
|
||
|
EncryptedData encryptedData = CreateEncryptedData();
|
||
|
encryptedData.Type = EncryptedData.ContentType;
|
||
|
return encryptedData;
|
||
|
}
|
||
|
|
||
|
void EncryptAndWriteHeader(MessageHeader plainTextHeader, string id, MemoryStream stream, XmlDictionaryWriter writer)
|
||
|
{
|
||
|
EncryptedHeader encryptedHeader = EncryptHeader(
|
||
|
plainTextHeader,
|
||
|
this.encryptingSymmetricAlgorithm, this.encryptionKeyIdentifier, this.Version,
|
||
|
id, stream);
|
||
|
encryptedHeader.WriteHeader(writer, this.Version);
|
||
|
}
|
||
|
|
||
|
void EncryptElement(SendSecurityHeaderElement element)
|
||
|
{
|
||
|
string id = GenerateId();
|
||
|
ISecurityElement encryptedElement = CreateEncryptedData(CaptureSecurityElement(element.Item), id, true);
|
||
|
this.referenceList.AddReferredId(id);
|
||
|
element.Replace(id, encryptedElement);
|
||
|
}
|
||
|
|
||
|
protected virtual EncryptedHeader EncryptHeader(MessageHeader plainTextHeader, SymmetricAlgorithm algorithm,
|
||
|
SecurityKeyIdentifier keyIdentifier, MessageVersion version, string id, MemoryStream stream)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||
|
SR.GetString(SR.HeaderEncryptionNotSupportedInWsSecurityJan2004, plainTextHeader.Name, plainTextHeader.Namespace)));
|
||
|
}
|
||
|
|
||
|
HashStream TakeHashStream()
|
||
|
{
|
||
|
HashStream hashStream = null;
|
||
|
if (this.hashStream == null)
|
||
|
{
|
||
|
this.hashStream = hashStream = new HashStream(CryptoHelper.CreateHashAlgorithm(this.AlgorithmSuite.DefaultDigestAlgorithm));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hashStream = this.hashStream;;
|
||
|
hashStream.Reset();
|
||
|
}
|
||
|
return hashStream;
|
||
|
}
|
||
|
|
||
|
XmlDictionaryWriter TakeUtf8Writer()
|
||
|
{
|
||
|
return this.signedInfo.ResourcePool.TakeUtf8Writer();
|
||
|
}
|
||
|
|
||
|
MessagePartProtectionMode GetProtectionMode(MessageHeader header)
|
||
|
{
|
||
|
if (!this.RequireMessageProtection)
|
||
|
{
|
||
|
return MessagePartProtectionMode.None;
|
||
|
}
|
||
|
bool sign = this.signedInfo != null && this.effectiveSignatureParts.IsHeaderIncluded(header);
|
||
|
bool encrypt = this.referenceList != null && this.EncryptionParts.IsHeaderIncluded(header);
|
||
|
return MessagePartProtectionModeHelper.GetProtectionMode(sign, encrypt, this.SignThenEncrypt);
|
||
|
}
|
||
|
|
||
|
protected override void StartEncryptionCore(SecurityToken token, SecurityKeyIdentifier keyIdentifier)
|
||
|
{
|
||
|
this.encryptingSymmetricAlgorithm = SecurityUtils.GetSymmetricAlgorithm(this.EncryptionAlgorithm, token);
|
||
|
if (this.encryptingSymmetricAlgorithm == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(
|
||
|
SR.GetString(SR.UnableToCreateSymmetricAlgorithmFromToken, this.EncryptionAlgorithm)));
|
||
|
}
|
||
|
this.encryptionKeyIdentifier = keyIdentifier;
|
||
|
this.referenceList = new ReferenceList();
|
||
|
}
|
||
|
|
||
|
protected override void StartPrimarySignatureCore(SecurityToken token,
|
||
|
SecurityKeyIdentifier keyIdentifier,
|
||
|
MessagePartSpecification signatureParts,
|
||
|
bool generateTargettableSignature)
|
||
|
{
|
||
|
SecurityAlgorithmSuite suite = this.AlgorithmSuite;
|
||
|
string canonicalizationAlgorithm = suite.DefaultCanonicalizationAlgorithm;
|
||
|
XmlDictionaryString canonicalizationAlgorithmDictionaryString = suite.DefaultCanonicalizationAlgorithmDictionaryString;
|
||
|
if (canonicalizationAlgorithm != SecurityAlgorithms.ExclusiveC14n)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new MessageSecurityException(SR.GetString(SR.UnsupportedCanonicalizationAlgorithm, suite.DefaultCanonicalizationAlgorithm)));
|
||
|
}
|
||
|
string signatureAlgorithm;
|
||
|
XmlDictionaryString signatureAlgorithmDictionaryString;
|
||
|
suite.GetSignatureAlgorithmAndKey(token, out signatureAlgorithm, out this.signatureKey, out signatureAlgorithmDictionaryString);
|
||
|
string digestAlgorithm = suite.DefaultDigestAlgorithm;
|
||
|
XmlDictionaryString digestAlgorithmDictionaryString = suite.DefaultDigestAlgorithmDictionaryString;
|
||
|
this.signedInfo = new PreDigestedSignedInfo(ServiceModelDictionaryManager.Instance, canonicalizationAlgorithm, canonicalizationAlgorithmDictionaryString, digestAlgorithm, digestAlgorithmDictionaryString, signatureAlgorithm, signatureAlgorithmDictionaryString);
|
||
|
this.signedXml = new SignedXml(this.signedInfo, ServiceModelDictionaryManager.Instance, this.StandardsManager.SecurityTokenSerializer);
|
||
|
if (keyIdentifier != null)
|
||
|
{
|
||
|
this.signedXml.Signature.KeyIdentifier = keyIdentifier;
|
||
|
}
|
||
|
if (generateTargettableSignature)
|
||
|
{
|
||
|
this.signedXml.Id = GenerateId();
|
||
|
}
|
||
|
this.effectiveSignatureParts = signatureParts;
|
||
|
this.hashStream = this.signedInfo.ResourcePool.TakeHashStream(digestAlgorithm);
|
||
|
}
|
||
|
|
||
|
protected override ISignatureValueSecurityElement CreateSupportingSignature(SecurityToken token, SecurityKeyIdentifier identifier)
|
||
|
{
|
||
|
StartPrimarySignatureCore(token, identifier, MessagePartSpecification.NoParts, false);
|
||
|
return CompletePrimarySignatureCore(null, null, null, null, false);
|
||
|
}
|
||
|
|
||
|
protected override ISignatureValueSecurityElement CreateSupportingSignature(SecurityToken token, SecurityKeyIdentifier identifier, ISecurityElement elementToSign)
|
||
|
{
|
||
|
SecurityAlgorithmSuite algorithmSuite = this.AlgorithmSuite;
|
||
|
string signatureAlgorithm;
|
||
|
XmlDictionaryString signatureAlgorithmDictionaryString;
|
||
|
SecurityKey signatureKey;
|
||
|
algorithmSuite.GetSignatureAlgorithmAndKey(token, out signatureAlgorithm, out signatureKey, out signatureAlgorithmDictionaryString);
|
||
|
SignedXml signedXml = new SignedXml(ServiceModelDictionaryManager.Instance, this.StandardsManager.SecurityTokenSerializer);
|
||
|
SignedInfo signedInfo = signedXml.Signature.SignedInfo;
|
||
|
signedInfo.CanonicalizationMethod = algorithmSuite.DefaultCanonicalizationAlgorithm;
|
||
|
signedInfo.CanonicalizationMethodDictionaryString = algorithmSuite.DefaultCanonicalizationAlgorithmDictionaryString;
|
||
|
signedInfo.SignatureMethod = signatureAlgorithm;
|
||
|
signedInfo.SignatureMethodDictionaryString = signatureAlgorithmDictionaryString;
|
||
|
|
||
|
if (elementToSign.Id == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ElementToSignMustHaveId)));
|
||
|
}
|
||
|
Reference reference = new Reference(ServiceModelDictionaryManager.Instance, "#" + elementToSign.Id, elementToSign);
|
||
|
reference.DigestMethod = algorithmSuite.DefaultDigestAlgorithm;
|
||
|
reference.DigestMethodDictionaryString = algorithmSuite.DefaultDigestAlgorithmDictionaryString;
|
||
|
reference.AddTransform(new ExclusiveCanonicalizationTransform());
|
||
|
((StandardSignedInfo)signedInfo).AddReference(reference);
|
||
|
|
||
|
signedXml.ComputeSignature(signatureKey);
|
||
|
if (identifier != null)
|
||
|
{
|
||
|
signedXml.Signature.KeyIdentifier = identifier;
|
||
|
}
|
||
|
return signedXml;
|
||
|
}
|
||
|
|
||
|
protected override void WriteSecurityTokenReferencyEntry(XmlDictionaryWriter writer, SecurityToken securityToken, SecurityTokenParameters securityTokenParameters)
|
||
|
{
|
||
|
SecurityKeyIdentifierClause keyIdentifierClause = null;
|
||
|
|
||
|
// Given a token this method writes its corresponding security token reference entry in the security header
|
||
|
// 1. If the token parameters is an issuedSecurityTokenParamter
|
||
|
// 2. If UseStrTransform is enabled on it.
|
||
|
|
||
|
IssuedSecurityTokenParameters issuedSecurityTokenParameters = securityTokenParameters as IssuedSecurityTokenParameters;
|
||
|
if (issuedSecurityTokenParameters == null || !issuedSecurityTokenParameters.UseStrTransform)
|
||
|
return;
|
||
|
|
||
|
if (this.ElementContainer.TryGetIdentifierClauseFromSecurityToken(securityToken, out keyIdentifierClause))
|
||
|
{
|
||
|
if (keyIdentifierClause != null && !String.IsNullOrEmpty(keyIdentifierClause.Id))
|
||
|
{
|
||
|
WrappedXmlDictionaryWriter wrappedLocalWriter = new WrappedXmlDictionaryWriter(writer, keyIdentifierClause.Id);
|
||
|
this.StandardsManager.SecurityTokenSerializer.WriteKeyIdentifierClause(wrappedLocalWriter, keyIdentifierClause);
|
||
|
}
|
||
|
else
|
||
|
throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenManagerCannotCreateTokenReference)), this.Message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class WrappedXmlDictionaryWriter : XmlDictionaryWriter
|
||
|
{
|
||
|
XmlDictionaryWriter innerWriter;
|
||
|
int index;
|
||
|
bool insertId;
|
||
|
bool isStrReferenceElement;
|
||
|
string id;
|
||
|
|
||
|
public WrappedXmlDictionaryWriter(XmlDictionaryWriter writer, string id)
|
||
|
{
|
||
|
this.innerWriter = writer;
|
||
|
this.index = 0;
|
||
|
this.insertId = false;
|
||
|
this.isStrReferenceElement = false;
|
||
|
this.id = id;
|
||
|
}
|
||
|
|
||
|
public override void WriteStartAttribute(string prefix, string localName, string namespaceUri)
|
||
|
{
|
||
|
if (isStrReferenceElement && this.insertId && localName == XD.UtilityDictionary.IdAttribute.Value)
|
||
|
{
|
||
|
// This means the serializer is already writing the Id out, so we don't write it again.
|
||
|
this.insertId = false;
|
||
|
}
|
||
|
this.innerWriter.WriteStartAttribute(prefix, localName, namespaceUri);
|
||
|
}
|
||
|
|
||
|
public override void WriteStartElement(string prefix, string localName, string namespaceUri)
|
||
|
{
|
||
|
if (isStrReferenceElement && this.insertId)
|
||
|
{
|
||
|
if (id != null)
|
||
|
{
|
||
|
this.innerWriter.WriteAttributeString(XD.UtilityDictionary.Prefix.Value, XD.UtilityDictionary.IdAttribute, XD.UtilityDictionary.Namespace, id);
|
||
|
}
|
||
|
|
||
|
isStrReferenceElement = false;
|
||
|
this.insertId = false;
|
||
|
}
|
||
|
|
||
|
index++;
|
||
|
|
||
|
if (index == 1 && localName == XD.SecurityJan2004Dictionary.SecurityTokenReference.Value)
|
||
|
{
|
||
|
this.insertId = true;
|
||
|
isStrReferenceElement = true;
|
||
|
}
|
||
|
|
||
|
this.innerWriter.WriteStartElement(prefix, localName, namespaceUri);
|
||
|
}
|
||
|
|
||
|
// Below methods simply call into innerWritter
|
||
|
public override void Close()
|
||
|
{
|
||
|
this.innerWriter.Close();
|
||
|
}
|
||
|
|
||
|
public override void Flush()
|
||
|
{
|
||
|
this.innerWriter.Flush();
|
||
|
}
|
||
|
|
||
|
public override string LookupPrefix(string ns)
|
||
|
{
|
||
|
return this.innerWriter.LookupPrefix(ns);
|
||
|
}
|
||
|
|
||
|
public override void WriteBase64(byte[] buffer, int index, int count)
|
||
|
{
|
||
|
this.innerWriter.WriteBase64(buffer, index, count);
|
||
|
}
|
||
|
|
||
|
public override void WriteCData(string text)
|
||
|
{
|
||
|
this.innerWriter.WriteCData(text);
|
||
|
}
|
||
|
|
||
|
public override void WriteCharEntity(char ch)
|
||
|
{
|
||
|
this.innerWriter.WriteCharEntity(ch);
|
||
|
}
|
||
|
|
||
|
public override void WriteChars(char[] buffer, int index, int count)
|
||
|
{
|
||
|
this.innerWriter.WriteChars(buffer, index, count);
|
||
|
}
|
||
|
|
||
|
public override void WriteComment(string text)
|
||
|
{
|
||
|
this.innerWriter.WriteComment(text);
|
||
|
}
|
||
|
|
||
|
public override void WriteDocType(string name, string pubid, string sysid, string subset)
|
||
|
{
|
||
|
this.innerWriter.WriteDocType(name, pubid, sysid, subset);
|
||
|
}
|
||
|
|
||
|
public override void WriteEndAttribute()
|
||
|
{
|
||
|
this.innerWriter.WriteEndAttribute();
|
||
|
}
|
||
|
|
||
|
public override void WriteEndDocument()
|
||
|
{
|
||
|
this.innerWriter.WriteEndDocument();
|
||
|
}
|
||
|
|
||
|
public override void WriteEndElement()
|
||
|
{
|
||
|
this.innerWriter.WriteEndElement();
|
||
|
}
|
||
|
|
||
|
public override void WriteEntityRef(string name)
|
||
|
{
|
||
|
this.innerWriter.WriteEntityRef(name);
|
||
|
}
|
||
|
|
||
|
public override void WriteFullEndElement()
|
||
|
{
|
||
|
this.innerWriter.WriteFullEndElement();
|
||
|
}
|
||
|
|
||
|
public override void WriteProcessingInstruction(string name, string text)
|
||
|
{
|
||
|
this.innerWriter.WriteProcessingInstruction(name, text);
|
||
|
}
|
||
|
|
||
|
public override void WriteRaw(string data)
|
||
|
{
|
||
|
this.innerWriter.WriteRaw(data);
|
||
|
}
|
||
|
|
||
|
public override void WriteRaw(char[] buffer, int index, int count)
|
||
|
{
|
||
|
this.innerWriter.WriteRaw(buffer, index, count);
|
||
|
}
|
||
|
|
||
|
public override void WriteStartDocument(bool standalone)
|
||
|
{
|
||
|
this.innerWriter.WriteStartDocument(standalone);
|
||
|
}
|
||
|
|
||
|
public override void WriteStartDocument()
|
||
|
{
|
||
|
this.innerWriter.WriteStartDocument();
|
||
|
}
|
||
|
|
||
|
public override WriteState WriteState
|
||
|
{
|
||
|
get { return this.innerWriter.WriteState; }
|
||
|
}
|
||
|
|
||
|
public override void WriteString(string text)
|
||
|
{
|
||
|
this.innerWriter.WriteString(text);
|
||
|
}
|
||
|
|
||
|
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
|
||
|
{
|
||
|
this.innerWriter.WriteSurrogateCharEntity(lowChar, highChar);
|
||
|
}
|
||
|
|
||
|
public override void WriteWhitespace(string ws)
|
||
|
{
|
||
|
this.innerWriter.WriteWhitespace(ws);
|
||
|
}
|
||
|
}
|
||
|
}
|