I am migrating a .NetFramework application that access a DMS system over SOAP
The working implementation involves some generated code where it was necessary to change the base class to Microsoft.Web.Services2.WebServiceClientProtocol in order for the security headers to be correctly built.
public partial class MyService: Microsoft.Web.Services2.WebServicesClientProtocol
The following code successfully calls the WsSearchDmsDocument
var token = new UsernameToken(DmsUsername, DmsPassword, PasswordOption.SendHashed);
var client = new MyService() {Url = ReinsUrl};
SoapContext requestContext = client.RequestSoapContext;
requestContext.Security.Timestamp.TtlInSeconds = 60;
requestContext.Security.Tokens.Add(token);
var myRequest = new Request();
var response = client.WsSearchDmsDocument(request);
Which sends the username/password security header looking like and returns the expected response
<wsse:UsernameToken wsu:Id='UsernameToken-238be95be3bf445fb8534666a7a8693c'>
<wsse:Username>***login***</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest'>***Base64 (SHA-1 (nonce + created + password) )***</wsse:Password>
<wsse:Nonce EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary'>***Base64 nonce***</wsse:Nonce>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
</wsse:UsernameToken>
In .Net5
I modified the MyService class in the following way
internal partial class MyService: System.ServiceModel.ClientBase<MyReinsServices>, MyReinsServices
Then I try to call the service
var token = new UsernameToken(DmsUsername, DmsPassword, PasswordOption.SendHashed);
MyService client = new MyService(ReinsServicesClient.EndpointConfiguration.ReinsServicesSoap11, ReinsUrl);
UserNamePasswordClientCredential credential = client.ClientCredentials.UserName;
credential.UserName = DmsUsername;
credential.Password = DmsPassword;
var myRequest = new Request();
var response = client.WsSearchDmsDocument(request);
But this fails with
com.sun.xml.wss.XWSSecurityException:
Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]:
No Security Header found;
nested exception is com.sun.xml.wss.XWSSecurityException:
com.sun.xml.wss.XWSSecurityException:
Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]:
No Security Header found
This is a similar error to what I was getting in the .Net Framework version before I used Microsoft.Web.Services2.WebServicesClientProtocol
I think I am very close to a solution but no matter what client.ClientCredentials I take it does not satisfy the security header needed by this particular SOAP service
EDIT
I am able to use SoapUI to call this service. I have to set in WS-Security setting a username and password with PasswordDigest and adding this as a Basic Auth to the outgoing WSS. I then copy the resulting SOAP Envelope into Soap.txt and try to send this via .NET5 in the following code
string soap = File.ReadAllText("soup.txt");
XmlDocument document = new XmlDocument();
document.LoadXml(soap); //loading soap message as string
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace("reins", "http://scor.com/dms-reins-webservices/schemas/2.0/reins");
manager.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
manager.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
manager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
//Build the token
RNGCryptoServiceProvider Generator = new RNGCryptoServiceProvider();
var _nonce = new byte[16];
Generator.GetBytes(_nonce);
string nonce = Convert.ToBase64String(_nonce);
var created = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string payLoad = nonce + created + DmsPassword;
byte[] payLoadBytes = System.Text.Encoding.UTF8.GetBytes(payLoad);
SHA1 sha = new SHA1CryptoServiceProvider();
var tokenBytes = sha.ComputeHash(payLoadBytes);
string token = Convert.ToBase64String(tokenBytes);
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsu:Created", manager).InnerText = created;
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsse:Password", manager).InnerText = token;
document.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security/wsse:UsernameToken/wsse:Nonce", manager).InnerText = nonce;
var httpClient = new HttpClient();
httpClient.DefaultRequestVersion = HttpVersion.Version11;
HttpResponseMessage response;
var soapMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(s, Encoding.UTF8, MediaTypeNames.Text.Xml),
};
soapMessage.Headers.Add("Accept", "text/xml");
soapMessage.Headers.Add("Accept-Encoding", "gzip,deflate");
soapMessage.Headers.Add("SOAPAction", "");
soapMessage.Headers.Add("Connection", "Keep-Alive");
soapMessage.Headers.Add("User-Agent", "Apache-HttpClient/4.5.5 (Java/12.0.1)");
response = httpClient.Send(soapMessage);
When I make the request via SoapUi I get a StatusCode 200 and the data I want. When I use the above code that should perform the exact same operation I get an StatusCode 500 Internal Server error.
Any suggestions how to solve either the first part of this or to get this hack to work using .NET5 or .NET6 would be welcome