why no client certificate received in my IIS hosted web service? - web-services

I have an asmx web service, which I am trying to verify client certificate:
var cert = Context.Request.ClientCertificate.Certificate;
if (cert == null || cert.Length == 0)
{
throw new ArgumentException("certificate can not be found");
}
My testing code is like this in a console app:
ServiceReference1.PortfolioImportSoapClient import = new ServiceReference1.PortfolioImportSoapClient();
X509Certificate2 x509 = new X509Certificate2(#"D:\zurichpdf\publickey.cer");
import.ClientCredentials.ClientCertificate.Certificate = x509;
var model = new ServiceReference1.PortfolioMessageModel();
model.ClientID = 1;
import.ImportTransactionPortfolio(model);
What I can see is that from the console app, the client certificate is included. But at the server side, Context.Request.ClientCertificate.Certificate is empty.
I checked the config file, it has something like this:
<basicHttpBinding>
<binding name="PortfolioImportSoap" >
</binding>
</basicHttpBinding>
Can someone tell me why the x509 certificate is not passed to my asmx web service? Should I set some security mode here as well?
Many Thanks

Related

Azure WebJobs and Service Bus : 2 settings for the same thing?

When I add the following NuGet package to my WebJob: Microsoft.Azure.WebJobs.ServiceBus 2.0.0,
two new items are added to the app.config file.
It seems they are both used to define the Service Bus connection string.
Can I get rid of one of them?
<connectionStrings>
<add name="AzureWebJobsServiceBus" connectionString="..." />
</connectionStrings>
<appSettings>
<!-- Service Bus specific app setings for messaging connections -->
<add key="Microsoft.ServiceBus.ConnectionString" value="..." />
</appSettings>
Thanks for your help!
It seems they are both used to define the Service Bus connection string. Can I get rid of one of them?
According the source code of ServiceBusConfiguration, WebJob Service Bus SDK(ServiceBusTrigger) will use the connection string which stored under the connectionStrings section.
public string ConnectionString
{
get
{
if (!_connectionStringSet)
{
_connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.ServiceBus);
_connectionStringSet = true;
}
return _connectionString;
}
set
{
_connectionString = value;
_connectionStringSet = true;
}
}
You can also set the connection string at runtime.
JobHostConfiguration config = new JobHostConfiguration();
config.UseServiceBus(new ServiceBusConfiguration() { ConnectionString = "" });
JobHost host = new JobHost(config);
If you want to create a instance of Service Bus Client(for example QueueClient) to do some specific operations, you could use the Service Bus connection string configured in appSettings.
//Use CloudConfigurationManager to read the connection string stored in appSettings
string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
var client = QueueClient.CreateFromConnectionString(connectionString, "queueName");

Adding custom message header to a WCF service which is consumed from Windows Mobile 6 client

I have a WCF Service Application which server to different type of clients. While calling the service methods I want to send some spesific information within the service header.
While using a newer version of .NET Framework I can handle the situation using MessageHeader. Since the consumer can see the service as WCF Service there is no problem.
[DataContract]
public class AuthToken
{
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
}
Client-side:
AuthWCFSvc.Service1Client client = new AuthWCFSvc.Service1Client();
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
SvcAuthClient.AuthWCFSvc.AuthToken token = new AuthWCFSvc.AuthToken();
token.Username = "wcfuser";
token.Password = "wcfpass";
MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken> header = new MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken>(token);
var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
client.TestHeader();
}
Server-side :
MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
AuthToken token = headers.GetHeader<AuthToken>("Identity", "http://www.my-website.com");
But there are Windows Mobile 6 devices with .NET Framework 3.5 Compact Edition, using these services as well. Due to technological restrictions, they only can process the WCF services as Web Services.
If a client is consuming a WCF service as Web Service how can add spesific header information and resolve the header information at the service method?
As you know, on .NET CF 3.5 you can only use WCF as standard webservice on SOAP way. Therefore, you can't use any of WCF native security resources.
I figured out how to use Basic Http Authentication, configuring client and server sides and I can explain it as follows:
Client Side
On client side (on your device with .Net CF 3.5), its easy. Just inform your credentials configuring your clientServiceProxy by using:
var service = new YourServiceNamespace.YourService();
service.Credentials = new NetworkCredential("login", "12345");
service.PreAuthenticate = true;
This will make your client deal with the "WWW-Authenticate" header from the server response and pass your credentials automatically via the response header "Authorization: Basic".
Server Side
On the WCF configuration on your web.config, you should configure security just for Transport, and use HTTPS (this is enough to protect your message from sniffers).
<basicHttpBinding>
<binding>
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
Now, as WCF doesn't has native support for Basic Http Authentication, we have to use a custom HTTP module to deal with it.
public class BasicHttpAuthentication : IHttpModule
{
public delegate bool AuthenticateDelegate( string username, string password );
public static AuthenticateDelegate AuthenticateMethod;
public void Dispose() { }
public void Init( HttpApplication application )
{
application.AuthenticateRequest += this.OnAuthenticateRequest;
application.EndRequest += this.OnEndRequest;
}
private void DenyAccess( HttpApplication app )
{
app.Response.StatusCode = 401;
app.Response.StatusDescription = "Access Denied";
// Write to response stream as well, to give user visual
// indication of error during development
app.Response.Write( "401 Access Denied" );
app.CompleteRequest();
}
private void OnAuthenticateRequest( object source, EventArgs eventArgs )
{
if ( AuthenticateMethod == null )
return;
var app = ( HttpApplication )source;
//the Authorization header is checked if present
string authHeader = app.Request.Headers["Authorization"];
if ( !string.IsNullOrEmpty( authHeader ) )
{
string authStr = app.Request.Headers["Authorization"];
if ( string.IsNullOrEmpty( authStr ) )
return; // No credentials; anonymous request
authStr = authStr.Trim();
if ( authStr.IndexOf( "Basic", 0 ) != 0 )
// header is not correct...we'll pass it along and
// assume someone else will handle it
return;
authStr = authStr.Trim();
string encodedCredentials = authStr.Substring( 6 );
byte[] decodedBytes = Convert.FromBase64String( encodedCredentials );
string s = new ASCIIEncoding().GetString( decodedBytes );
string[] userPass = s.Split( new[] { ':' } );
string username = userPass[0];
string password = userPass[1];
if ( !AuthenticateMethod( username, password ) )
this.DenyAccess( app );
}
else
{
app.Response.StatusCode = 401;
app.Response.End();
}
}
private void OnEndRequest( object source, EventArgs eventArgs )
{
//the authorization header is not present
//the status of response is set to 401 and it ended
//the end request will check if it is 401 and add
//the authentication header so the client knows
//it needs to send credentials to authenticate
if ( HttpContext.Current.Response.StatusCode == 401 )
{
HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401;
context.Response.AddHeader( "WWW-Authenticate", "Basic Realm=\"Please inform your credentials\"" );
}
}
}
To enable the HTTP module, add the following to your web.config file in the system.webServer section:
<system.webServer>
<modules>
<add name="BasicHttpAuthentication"
type="BasicHttpAuthentication, YourAssemblyName"/>
</modules>
Now you have to inform to the module a Function to use for validating the credentials from the client. You can see that there's a static delegate inside the module called "AuthenticateMethod", so you can inform a function on your Application_Start of your global.asax:
BasicHttpAuthentication.AuthenticateMethod = ( username, password ) => username == "login" && password == "12345";

why NotFound error occur in REST services with windows Phone app?

i tried to connect REST web servie from windows phone 8 application.
it was working proberly for weeks but after no change in it I get this generic error :
System.Net.WebException: The remote server returned an error:
NotFound.
i tried to test it by online REST Clients and services works properly
i tried to handle Exception and parse it as webException by this code :
var we = ex.InnerException as WebException;
if (we != null)
{
var resp = we.Response as HttpWebResponse;
response.StatusCode = resp.StatusCode;
and i get no more information and final response code is : "NotFound"
any one have any idea about what may cause this error?
there is already a trusted Certificate implemented on the server . the one who has the server suggested to have a DNS entry for the server, this entry should be at the customer DNS or in the phone hosts file .that what i done and worked for awhile but now it doesn't work however i checked that there is no thing changed
this is sample for Get Request it works proberly on Windwos Store apps :
async Task<object> GetHttps(string uri, string parRequest, Type returnType, params string[] parameters)
{
try
{
string strRequest = ConstructRequest(parRequest, parameters);
string encodedRequest = HttpUtility.UrlEncode(strRequest);
string requestURL = BackEndURL + uri + encodedRequest;
HttpWebRequest request = HttpWebRequest.Create(new Uri(requestURL, UriKind.Absolute)) as HttpWebRequest;
request.Headers["applicationName"] = AppName;
request.Headers["applicationPassword"] = AppPassword;
if (AppVersion > 1)
request.Headers["applicationVersion"] = AppVersion.ToString();
request.Method = "GET";
request.CookieContainer = cookieContainer;
var factory = new TaskFactory();
var getResponseTask = factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
HttpWebResponse response = await getResponseTask as HttpWebResponse;
// string s = response.GetResponseStream().ToString();
if (response.StatusCode == HttpStatusCode.OK)
{
XmlSerializer serializer = new XmlSerializer(returnType);
object obj = serializer.Deserialize(response.GetResponseStream());
return obj;
}
else
{
var Instance = Activator.CreateInstance(returnType);
(Instance as ResponseBase).NetworkError = true;
(Instance as ResponseBase).StatusCode = response.StatusCode;
return Instance;
}
}
catch (Exception ex)
{
return HandleException(ex, returnType);
}
}
i tried to monitor connections from Emulator and i found this error in connection :
**
Authentication failed because the remote party has closed the
transport stream.
**
You saw the client implement a server side certificate in the service. Did you have that certificate installed on the phone? That can be the cause of the NotFound error. Please, can you try to navigate to the service in the phone or emulator internet explorer prior to testing the app? If you do that, you can see the service working in the emulator/phone internet explorer? Maybe at that point internet explorer ask you about installing the certificate and then you can open your app, and it works.
Also remember if you are testing this in the emulator, every time you close it, the state is lost so you need to repeat the operation of installing the certificate again.
Hope this helps.
If you plan to use SSL in production in general public application (not company-distribution app), you need to ensure your certificate has one of the following root authorities:
SSL root certificates for Windows Phone OS 7.1.
When we had same issue, we purchased SSL certificate from one of those providers and after installing it on server we were able to make HTTPS requests to our services with no problem.
If you have company-distribution app, you can use any certificate from company's Root CA.

Integrated Windows Security when calling an ASMX service

I have some issues with an ASMX web service running on Win XP with IIS 5.1. The IIS is set up with Integrated Windows Authentication and with anonymous access disabled. (When I have anonymous access enabled, everything works as a charm.)
However, when I turn of anonymous access and run with only Integrated Windows Authentication on my IIS, I get the following exception:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.
This exception is generated from my really simple console application that I'm using just to test the concept:
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; // .None;
binding.UseDefaultWebProxy = false;
EndpointIdentity spn = EndpointIdentity.CreateSpnIdentity("WORKGROUP/VirtualXP-91051");
Uri uri = new Uri("http://169.254.91.91/MyWebSite/MyService.asmx");
EndpointAddress address = new EndpointAddress(uri, spn);
MyServiceSoapClient client = new MyServiceSoapClient(binding, address);
client.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
String req = "( the request string is blanked out.. ;) )";
string resp = client.ReqAddressData(req);
Console.WriteLine(resp);
Console.ReadLine();
The interesting part of App.config:
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
This isn't rocket science, so I must be missing something obvious. I've done some searching here on SO, and eve though I can find many similar questions I've found nothing that solved my problem.

401 error if I connect from my ASP.NET app. to CrmDiscoveryService.asmx (CRM 2011)

I have to use CrmDiscoveryService.asmx for DYNAMICS CRM 2011 because of the compatability to CRM 4.0. I impemented IFD authentification algorithm and now it doesnt works:
I can connect me successfully to CrmDiscoveryService.asmx from Browser (anonymous connection enabled)
URL: https://internalcrm.organme>.com:444/mscrmservices/2007/spla/crmdiscoveryservice.asmx
But if I try to get connected with the same URL from ASP.NET - i receive 401 error.
Code:
RetrieveCrmTicketRequest crmTicketRequest = new RetrieveCrmTicketRequest();
crmTicketRequest.OrganizationName = OrgUniqueName;
crmTicketRequest.UserId = "bnet"+"\\"+"izz";
crmTicketRequest.Password = "pass";
CrmDiscoveryService discoveryService = serviceHandler.CreateCrmDiscoveryService(crmDiscoveryServiceUrl);
discoveryService.UseDefaultCredentials = true;
RetrieveCrmTicketResponse crmTicketResponse =
(RetrieveCrmTicketResponse)discoveryService.Execute(crmTicketRequest);
//Trust all certificates
System.Net.ServicePointManager.ServerCertificateValidationCallback =
((sender, certificate, chain, sslPolicyErrors) => true);
Might be it make sense to start from retrieving a list of available organizations, like in a sample Using the CrmDiscoveryService Web Service: IFD
// Configure an instance of the CrmDiscoveryService Web service proxy.
CrmDiscoveryService disco = new CrmDiscoveryService();
disco.Url = "https://localhost/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx";
//Retrieve a list of available organizations from the CrmDiscoveryService Web service.
RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
// Substitute an appropriate domain, username, and password here.
orgRequest.UserId = domain + "\\" + username;
orgRequest.Password = password;
RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest);
//Find the target organization.
OrganizationDetail orgInfo = null;
foreach (OrganizationDetail orgdetail in orgResponse.OrganizationDetails)
{
if (orgdetail.OrganizationName.Equals("AdventureWorksCycle"))
{
orgInfo = orgdetail;
break;
}
}
// Check whether a matching organization was not found.
if (orgInfo == null)
throw new Exception("The specified organization was not found.");
//Retrieve a CrmTicket from the CrmDiscoveryService Web service.
RetrieveCrmTicketRequest ticketRequest = new RetrieveCrmTicketRequest();
ticketRequest.OrganizationName = orgInfo.OrganizationName;
ticketRequest.UserId = domain + "\\" + username;
ticketRequest.Password = password;
RetrieveCrmTicketResponse ticketResponse =
(RetrieveCrmTicketResponse)disco.Execute(ticketRequest);
As paramosh was saying, try setting the credentials on the discovery service too:
discoveryService.Credentials = new System.Net.NetworkCredential("izz", "password", "bnet");