How to get specific RequestSecurityToken serialization - c#

I'm new to WCF, .NET, web services and everything - in fact, I was mainly a java & SQL coder until I took on my current job.
The task at hand: Portions of our customers' data in our database needs to be exported regularly to a database that is provided by a third party and accessible through a web service, which in turn is federation-secured with an STS provided by a fourth party.
After hours spent with confusing MSDN documentation and tons of blog posts on WCF, I still can't get the STS to talk to me, all I get is a (400) Bad Request..
My code:
private static SecurityToken RequestSecurityToken()
{
WSHttpBinding binding = new WSHttpBinding();
binding.AllowCookies = true;
WSHttpSecurity security = new WSHttpSecurity();
security.Mode = SecurityMode.TransportWithMessageCredential;
security.Message.ClientCredentialType = MessageCredentialType.Certificate;
security.Message.NegotiateServiceCredential = true;
security.Message.EstablishSecurityContext = false;
binding.Security = security;
WSTrustChannelFactory factory = new WSTrustChannelFactory(binding, "https://FOURTH_PARTY_STS");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "CUSTOMER_CERTIFICATE");
RequestSecurityToken rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointReference("https://THIRD_PARTY_WS"),
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
};
rst.Claims.Dialect = "http://docs.oasis-open.org/wsfed/authorization/200706/authclaims"; // Taken from an exception message
rst.Claims.Add(new RequestClaim("urn:tgic:names:ISTS:1.0:user:PartnerId", false, "CUSTOMER_ID"));
return factory.CreateChannel().Issue(rst);
}
produces this SOAP body:
<trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>https://THIRD_PARTY_WS</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<trust:Claims Dialect="http://docs.oasis-open.org/wsfed/authorization/200706/authclaims" xmlns:auth="http://docs.oasis-open.org/wsfed/authorization/200706">
<auth:ClaimType Uri="urn:tgic:names:ISTS:1.0:user:PartnerId" Optional="true">
<auth:Value>CUSTOMER_ID</auth:Value>
</auth:ClaimType>
</trust:Claims>
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
<trust:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</trust:TokenType>
</trust:RequestSecurityToken>
According to the fourth party's documentation, the STS expects something like this:
<wst:RequestSecurityToken
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsp15="http://www.w3.org/ns/ws-policy"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
Context="1aae57c8-092c-47a4-a5eb-c2ecbc21441d">
<wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>
<wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
<wsp:AppliesTo>
<wsp15:URI>https://THIRD_PARTY_WS</wsp15:URI>
</wsp:AppliesTo>
<wst:Claims Dialect="urn:oasis:names:tc:SAML:2.0:assertion:AttributeStatementType">
<saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Attribute Name="urn:tgic:names:ISTS:1.0:user:PartnerId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue>9330931615</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</wst:Claims>
<wst:Lifetime>
<wsu:Created>2013-09-17T18:18:10Z</wsu:Created>
<wsu:Expires>2013-09-17T18:23:10Z</wsu:Expires>
</wst:Lifetime>
</wst:RequestSecurityToken>
Somewhere else in that documentation it's stated that Context and Lifetime are optional. Therefore, as far as I can see, I have two issues:
How do I get the AppliesTo address to be serialized as URI element?
How do I get the Claims to be serialized as Attribute/AttributeValue elements (with correct dialect)?
Do I have to implement some custom serialization? If so, how and where do I hook it into the factory/binding/request?

To work with STS you have to use WS2007HttpBinding instead of WSHttpBinding. You can find STS example in WIF SDK. It contain an example with custom token. Look here
Try issue securitytoken without additional client parameters. Make a working solution that just issue token and call RP method.
As far as I understand you want pass to STS custom client credential PartnerId. In my project I'm using passing custom credentials through the request's AdditionalContext.
rst.AdditionalContext = new AdditionalContext();
rst.AdditionalContext.Items.Add(new ContextItem(new Uri("you_any_uri"), PartnerId));
This way does not require any serializers.
Another way to pass client credentials's: using RequestSecurityToken.Properties. In this case you must implement a custom request serializer.
var channelFactory = new WSTrustChannelFactory(binging, endpoint)
{
TrustVersion = TrustVersion.WSTrust13
};
channelFactory.WSTrustRequestSerializer = CustomRequestSerializer;
Here implementation of custom WSTrust13RequestSerializer
public class CustomRequestSerializer: WSTrust13RequestSerializer
{
public override void WriteXmlElement(XmlWriter writer, string elementName, object elementValue, RequestSecurityToken rst,
WSTrustSerializationContext context)
{
var parameters = new string[1] {"paramname"};
if (parameters.Any(p => p == elementName))
{
writer.WriteElementString(elementName, (string)elementValue);
}
else
{
base.WriteXmlElement(writer, elementName, elementValue, rst, context);
}
}
public override void ReadXmlElement(XmlReader reader, RequestSecurityToken rst, WSTrustSerializationContext context)
{
var parameters = new string[1] {"paramname"};
var key = parameters.FirstOrDefault(reader.IsStartElement);
if (!string.IsNullOrWhiteSpace(key))
{
rst.Properties.Add(key, reader.ReadElementContentAsString());
return;
}
base.ReadXmlElement(reader, rst, context);
}
}

Related

Programmatically have Client application list a WCF service's Endpoints created during runtime using a URL (like WCF Test Client does)

Maybe I'm not searching with the right terms because this seems pretty simple. I'm just looking for a way to list the endpoints of a WCF service that has it's endpoints created during runtime the same way WCF Test Client Does.
Specify URL
Get MetaData and Endpoints
This is how I add the endpoints during runtime
string SetInstrumentsURL = serviceUrl + "SetInstruments/";
string SetInstrumentsPipe = "net.pipe://localhost/TestService/SetInstruments/";
ServiceHost SetInstrumentsHost = null;
var SetInstruments = InstrumentLoader.Factory.GetIEnumerableOf<ISetInstrument>();
if (SetInstruments.Count() > 0)
{
Uri SetInstrumentsURI = new Uri(SetInstrumentsURL);
Uri SetInstrumentsPipedURI = new Uri(SetInstrumentsPipe);
NetTcpBinding netTcpBindingSetInstruments = new NetTcpBinding();
NetNamedPipeBinding NamedPipeBindingSetInstruments = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
SetInstrumentsHost = new ServiceHost(typeof(TeraSetInstrumentService), new Uri[] { SetInstrumentsURI, SetInstrumentsPipedURI });
ServiceMetadataBehavior SetInstrumentServiceMetadataBehavior = new ServiceMetadataBehavior();
SetInstrumentsHost.Description.Behaviors.Add(SetInstrumentServiceMetadataBehavior);
SetInstrumentsHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
SetInstrumentsHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexNamedPipeBinding(), "mex");
foreach (var setter in SetInstruments)
{
SetInstrumentsHost.AddServiceEndpoint(typeof(ISetInstrumentService), netTcpBindingSetInstruments, SetInstrumentsURL + setter.Name).Name = "Set_" + setter.Name.Replace(" ", "_");
SetInstrumentsHost.AddServiceEndpoint(typeof(ISetInstrumentService), NamedPipeBindingSetInstruments, SetInstrumentsPipe + setter.Name).Name = "Set_" + setter.Name.Replace(" ", "_");
}
SetInstrumentsHost.Open();
}
What functions can I use from the client side to access those same endpoints as WCF Test Client? I know how to connect to those endpoints if I already have the Endpoint's URL but I would like to have a list of the endpoints so I can create a drop down list choose from that changes depending on what host you connect to.
Adding a service reference through Visual Studio doesn't list all of the endpoints because the are not created yet. Is the a library I can use to get them at run time like WCF Test Client does.
Provided that we have the service metadata URI, we could use MetadataExchangeClientMode and MetadataResolver class which is provided in the System.ServiceModel.Description namespace to retrieve and process the metadata.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-metadataresolver-to-obtain-binding-metadata-dynamically
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-metadataexchangeclient-to-retrieve-metadata
I have made a simple example, wish it is useful to you.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://10.157.13.69:3336/mex");
MetadataExchangeClient client = new MetadataExchangeClient(uri, MetadataExchangeClientMode.MetadataExchange);
MetadataSet metadata = client.GetMetadata();
WsdlImporter importer = new WsdlImporter(metadata);
ServiceEndpointCollection endpoints = importer.ImportAllEndpoints();
//ServiceEndpointCollection endpoints = MetadataResolver.Resolve(typeof(IService), uri, MetadataExchangeClientMode.MetadataExchange);
foreach (var item in endpoints)
{
Console.WriteLine(item.Address.Uri);
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
Result
Okay it was pretty easy to do, I just needed to use the MetadataExchangeClient to get the config. In the code all I had to do to get the MetaData Xml was this:
var meta = new System.ServiceModel.Description.MetadataExchangeClient(new Uri("net.tcp://10.0.2.124:9000/TeraService/SetInstruments/mex"), System.ServiceModel.Description.MetadataExchangeClientMode.MetadataExchange);
var data = meta.GetMetadata();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(data.GetType());
TextWriter writer = new StreamWriter("xmlfile.xml");
x.Serialize(writer, data);
writer.Close();
I posted my answer in the wrong place. But Abraham Qian has a more elegant solution that I will test now.

.Net Core 2.0 call to WCF client configuration

I have a .NET Core 2.0 application and need to call a WCF client from one of its controllers, and pass the user credentials for authentication.
Within the .net core app I created a reference for the WCF client using the Connected Services (WCF Web Service Reference Provider) and now in a process of configuring the call. Note that I can use the same endpoint form a 4.6 framework application without any problems.
Here's my code:
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport}};
var address = new EndpointAddress("https://my-endpoint.asmx");
var client = new MyAppSoapClient(binding, address);
var credentials = CredentialCache.DefaultNetworkCredentials;
client.ClientCredentials.Windows.ClientCredential = credentials;
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
var response = client.GetStuff("param").Result;
I face a number of problems:
It has to be a https call
I need to pass the currently log in user credentials to the call
The current error I get is as follows:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate, NTLM'
Also the ConnectedService.json (created automativcally by WCF Web Service Reference Provider) has a predefined endpoint Uri.. I don't understand why I need to pass the address to the client manually (the code seems to be forcing me to do so).. ideally I'd like to get this dynamically amended in json depending on environment.
Thanks.
I noticed that you passed the current logged-in user as a Windows credential (which is also necessary for enabling impersonation), but you did not explicitly set the client credentials for the transport layer security.
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Also the ConnectedService.json (created automativcally by WCF Web
Service Reference Provider) has a predefined endpoint Uri.. I don't
understand why I need to pass the address to the client manually (the
code seems to be forcing me to do so)
You can modify the method of automatic generation of proxy client to construct client proxy class (located in the reference.cs)
Modify the binding security
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
Modify the endpoint.
private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
return new System.ServiceModel.EndpointAddress("http://10.157.13.69:8001/webservice1.asmx");
Construct the client proxy class.
ServiceReference1.WebService1SoapClient client = new WebService1SoapClient(WebService1SoapClient.EndpointConfiguration.WebService1Soap);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "123456";
Feel free to let me know if there is anything I can help with.
My binding was missing the security Ntlm credential type (see below).
Problem solved.
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport,
Transport = new HttpTransportSecurity(){ClientCredentialType = HttpClientCredentialType.Ntlm } }};

Authenticating against ReportExecution2005.asmx in .NET Core

I'm trying to execute an SSRS report in .NET Core.
Since .NET Core doesn't let you add service references, you have to use the WCF Connected Service to add a reference to the WSDL so it can generate .NET Core compatible code. This is what I did for ReportExecution2005.asmx (SQL Server 2016 if it matters).
I tried using the following to authenticate against the service:
var rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
new EndpointAddress("http://server/ReportServer/ReportExecution2005.asmx"))
{
ClientCredentials =
{
Windows =
{
AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation,
ClientCredential = new NetworkCredential("username", "password")
}
}
};
Also tried setting the Username object instead of Windows object, but either way the result is the following error:
MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'.
Looking at Fiddler, the code isn't passing the credentials along.
This is the code that got generated off the WSDL
public ReportExecutionServiceSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress)
: base(ReportExecutionServiceSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
I may be mistaken, but isn't this calling the private method ConfigureEndpoint with the ClientCredentials object before the ClientCredentials object has even been set?
I'm not seeing any other way to configure the ClientCredentials or call ConfigureEndpoint, so how exactly are you supposed to authenticate? The other constructors are basically the same thing, except for one which takes in a Binding instead of an EndpointConfiguration. Any ideas?
After fighting with this for a day, I found an approach that seems to work, by using the only constructor that does not immediately call ConfigureEndpoint as pointed out in the question. If I create a binding that specifies NTLM, and I pass that binding along with a manually created endpoint, it works:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {ClientCredentialType = HttpClientCredentialType.Ntlm}
}
};
var reportService = new CssbiReportService.ReportExecutionServiceSoapClient(binding,
new EndpointAddress("http://myserver/ReportServer/ReportExecution2005.asmx"));
This is working for me in .NET Core.
Edit: update the code for .NET Core
Unfortunately, I don't have SSRS here to test the code right now.
But, try this code (no error check):
// parameters of report (if any)
ParameterValue[] parameters = {new ParameterValue {Name = "ApontamentoID", Value = "364"}};
// connect to the service
ReportExecutionServiceSoapClient webServiceProxy =
new ReportExecutionServiceSoapClient(
ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap,
"http://report_server_url/ReportExecution2005.asmx?wsdl");
// logon the user
await webServiceProxy.LogonUserAsync("username", "password", null);
// ask for the report
await webServiceProxy.LoadReportAsync("/report_path", null);
await webServiceProxy.SetExecutionParametersAsync(parameters, null);
// you can use RenderStreamRequest too
RenderRequest request = new RenderRequest("pdf", null);
RenderResponse response = await webServiceProxy.RenderAsync(request);
// save to the disk
System.IO.File.WriteAllBytes(#"c:\temp\output.pdf", response.Result);
// logoff the user
await webServiceProxy.LogoffAsync();
// close
await webServiceProxy.CloseAsync();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
{
Security =
{
Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.Ntlm
}
}
};
yourClient = ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress) {
ClientCredentials =
{ ...
^^^ This for NTLM.
Also, I was getting read-only errors trying to set some properties on the client after it had been created. In case it helps someone, properties must all be set at client-creation time to avoid this as per "yourClient" above.
I had the same problem, for me the following addition was helpful:
ReportExecutionServiceSoapClient rsClient = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

Implementing ACS with and ADFS as STS

We are attempting to use the ACS sample 4 (from http://claimsid.codeplex.com/) as template for our ADFS project.
We have no problem with passive requests to ADFS authenticated services. In the sample, the federation provider is a custom STS, and the sample works fine.
Now we wish to replace the custom federation provider (Adatum FP in the sample) with our own ADFS.
Our setup right now is as follows (Namespaces hidden)
ServiceClient: Console Applicaton, calls Services
Services: WCF Webservice, Single method returning a string. This as is default
[Ordertracking.Services in sample]
Services.Authentication: Our custom Identity Provider. This is as default [Litware.SimulatedIssuer in sample]
ADFS: Our Federation Provider [FederationProvider.Adatum in
example]
ServiceClient wants to call Services, and from configuration it knows it has to get a token from IP (Services.Authentication). The token is then passed to ADFS, who validates the token
and sends a new token back to ServiceClient. The client new passes the FP token to the service, and the service (being a relying party on ADFS) verifies the token against ADFS, and performs the service method.
The issue:
Replacing the STS in the example with ADFS, seems to break the integration. We seem to be getting the token from the IP back correctly, but we are running into problems when passing the IP token to ADFS.
It seems that we have a problem with our Audience Uri's, but we have added
https://'adfs fqdn'/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256
Client Exception
We get an MessageSecurityException in the client with this InnerException
InnerException
{"ID3242: The security token could not be authenticated or authorized."}
[System.ServiceModel.FaultException]: {"ID3242: The security token could not be authenticated or authorized."}
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
InnerException: null
Message: "ID3242: The security token could not be authenticated or authorized."
Source: null
StackTrace: null
TargetSite: null
ADFS Debug log
<TraceRecord xmlns="http://schemas.microsoft.com/2009/10/IdentityModel/TraceRecord" Severity="Error">
<Description>Handled exception.</Description>
<AppDomain>Microsoft.IdentityServer.ServiceHost.exe</AppDomain>
<Exception>
<ExceptionType>Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</ExceptionType>
<Message>ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris. Audience: 'https://<adfs fqdn>/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256'</Message>
<StackTrace>
at Microsoft.IdentityModel.Tokens.SamlSecurityTokenRequirement.ValidateAudienceRestriction(IList`1 allowedAudienceUris, IList`1 tokenAudiences) at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2Conditions conditions, Boolean enforceAudienceRestriction) at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateToken(SecurityToken token) at Microsoft.IdentityServer.Service.Tokens.MSISSaml2TokenHandler.ValidateToken(SecurityToken token) at Microsoft.IdentityModel.Tokens.WrappedSaml2SecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token) at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token) at Microsoft.IdentityModel.Tokens.WrappedSamlSecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token) at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token) at System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver, IList`1 allowedTokenAuthenticators, SecurityTokenAuthenticator&amp; usedTokenAuthenticator) at
....
</StackTrace>
</Exception>
</TraceRecord>
We have added the audience uri to our IP Web.config:
<audienceUris mode="Always">
<add value="https://<adfs fqdn>/adfs/services/Trust/13/IssuedTokenMixedSymmetricBasic256" />
</audienceUris>
If necessary we can post additional config files and screenshots of ADFS configuration.
This took a bit of work but we finally solved the problem. Instead of configuring this, we built the connection in code. I'm thinking we probably had an error somewhere in the client configuration.
Some advice to anyone trying this - build the connections in code first. The XML configuration is a bit harder to work with.
We found some sample code on leastprivilege.com
private static SecurityToken GetIdPToken()
{
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
"https://systemidp.dk/Issuer.svc");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = "LITWARE\\rick";
factory.Credentials.UserName.Password = "thisPasswordIsNotChecked";
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("https://adfsfqdn/adfs/services/trust"),
KeyType = WSTrust13Constants.KeyTypes.Symmetric,
ReplyTo = "https://adfsfqdn/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256/"
};
factory.ConfigureChannelFactory();
var channel = factory.CreateChannel();
return channel.Issue(rst);
}
private static SecurityToken GetRSTSToken(SecurityToken idpToken)
{
var binding = new IssuedTokenWSTrustBinding();
binding.SecurityMode = SecurityMode.TransportWithMessageCredential;
var factory = new WSTrustChannelFactory(
binding,
"https://adfsfqdn/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256/");
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.SupportInteractive = false;
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress("https://services.dk/WebService.svc"),
KeyType = WSTrust13Constants.KeyTypes.Symmetric
};
factory.ConfigureChannelFactory();
var channel = factory.CreateChannelWithIssuedToken(idpToken);
return channel.Issue(rst);
}
Creating a WCF call with the token
var ipdtoken = GetIdPToken();
var stsToken = GetRSTSToken(ipdtoken);
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.EstablishSecurityContext = false;
var factory = new ChannelFactory<IWebService>(binding, "https://services.dk/WebService.svc");
factory.ConfigureChannelFactory();
factory.Credentials.SupportInteractive = false;
var serviceChannel = factory.CreateChannelWithIssuedToken(stsToken);
var s = serviceChannel.GetUserInformation();
The audienceUri configuration on your IP looks fine. I think ADFS is the one that's throwing the ID3242 fault. Can you check to make sure your IP is correctly configured under Claim Provider Trusts on your ADFS server?
If you have your IP's federation metadata handy, you could also try recreating it in ADFS.

MessageSecurityException: The security header element 'Timestamp' with the '' id must be signed

I'm asking the same question here that I've already asked on msdn forums http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/70f40a4c-8399-4629-9bfc-146524334daf
I'm consuming a (most likely Java based) Web Service with I have absolutely no access to modify. It won't be modified even though I would ask them (it's a nation wide system).
I've written the client with WCF. Here's some code:
CustomBinding binding = new CustomBinding();
AsymmetricSecurityBindingElement element = SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
element.AllowSerializedSigningTokenOnReply = true;
element.SetKeyDerivation(false);
element.IncludeTimestamp = true;
element.KeyEntropyMode = SecurityKeyEntropyMode.ClientEntropy;
element.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
element.LocalClientSettings.IdentityVerifier = new CustomIdentityVerifier();
element.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
element.IncludeTimestamp = false;
binding.Elements.Add(element);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement());
EndpointAddress address = new EndpointAddress(new Uri("url"));
ChannelFactory<MyPortTypeChannel> factory = new ChannelFactory<MyPortTypeChannel>(binding, address);
ClientCredentials credentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
credentials.ClientCertificate.Certificate = myClientCert;
credentials.ServiceCertificate.DefaultCertificate = myServiceCert;
credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
service = factory.CreateChannel();
After this every request done to the service fails in client side (I can confirm my request is accepted by the service and a sane response is being returned)
I always get the following exception
MessageSecurityException: The security
header element 'Timestamp' with the ''
id must be signed.
By looking at trace I can see that in the response there really is a timestamp element, but in the security section there is only a signature for body.
Can I somehow make WCF to ingore the fact Timestamp isn't signed?
You could try using a WCF Message contract. When you have a Message contract you can specify that the items in the header should be signed:
[MessageContract]
public class CustomType
{
[MessageHeader(ProtectionLevel = ProtectionLevel.Sign)]
string name;
[MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
string secret;
I got this sorted out, the answer can be found in here:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/371184de-5c05-4c70-8899-13536d8f5d16
Main points: add a custom StrippingChannel to the custombinding to strip off the timestamp from the securityheader and configure WCF to not detect replies.

Categories