Generating Username Token For Soap Request - c#

I am trying to generate SOAP Username token using C# but without success
<wsse:UsernameToken wsu:Id='UsernameToken-1231231231123123'>
<wsse:Username>UserName</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>Password</wsse:Password>
</wsse:UsernameToken>
the above one is the correct format for our SOAP endpoint but when i am trying to generate token using UsernameToken from namespace
Microsoft.Web.Services2.Security.Tokens
UsernameToken t;
t = new UsernameToken("UserName", "Password");
string usernameTokenSection1 = t.GetXml(new XmlDocument()).OuterXml.ToString();
I got this result which
is not working
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-cf96131b-1528-46a1-8f00-f61af616db91" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username>Username</wsse:Username>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">+S3AH9NHRtOpgdxEwqIVIg==</wsse:Nonce><wsu:Created>2020-04-04T06:46:53Z</wsu:Created></wsse:UsernameToken>

the wsu:Id='UsernameToken-1231231231123123' attribute is Id property of UsernameToken
So, you do this :
UsernameToken t = new UsernameToken("UserName", "Password", PasswordOption.SendPlainText)
{
Id = "UsernameToken-1231231231123123"
};
Then you can parse it in XmlDocument or XDocument which would give you the ability to adjust the elements to fit your requirements.
you can parse it like this var doc = XDocument.Parse(usernameTokenSection1);
Now, using the parsed XML, you can adjust it to your requirements. For instance you can remove Nonce and Created elements like this :
var doc = XDocument.Parse(usernameTokenSection1);
XNamespace wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
XNamespace wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
doc.Root.Descendants(wsse + "Nonce").Remove();
doc.Root.Descendants(wsu + "Created").Remove();

Related

The top XML element 'Data' from namespace '' references distinct types

I have to send Active Directory data to a third-party application. This third-party application provides a .WSDL URL which I have added as a Service Reference in my .NET application. After this I have to call a function "Import" which looks something like this:
Import(Security security, string Data, int FolderID)
Here 'Data' is the XML data that needs to be transferred. I have an XML something like this:
var xEle = new XElement("Document",
from emp in lstADUsers
select new XElement("Record",
new XElement("UserName", emp.UserName),
new XAttribute("FirstName", "TEST_FNAME"),
new XAttribute("LastName", "Test_LNAME"),
new XAttribute("Email", "test#test.com")
));
I call the Import method as:
Import(token, xEle, 1)
When this method is hit, I am getting below error:
The top XML element 'Data' from namespace '' references distinct types
System.String and System.Byte[]. Use XML attributes to specify another
XML name or namespace for the element or types.
The third-party application expects SOAP data.
Extra details
SOAP envelope looks like this:
<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:sec='http://schemas.xmlsoap.org/ws/2003/06/secext' xmlns:wsu='http://schemas.xmlsoap.org/ws/2003/06/utility'
xmlns:urn='urn:TestApp'>
<soapenv:Header>
<sec:Security>
<sec:UsernameToken wsu:Id='TestApp'>
<sec:Username>TestUser</sec:Username>
<!--Optional:-->
<sec:Password>TestPassword</sec:Password>
</sec:UsernameToken>
</sec:Security>
</soapenv:Header>
<soapenv:Body>
<urn:ImportData>
<Table></Table>
<FolderId>0</FolderId>
<Data><![CDATA[<Document>
<Record></Record>
</Document>
</Data>
Code Changes Below
/// <summary>
/// Summary description for ThirdPArtyApp
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class ThirdParty : System.Web.Services.WebService
{
public Header header;
[WebMethod]
[SoapHeader("header", Direction = SoapHeaderDirection.InOut)]
public void CreateXmlForImport()
{
ThirdParty_Serv thirdParty = new ThirdParty_Serv();
header.Username = "test123";
header.Password = "XYZ123";
const string FILENAME = #"D:\Users\Documents\test.xml";
XDocument doc = XDocument.Load(FILENAME);
XNamespace nsUrn = doc.Root.GetNamespaceOfPrefix("urn");
XElement importData = doc.Descendants(nsUrn + "ImportData").FirstOrDefault();
XElement xCdata = new XElement("Document",
new XElement("Record",
new XAttribute("UserName", "T12345"),
new XAttribute("FirstName", "TEST_RP"),
new XAttribute("LastName", "Test_LNAME"),
new XAttribute("Email", "test#test.com")
));
string cDataStr = xCdata.ToString();
XCData cdata = new XCData(cDataStr);
thirdParty.ImportData("/Do/Persons", 0, cDataStr, 3);
}
}
I used the schema to get correct structure. From VS you can test the syntax by going to menu Project:Add New Item : Xml File. Than past the xml into view. Errors will show like compiler errors in the Error List. Also if you type an opening angle bracket is will show the elements that can be added in any section.
I used following xml
<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:sec='http://schemas.xmlsoap.org/ws/2003/06/secext' xmlns:wsu='http://schemas.xmlsoap.org/ws/2003/06/utility'
xmlns:urn='urn:TestApp'>
<soapenv:Header>
<sec:Security>
<sec:UsernameToken wsu:Id='TestApp'>
<sec:Username>TestUser</sec:Username>
<!--Optional:-->
<sec:Password>TestPassword</sec:Password>
</sec:UsernameToken>
</sec:Security>
</soapenv:Header>
<soapenv:Body>
<urn:ImportData>
<Table></Table>
<FolderId>0</FolderId>
<Record></Record>
</urn:ImportData>
</soapenv:Body>
</soapenv:Envelope>
Here is my code
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace nsUrn = doc.Root.GetNamespaceOfPrefix("urn");
XElement importData = doc.Descendants(nsUrn + "ImportData").FirstOrDefault();
XElement xCdata = new XElement("Record",
new XElement("UserName", "username"),
new XAttribute("FirstName", "TEST_FNAME"),
new XAttribute("LastName", "Test_LNAME"),
new XAttribute("Email", "test#test.com")
);
string cDataStr = xCdata.ToString();
XCData cdata = new XCData(cDataStr);
importData.Add(cdata);
}
Here is final Xml
<?xml version="1.0"?>
<soapenv:Envelope xmlns:urn="urn:TestApp" xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility" xmlns:sec="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<sec:Security>
<sec:UsernameToken wsu:Id="TestApp">
<sec:Username>TestUser</sec:Username>
<!--Optional:-->
<sec:Password>TestPassword</sec:Password>
</sec:UsernameToken>
</sec:Security>
</soapenv:Header>
<soapenv:Body>
<urn:ImportData>
<Table/>
<FolderId>0</FolderId>
<Record/>
<![CDATA[<Record FirstName="TEST_FNAME" LastName="Test_LNAME" Email="test#test.com"><UserName>username</UserName></Record>]]>
</urn:ImportData>
</soapenv:Body>
</soapenv:Envelope>

How to Add OrganizationToken to WCF Endpoint in code

I am trying to connect to a SOAP API of a vendor and I have a sample project they provided which works. I am trying to figure out how to replace their endpoint which is in the app.config with one created in code so I can load endpoint URL, username, and password using DI.
Here is the relevant XML from the app.config:
<client>
<endpoint address="https://strongmail.com/sm/services/mailing/v2" binding="basicHttpBinding" bindingConfiguration="MailingServiceServiceSoapBinding" contract="MailingService" name="MailingServicePort">
<headers>
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken">
<wsse:Username>Username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Password</wsse:Password>
</wsse:UsernameToken>
<OrganizationToken xmlns="http://www.strongmail.com/services/v2/schema">
<organizationName>admin</organizationName>
<subOrganizationId>
<id>1</id>
</subOrganizationId>
</OrganizationToken>
</wsse:Security>
</SOAP-ENV:Header>
</headers>
</endpoint>
</client>
This is the code I am trying to use to instantiate this.
public class SelligentOrganizationToken
{
public string organizationName { get; set; }
public SelligentOrganization subOrganizationId { get; set; }
}
public class SelligentOrganization
{
public string id { get; set; }
}
private MailingService CreateMailingService(string mailingServiceUrl, string userName, string password)
{
var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.IncludeTimestamp = false;
var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpsTransportBindingElement();
var customBinding = new CustomBinding(securityElement, encodingElement, transportElement);
var remoteAddress = new EndpointAddress(mailingServiceUrl);
var mailingService = new MailingServiceClient(customBinding, remoteAddress);
mailingService.ClientCredentials.UserName.UserName = userName;
mailingService.ClientCredentials.UserName.Password = password;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
var selligentOrganizationToken = new SelligentOrganizationToken()
{
organizationName = "admin",
subOrganizationId = new SelligentOrganization()
{
id = "1"
}
};
//Serialize object to xml
XmlObjectSerializer xmlObjectSerializer = new DataContractSerializer(typeof(SelligentOrganizationToken), "OrganizationToken", "http://www.strongmail.com/services/v2/schema");
var eab = new EndpointAddressBuilder(mailingService.Endpoint.Address);
eab.Headers.Add(AddressHeader.CreateAddressHeader("OrganizationToken", "http://www.strongmail.com/services/v2/schema", selligentOrganizationToken, xmlObjectSerializer));
mailingService.Endpoint.Address = eab.ToEndpointAddress();
return mailingService;
}
When I try to run this I am getting:
System.ServiceModel.FaultException: 'A security error was encountered when verifying the message'
Which, incidentally, is what I get when running the example code if I remove the OrganizationToken node.
Any help on this will be greatly appreciated, thank you in advance!
I see you app.config has configured headers node and it seems you are not using the endpointconfig. Instead, you add headers in your c# code.
How about directly use the endpoint configuration in your app.config?
var mailingService = new MailingServiceClient("MailingServicePort")
If you want to add addressHeader, you could also try to use OutgoingMessageHeaders or HttpRequestMessageProperty.
https://code.msdn.microsoft.com/windowsapps/How-to-add-custom-5cbbf066
Please ensure where you want to add the header, OutgoingMessageHeaders is used to add a soap header and HttpRequestMessageProperty is used to add a header in request header.

Adding SOAP Header to request

I have been trying to add a header to SOAP request as follows
<soapenv:Header>
<UsernameToken xmlns="http://test.com/webservices">username</UsernameToken>
<PasswordText xmlns="http://test.com/webservices">password</PasswordText>
<SessionType xmlns="http://test.com/webservices">None</SessionType>
</soapenv:Header>
I have found suggestions to use SoapHeader to include header values, but introduces another level such as
<soapenv:Header>
<CustomHeader>
<UsernameToken xmlns="http://test.com/webservices">username</UsernameToken>
<PasswordText xmlns="http://test.com/webservices">password</PasswordText>
<SessionType xmlns="http://test.com/webservices">None</SessionType>
</CustomHeader>
</soapenv:Header>
Can anyone suggest how I can form a request without CustomHeader.
Try to use this one
private static void Main()
{
using (var client = new ServiceClient())
using (var scope = new OperationContextScope(client.InnerChannel))
{
MessageHeader usernameTokenHeader = MessageHeader.CreateHeader("UsernameToken",
"http://test.com/webservices", "username");
OperationContext.Current.OutgoingMessageHeaders.Add(usernameTokenHeader);
MessageHeader passwordTextHeader = MessageHeader.CreateHeader("PasswordText",
"http://test.com/webservices", "password");
OperationContext.Current.OutgoingMessageHeaders.Add(passwordTextHeader);
MessageHeader sessionTypeHeader = MessageHeader.CreateHeader("SessionType",
"http://test.com/webservices", "None");
OperationContext.Current.OutgoingMessageHeaders.Add(sessionTypeHeader);
string result = client.GetData(1);
Console.WriteLine(result);
}
Console.ReadKey();
}
The Service Trace viewer shows following
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<UsernameToken xmlns="http://test.com/webservices">username</UsernameToken>
<PasswordText xmlns="http://test.com/webservices">password</PasswordText>
<SessionType xmlns="http://test.com/webservices">None</SessionType>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:13332/Service1.svc</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IService/GetData</Action>
</s:Header>
</s:Envelope>
Take a look OperationContextScope for more info

How to make WCF Client conform to specific WS-Security - sign UsernameToken and SecurityTokenReference

I need to create a wcf client to call a service that I have no control over.
I have been given a wsdl and a working soapui project.
The service uses both a username/password and a x509 certificate.
UPDATE
I now understand what the problem is, but am still unsure what steps I need to take to be able to create the required message, so any help would be much appreciated.
I need to sign both the UsernameToken and the SecurityTokenReference.
The code I had to create the custom binding has been removed from this post as its no longer used. I no longer add a SecurityBindingElement to the binding, instead I add a new behaviour that writes the security element into the header.
So the security node is created from scratch by subclassing the SignedXml class, adding signing references and then calling ComputeSignature to create the Signature node within the Security header.
You need to pass the xml to sign into the SignedXml constructor for this to work. It is no problem passing in the UsernameToken and this appears to be signed correctly.
The problem is that the SecurityTokenReference is only created when ComputeSignature() is called, so I'm not able to add a signing Reference to this element, as it does not exist at the time it is required (within the overridden GetIdElement method of SignedXml which is called prior to ComputeSignature())
The code I'm using to create the signature block to insert into the Security header is as follows
string certificatePath = System.Windows.Forms.Application.StartupPath + "\\" + "Certs\\sign-and- enc.p12";
XmlDocument xd = new XmlDocument();
xd.LoadXml(xml);
// Set Certificate
System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificatePath, "password");
MySignedXml signedXml = new MySignedXml(xd);
signedXml.SigningKey = cert.PrivateKey;
// Create a new KeyInfo object.
KeyInfo keyInfo = new KeyInfo();
keyInfo.Id = "";
MemoryStream keyInfoStream = new MemoryStream();
XmlWriter keyInfoWriter = XmlWriter.Create(keyInfoStream);
WSSecurityTokenSerializer.DefaultInstance.WriteKeyIdentifierClause(keyInfoWriter, new LocalIdKeyIdentifierClause("token_reference", typeof(X509SecurityToken)));
keyInfoWriter.Flush();
keyInfoStream.Position = 0;
XmlDocument keyInfoDocument = new XmlDocument();
keyInfoDocument.Load(keyInfoStream);
XmlAttribute attrib = keyInfoDocument.CreateAttribute("ValueType");
attrib.InnerText = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
keyInfoDocument.ChildNodes[1].ChildNodes[0].Attributes.Append(attrib);
KeyInfoNode keyInfoNode = new KeyInfoNode();
keyInfoNode.LoadXml(keyInfoDocument.DocumentElement);
keyInfo.AddClause(keyInfoNode);
// Add the KeyInfo object to the SignedXml object.
signedXml.KeyInfo = keyInfo;
// Need to use External Canonicalization method.
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "#UsernameToken-1";
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
env.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
reference.AddTransform(env);
reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
signedXml.AddReference(reference);
Reference reference2 = new Reference();
reference2.Uri = "#token_reference";
XmlDsigEnvelopedSignatureTransform env2 = new XmlDsigEnvelopedSignatureTransform();
env2.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
reference2.AddTransform(env2);
reference2.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
signedXml.AddReference(reference2);
// Add the Signature Id
signedXml.Signature.Id = "MYSIG_ID";
// Compute the signature.
signedXml.ComputeSignature();
XmlElement xmlDigitalSignature = signedXml.GetXml();
where the xml variable is the the UsernameToken xml string, and the MySignedXml class is a subclassed SignedXml with the GetIdElement method overridden (to try to find and correctly refreence the non-existant SecurityTokenReference)
I've spend days researching and testing this now, and unfortunately the company supplying the service isn't any help - but I need to use their service.
Full working soap message (soapUI project)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:XXXXX">
<soapenv:Header xmlns:ebxml="http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/">
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="CertId-D05E596B5ABC341FEB13505090224061" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">MIIEnDCCBAWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnowHhcNMTEwOTE1MDIwNjIwWhcNMjEwOTEyMDIwNjIwWjCByTELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxJTAjBgNVBAMTHHNpZ24tYW5kLWVuYy5kZXYuaXJkLmdvdnQubnoxFTATBgNVBCkTDHNpZ24tYW5kLWVuYzEoMCYGCSqGSIb3DQEJARYZY2hyaXMuc2NodWx0ekBpcmQuZ292dC5uejCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAykyZHVnXjsG220zB3kNOsGBeGP2rdNbLlIqW1D8yZO1fcj3/RhRiqsopbUrb8AU1ClpfhbH2K68kg7V91VAY0qrwNxP+pPPo1vYKMU6pT38qJGQzffr+iV2BCJshZvSk9E7QSWO5mFNstdg19xc+5ST1Lgb3fefuRG2KZVxPx0sCAwEAAaOCAZUwggGRMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDQGCWCGSAGG+EIBDQQnFiVFYXN5LVJTQSBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBSczRKXKPe3Sin7eFrVXfI7MXckzzCB+QYDVR0jBIHxMIHugBSLWxPSZd9otEj16vhLyovMCI9OMaGByqSBxzCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnqCCQDL/qDdlx2j6DATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAS4ZPIVVpgTOGN4XcIC3SiYlxF8wYg7qnUhH5wJsAD3VCKfj68j9FSJtdBWLlWvvRxEoDP2lZ0IbFl6Rjnl+2ibzFnyac2G1AVm5mwPrNKHBQJ9J5eDJi0iUVY7Wphz86tKnqj34GvlHPNXmrF7oGEcDhPwK0T8zRdE/pvKIUiJc=</wsse:BinarySecurityToken>
<ds:Signature Id="Signature-2" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>3iVAUEAt8vAb7Ku+jf2gwSkSm0Q=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#UsernameToken-1">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>r4HLEAWJldJwmEmcAqV6Y8rnTPE=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
YGh2I3VcukqjT0O7hKItiykWN5tlID18ZXRCwQjXriHmnVsO4wGcHjWfmhuNDecq+xRN+SjG8E7M
2Rx/5/BbFKbVlNOkQOSbSxIs1YT9GaThK16pMrX5KRkkJme1W3R0pGIIQh6gGRSUf79RZUIYxyVl
LqdIe561TXXDdtbt/6Q=
</ds:SignatureValue>
<ds:KeyInfo Id="KeyId-D05E596B5ABC341FEB13505090224372">
<wsse:SecurityTokenReference wsu:Id="STRId-D05E596B5ABC341FEB13505090224373" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/></wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsse:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>XXXXXX</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXX</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<ebxml:Messaging xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ebxml:UserMessage>
<ebxml:MessageInfo>
<ebxml:Timestamp>2002-02-02T14:18:02.0Z</ebxml:Timestamp>
<ebxml:MessageId>bf9433d9-c6e9-4c12-9c98-724008a09c21</ebxml:MessageId>
</ebxml:MessageInfo>
<ebxml:PartyInfo>
<ebxml:From>
<ebxml:PartyId type="identifier">Trading Partner X</ebxml:PartyId>
<ebxml:Role>Provider</ebxml:Role>
</ebxml:From>
<ebxml:To>
<ebxml:PartyId type="identifier">XXXXXXX</ebxml:PartyId>
<ebxml:Role>Requestor</ebxml:Role>
</ebxml:To>
</ebxml:PartyInfo>
<ebxml:CollaborationInfo>
<ebxml:AgreementRef>urn:XXXXXXXXX</ebxml:AgreementRef>
<ebxml:Service type="Web Service">urn:XXXXXXXX</ebxml:Service>
<ebxml:Action>customerInformation</ebxml:Action>
<ebxml:ConversationId>e302426a-b2d9-4ff1-a14b-fbbc2f40a017</ebxml:ConversationId>
</ebxml:CollaborationInfo>
</ebxml:UserMessage>
</ebxml:Messaging>
</soapenv:Header>
<soapenv:Body>
<urn:ConnectionTest>
<Message>Bonjour</Message>
</urn:ConnectionTest>
</soapenv:Body>
</soapenv:Envelope>
We were supplied with some documentation. The security section is copied below
The following security must be applied to each request in the following order:
A wsse:UsernameToken must be included and contain:
The Agent‟s Portal Username for the value of the wsse:Username element
The Agent‟s Portal Password for the value of the wsse:PasswordText (clear text) in the Password element
A Signature block:
Digital Signatures are created using x509 certificates. Each software provider is required to hold and protect a valid certificate and private key issued by [To Be Determined]. With each service request the software must:
Include the Certificate that matches the private key used for signing as a wsse:BinarySecurityToken and use it as the wsse:SecurityTokenReference for the Signature
Sign the wsse:BinarySecurityToken
Sign the wsse:UsernameToken
Use Canonicalization Method Algorithm: http://www.w3.org/2001/10/xml-exc-c14n#
Use Signature Method Algorithm: http://www.w3.org/2000/09/xmldsig#rsa-sha1
Use Digest Method Algorithm: http://www.w3.org/2000/09/xmldsig#sha1
Update
Image of the SoapUI configuration I was initially given
Finally sorted the problem today. In terms of terminology, it is not the SecurityTokenReference that I need to sign, but the Binary Security Token.
In order to do this I needed to hide the certificates for Initiator and Recipient and add a signed supporting token.
I went back to using configuration to create and sign the message, rather than trying to add the signature manually.
Other problem that would have stopped this from working is that I had an incorrect namespace on my custom 'Messaging' header, so be mindful of namespaces, I didn't think they would be as important as what they are.
The code to create the binding follows
private System.ServiceModel.Channels.Binding GetCustomBinding()
{
System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement();
asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
asbe.EnableUnsecuredResponse = true;
asbe.IncludeTimestamp = false;
asbe.SetKeyDerivation(false);
asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;
asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
CustomBinding myBinding = new CustomBinding();
myBinding.Elements.Add(asbe);
myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.RequireClientCertificate = true;
myBinding.Elements.Add(httpsBindingElement);
return myBinding;
}
When using the binding, I set the ClientCredentials UserName, ServiceCertificate and ClientCertificate, and all works as expected.
Using the code is as follows
using (CredentialingService.SOAPPortTypeClient client = GetCredentialingClient())
{
client.Open();
etc....
}
private static CredentialingService.SOAPPortTypeClient GetCredentialingClient()
{
CredentialingService.SOAPPortTypeClient client = new CredentialingService.SOAPPortTypeClient(GetCustomBinding(), new EndpointAddress(new Uri(Settings.AppSettings.B2BUrl), new DnsEndpointIdentity(Settings.AppSettings.B2BDNSEndpoint), new AddressHeaderCollection()));
client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
SetClientCredentialsSecurity(client.ClientCredentials);
return client;
}
where GetCustomBinding is specified in my post
SetClientCredentialsSecurity is where the certificate is set, and is as follows
private static void SetClientCredentialsSecurity(ClientCredentials clientCredentials)
{
clientCredentials.UserName.UserName = Settings.AppSettings.B2BUserName;
clientCredentials.UserName.Password = Settings.AppSettings.B2BPassword;
string directoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
clientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BServerCertificateName));
clientCredentials.ClientCertificate.Certificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BClientCertificateName), Settings.AppSettings.B2BClientCertificatePassword);
}
Hopefully that makes it a bit clearer
Maybe it's because your certificate is not publicly valid. Try this:
System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; });
//...
using (var client = new SrvClient())
{
client.ClientCredentials.UserName.UserName = "usr";
client.ClientCredentials.UserName.Password = "psw";
//...
}

Can I remove the default namespace when creating raw Soap XML?

I'm trying to send a soap request to a WCF service. I am building the soap request using the System.ServiceModel.Channels.Message.CreateMessage() method.
I haven't gotten super deep into building the body, but here is what I have...
Message msg = Message.CreateMessage( MessageVersion.Soap11WSAddressing10, "MethodName" );
msg.Headers.MessageId = new UniqueId( Guid.NewGuid().ToString() );
msg.Headers.Add( Message.CreateHeader( "Security", "",
new Security()
{
TimeStamp = new TimeStampType() {
Created = DateTime.Now,
Expires = Created.AddDays( 1 )
},
UsernameToken = new UsernameToken() {
Username = "stackoverflow",
Password = new Password() {
Type = "hashed",
Value = "Password"
}
}
} ) ) );
string s = msg.ToString();
When I run this, I get the following output. I'm using the Visual Studio XML Visualizer btw.
<s:Envelope>
<s:Header>
<Action>MethodName</Action>
<MessageID>GUIDVALUE</MessageID>
<Security>
<Timestamp xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
.....
</s:Header>
<s:Body />
</s:Envelope>
My question is, can I remove xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" from the xml? It shows up in Timestamp and in UsernameToken.
Thanks
set Namespace to empty in datacontract
in a class that wrap your security and timestamp
[DataContract(Namespace = "")]

Categories

Resources