diff --git a/eng/SendToHelix.proj b/eng/SendToHelix.proj
index a7aa7ca3a04..8aec9ec4119 100644
--- a/eng/SendToHelix.proj
+++ b/eng/SendToHelix.proj
@@ -25,7 +25,7 @@
-
+
diff --git a/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs b/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs
index fa2121a2fa9..a1d35b58368 100644
--- a/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs
+++ b/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Runtime;
using System.ServiceModel.Channels;
+using System.Threading.Tasks;
using System.Xml;
using IPrefixGenerator = System.IdentityModel.IPrefixGenerator;
@@ -138,6 +139,33 @@ protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
}
}
+ protected override async Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer)
+ {
+ switch (_state)
+ {
+ case BodyState.Created:
+ await InnerMessage.WriteBodyContentsAsync(writer);
+ return;
+ case BodyState.Signed:
+ case BodyState.EncryptedThenSigned:
+ XmlDictionaryReader reader = _fullBodyBuffer.GetReader(0);
+ reader.ReadStartElement();
+ while (reader.NodeType != XmlNodeType.EndElement)
+ {
+ await writer.WriteNodeAsync(reader, false);
+ }
+
+ reader.ReadEndElement();
+ reader.Close();
+ return;
+ case BodyState.Encrypted:
+ case BodyState.SignedThenEncrypted:
+ throw ExceptionHelper.PlatformNotSupported();
+ default:
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBadStateException(nameof(OnWriteBodyContentsAsync)));
+ }
+ }
+
protected override void OnWriteMessage(XmlDictionaryWriter writer)
{
// For Kerb one shot, the channel binding will be need to be fished out of the message, cached and added to the
@@ -197,6 +225,65 @@ protected override void OnWriteMessage(XmlDictionaryWriter writer)
writer.WriteEndElement();
}
+ public override async Task OnWriteMessageAsync(XmlDictionaryWriter writer)
+ {
+ // For Kerb one shot, the channel binding will be need to be fished out of the message, cached and added to the
+ // token before calling ISC.
+
+ AttachChannelBindingTokenIfFound();
+
+ EnsureUniqueSecurityApplication();
+
+ MessagePrefixGenerator prefixGenerator = new MessagePrefixGenerator(writer);
+ _securityHeader.StartSecurityApplication();
+
+ Headers.Add(_securityHeader);
+
+ InnerMessage.WriteStartEnvelope(writer);
+
+ Headers.RemoveAt(Headers.Count - 1);
+
+ _securityHeader.ApplyBodySecurity(writer, prefixGenerator);
+
+ InnerMessage.WriteStartHeaders(writer);
+ _securityHeader.ApplySecurityAndWriteHeaders(Headers, writer, prefixGenerator);
+
+ _securityHeader.RemoveSignatureEncryptionIfAppropriate();
+
+ _securityHeader.CompleteSecurityApplication();
+ _securityHeader.WriteHeader(writer, Version);
+ await writer.WriteEndElementAsync();
+
+ if (_fullBodyFragment != null)
+ {
+ ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_fullBodyFragment, 0, _fullBodyFragmentLength);
+ }
+ else
+ {
+ if (_startBodyFragment != null)
+ {
+ ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_startBodyFragment.GetBuffer(), 0, (int)_startBodyFragment.Length);
+ }
+ else
+ {
+ OnWriteStartBody(writer);
+ }
+
+ await OnWriteBodyContentsAsync(writer);
+
+ if (_endBodyFragment != null)
+ {
+ ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_endBodyFragment.GetBuffer(), 0, (int)_endBodyFragment.Length);
+ }
+ else
+ {
+ writer.WriteEndElement();
+ }
+ }
+
+ await writer.WriteEndElementAsync();
+ }
+
private void AttachChannelBindingTokenIfFound()
{
// Only valid when using a TokenParameter of type KerberosSecurityTokenParameters which isn't currently supported
diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs
index 72a2654729a..3d497e65706 100644
--- a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs
+++ b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs
@@ -12,7 +12,7 @@ public class BasicHttpTransportWithMessageCredentialSecurityTests : ConditionalW
nameof(Client_Certificate_Installed),
nameof(SSL_Available))]
[OuterLoop]
- public static void Https_SecModeTransWithMessCred_CertClientCredential_Succeeds()
+ public static void BasicHttps_SecModeTransWithMessCred_CertClientCredential_Succeeds()
{
string clientCertThumb = null;
EndpointAddress endpointAddress = null;
@@ -54,6 +54,56 @@ public static void Https_SecModeTransWithMessCred_CertClientCredential_Succeeds(
}
}
+ [WcfTheory]
+ [Condition(nameof(Root_Certificate_Installed),
+ nameof(SSL_Available))]
+ [OuterLoop]
+ [InlineData(TransferMode.Buffered)]
+ [InlineData(TransferMode.Streamed)]
+ public static void BasicHttps_SecModeTransWithMessCred_UserNameClientCredential_Succeeds(TransferMode transferMode)
+ {
+ EndpointAddress endpointAddress = null;
+ string testString = "Hello";
+ string username = null;
+ string password = null;
+ ChannelFactory factory = null;
+ IWcfService serviceProxy = null;
+
+ try
+ {
+ // *** SETUP *** \\
+ BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
+ binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
+ binding.TransferMode = transferMode;
+ endpointAddress = new EndpointAddress(new Uri(Endpoints.BasicHttps_SecModeTransWithMessCred_ClientCredTypeUserName + $"/{Enum.GetName(typeof(TransferMode), transferMode)}"));
+
+ factory = new ChannelFactory(binding, endpointAddress);
+ username = Guid.NewGuid().ToString("n").Substring(0, 8);
+ char[] usernameArr = username.ToCharArray();
+ Array.Reverse(usernameArr);
+ password = new string(usernameArr);
+ factory.Credentials.UserName.UserName = username;
+ factory.Credentials.UserName.Password = password;
+
+ serviceProxy = factory.CreateChannel();
+
+ // *** EXECUTE *** \\
+ string result = serviceProxy.Echo(testString);
+
+ // *** VALIDATE *** \\
+ Assert.Equal(testString, result);
+
+ // *** CLEANUP *** \\
+ ((ICommunicationObject)serviceProxy).Close();
+ factory.Close();
+ }
+ finally
+ {
+ // *** ENSURE CLEANUP *** \\
+ ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
+ }
+ }
+
[WcfFact]
[Condition(nameof(Root_Certificate_Installed),
nameof(SSL_Available))]
diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs
index 4dff329c664..f367b965530 100644
--- a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs
+++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
@@ -11,13 +12,25 @@ namespace WcfService
[TestServiceDefinition(Schema = ServiceSchema.HTTPS, BasePath = "BasicHttpsTransSecMessCredsUserName.svc")]
internal class BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost : TestServiceHostBase
{
- protected override string Address { get { return "https-message-credentials-username"; } }
+ protected override IList GetBindings()
+ {
+ return new List { GetBufferedBinding(), GetStreamedBinding() };
+ }
- protected override Binding GetBinding()
+ protected Binding GetBufferedBinding()
{
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
+ binding.Name = "https-message-credentials-username/Buffered";
+ return binding;
+ }
+ protected Binding GetStreamedBinding()
+ {
+ BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
+ binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
+ binding.TransferMode = TransferMode.Streamed;
+ binding.Name = "https-message-credentials-username/Streamed";
return binding;
}