I have added a Connected Reference in Visual Studio 2019. It consumed a https endpoint, and created all binding information needed into a reference.cs file.
It didn't generate any App.config file, so I suspected what I needed was bundled into the reference.cs file. Indeed, looking into it, it mostly was.
So I tried creating a client, specify client credentials in two ways, as you can see, but still, doesn't matter how I specify it, I get an exception when calling this code below.
public async Task SendFile(Stream fileStream, string fileName, Guid machineKey)
{
_logger.LogInformation("Starting file sending to Manager 1.");
_logger.LogInformation($"Sending file {fileName} from Machine {machineKey}");
try
{
var client = new FileTransferClient(FileTransferClient.EndpointConfiguration.BasicHttpBinding_IFileTransfer, _options.FileTransferEndPoint)
{
ClientCredentials =
{
UserName =
{
UserName = _options.FileTransferUsername,
Password = _options.FileTransferPassword
}
}
};
client.ClientCredentials.UserName.UserName = _options.FileTransferUsername;
client.ClientCredentials.UserName.Password = _options.FileTransferPassword;
using (new OperationContextScope(client.InnerChannel))
{
}
await client.UploadAsync(new FileUploadMessage
{
// Assume that this is enough. Can't really supply file length...
FileInfo = new FileTransferInfo
{
TransferId = new Guid(),
MachineUUID = machineKey.ToString(),
Name = fileName
},
TransferStream = fileStream
});
}
catch (Exception e)
{
_logger.LogError("An unexpected exception occurred while sending file to Manager 1G.", e);
}
_logger.LogInformation("File sending finished.");
}
The exception is "The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic Realm'."
I have compared to similar APIs that use the beforementioned App.config, and have edited the reference.cs to match the security I think it should have.
Specifically, I've added the security related lines here:
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_IFileTransfer))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
result.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
return result;
}
if ((endpointConfiguration == EndpointConfiguration.MetadataExchangeHttpsBinding_IFileTransfer))
{
System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
result.Elements.Add(textBindingElement);
System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement();
httpsBindingElement.AllowCookies = true;
httpsBindingElement.MaxBufferSize = int.MaxValue;
httpsBindingElement.MaxReceivedMessageSize = int.MaxValue;
result.Elements.Add(httpsBindingElement);
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
What I found dumbfounding, was that with embedding in the constructor calling setting the ClientCredentials, they were not in any way populated when I inspected the client with a debug session attached. Hence I tried to set it afterwards specifically.
But either way, the end result is the same, get the same error.
How can I resolve that error in Code?
I can in theory try to add an App.config and do it there, but I don't know the Contract. And I am not sure what to look for in the generated reference.cs to identify it. So I'd prefer to learn to do this by Code, as the Contract is already in place there, and I can supply the endpoint via the _options, so it should be able to configure for different environments by that.
Turned out I had indeed password and username exchanged, so fixing that helped me get past of this issue.
Related
I'm actually trying to expose some methods of an ASP.NET MVC specific controller, in order to secure sensitive calls.
The entire website doesn't have to be protected by a specific SSL certificate, but some requests are.
Here is my code (as simple as it is) to get "Data", as you can see, I first check the SSL certificate, then the process continues if the SSL Certificate is correct :
public string GetData()
{
try
{
var certificate = Request.ClientCertificate;
if (certificate == null || string.IsNullOrEmpty(certificate.Subject))
{
// certificate may not be here
throw new Exception("ERR_00_NO_SSL_CERTIFICATE");
}
if (!certificate.IsValid || !IsMyCertificateOK(certificate))
{
// certificate is not valid
throw new Exception("ERR_01_WRONG_SSL_CERTIFICATE");
}
// Actions here ...
}
catch (Exception)
{
Response.StatusCode = 400;
Response.StatusDescription = "Bad Request";
}
}
Here is my IIS configuration :
SSL Certificate is set to "Accept", thus, I hope I could get the client certificate in the Request.ClientCertificate property, but it's never the case, I never get the certificate set in my client.
Here is my client code (copied from generated Postman C# code) :
string PFX_PATH = #"C:\Test\test.pfx"; // set here path of the PFX file
string PFX_PASSWORD = "password"; // set here password of the PFX file
var client = new RestClient("https://mywebsite.com/GetData?input=test");
client.Timeout = -1;
client.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection()
{
new System.Security.Cryptography.X509Certificates.X509Certificate(PFX_PATH,
PFX_PASSWORD,
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable)
};
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
The PFX file has a private key, and is accessible from client side.
Am I missing something regarding the IIS configuration, or should I update my web.config somehow ?
The following code is used by software on a server outside AWS to obtain some information from a file within an S3 bucket in Amazon. This data is then broken up and used for other purposes.
List<Document> documentList = new List<Document>();
try
{
AmazonS3Config amazonS3Config = new AmazonS3Config();
amazonS3Config.RegionEndpoint = Amazon.RegionEndpoint.GetBySystemName(Settings.AWSRegion);
if (Settings.Proxy == true)
{
if (Settings.IsMasterService == true)
{
amazonS3Config.ProxyHost = Settings.ProxyHost;
amazonS3Config.ProxyPort = Settings.ProxyPort;
amazonS3Config.ProxyCredentials = System.Net.CredentialCache.DefaultCredentials;
}
else
{
if (Settings.IsCompanyStore == true)
{
amazonS3Config.ProxyHost = Settings.ProxyHostCompanyStore;
amazonS3Config.ProxyPort = Settings.ProxyPortCompanyStore;
NetworkCredential corpProxyCreds = new NetworkCredential(Settings.ProxyUserNameCompanyStore, Settings.ProxyPasswordCompanyStore);
amazonS3Config.ProxyCredentials = corpProxyCreds;
}
}
}
AmazonS3Client s3 = new AmazonS3Client(amazonCreds, amazonS3Config);
GetObjectRequest req = new GetObjectRequest();
req.BucketName = Settings.S3BucketName;
req.Key = Settings.S3ObjectName;
using (GetObjectResponse response = s3.GetObject(req))
if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
using (Stream amazonStream = response.ResponseStream)
{
StreamReader amazonStreamReader = new StreamReader(amazonStream);
string _lne = string.Empty;
while ((_lne = amazonStreamReader.ReadLine()) != null)
{
string[] _cfglines = _lne.Split('&');
foreach (string c in _cfglines)
{
string[] _fle = c.Split('|');
Document d = new Document();
d.Name = _fle[1];
d.FolderPath = _fle[0];
documentList.Add(d);
}
}
}
}
else
{
EventHandling.RaiseDebugEvent("response.HttpStatusCode.ToString() = " + response.HttpStatusCode.ToString());
throw new Exception("Could not obtain master configuration file. Status: " + response.HttpStatusCode.ToString());
}
}
catch (Exception ex)
{
EventHandling.RaiseDebugEvent(" ReturnCloudCaptureDocumentList ex.tostring = " + ex.ToString());
EventHandling.RaiseEvent(ex.Message, System.Diagnostics.EventLogEntryType.Error);
}
return documentList;
We have two different types of servers outside AWS. One behind a proxy, one not behind a proxy.
On the server not behind a proxy, this code works fine.
On the server behind a web proxy, this code fails every time with the following error:
'Error making request with Error Code ServiceUnavailable and Http
Status Code ServiceUnavailable. No further error information was
returned by the service.
Reviewing Amazon documentation, the ServiceUnavailable error occurs when you are making too many requests to S3 in a short space of time. This isn't true of this scenario however. We are making only one request, and even if we were making many requests, this would not explain why on one server it works fine, but on another it doesn't (with the only difference being the presence of a Proxy).
Any advice would be appreciated.
(Well shoot, if no one else wants the reputation, I'll take it. ;) )
There are at least three things I can think of (ht to #Collin-Dauphinee).
Access to the bucket may be restricted to the non-proxy'd machine's IP.
Your proxy may be mangling the request.
Your proxy may be refusing to forward the request.
We have a SOAP based web service and we are able to read its wsdl when we type in the url in Browser. We sit behind a proxy in our network but its not blocking anything and we are always able to read wsdl using browser.But when we enter the url in Browser say http://ist.services/CoreServices/coreservices?wsdl it asks for username and password which is not same as my windows credentials. So when i enter the username and password shared by the dev team , it returns the wsdl page. Please note that this webservice is developed and deployed on java based server.
How do i do the same in c#.net code and how do i pass the Security Crednetials in DiscoveryClientProtocol? I tried the below code which works for the webservices which doesn't ask for the Security credentials.
// Specify the URL to discover.
string sourceUrl = "http://ist.services/CoreServices/coreservices?wsdl";
string outputDirectory = "C:\\Temp";
DiscoveryClientProtocol client = new DiscoveryClientProtocol();
var credentials = new NetworkCredential("sunuser1", "xxxxxxx", "");
WebProxy proxy = new WebProxy("http://proxy.bingo:8000/", true) { Credentials = credentials };
client.Credentials = credentials;
// Use default credentials to access the URL being discovered.
//client.Credentials = credentials;//CredentialCache.DefaultCredentials;
client.Proxy = proxy;
String DiscoverMode = "DiscoverAny";
String ResolveMode = "ResolveAll";
try
{
DiscoveryDocument doc;
// Check to see if whether the user wanted to read in existing discovery results.
if (DiscoverMode == "ReadAll")
{
DiscoveryClientResultCollection results = client.ReadAll(Path.Combine("C:\\Temp", "results.discomap"));
//SaveMode.Value = "NoSave";
}
else
{
// Check to see if whether the user wants the capability to discover any kind of discoverable document.
if (DiscoverMode == "DiscoverAny")
{
doc = client.DiscoverAny(sourceUrl);
}
else
// Discover only discovery documents, which might contain references to other types of discoverable documents.
{
doc = client.Discover(sourceUrl);
}
// Check to see whether the user wants to resolve all possible references from the supplied URL.
if (ResolveMode == "ResolveAll")
client.ResolveAll();
else
{
// Check to see whether the user wants to resolve references nested more than one level deep.
if (ResolveMode == "ResolveOneLevel")
client.ResolveOneLevel();
else
Console.WriteLine("empty");
}
}
}
catch (Exception e2)
{
//DiscoveryResultsGrid.Columns.Clear();
//Status.Text = e2.Message;
Console.WriteLine(e2.Message);
}
// If documents were discovered, display the results in a data grid.
if (client.Documents.Count > 0)
Console.WriteLine(client);
}
}
Since the code didn't help me much , i opened the fiddler to trace the http calls when i manual read the wsdl in browser and i see it takes the credentials i entered as "Authorization: Basic cGDFDdsfdfsdsfdsgsgfg=" . In fiddler i see three calls with responses 401,302 and 200. But in my c#.net code i don't get the 200 response and it always throws me the 404 error.
I further debugged this and in httpresponse of client object i see the flag status as INVOCATION_FLAGS_INITIALIZED | INVOCATION_FLAGS_NEED_SECURITY
So looks like i need to pass the credentials as Security Credentials rather than Network credentials.
The below code has fixed the issue.
CredentialCache myCredentialCache = new CredentialCache { { new Uri(sourceUrl),
"Basic", networkCredential } };
discoveryClientProtocol.AllowAutoRedirect = true;
discoveryClientProtocol.Credentials = myCredentialCache;
I'm using a simple implementation of the Windows Service Bus 1.0 Brokered messaging to keep track of the user interactions with a particular web application.
Every time something is saved to a "sensitive" table in the database, I have setup the repository layer send a message like so:
ServiceBus.MessageQueue<T>.PushAsync(entity);
which will then serialize the entity and create a message out of it.
My MessageQueue class is something like this.
public static class MessageQueue<T>
{
static string ServerFQDN;
static int HttpPort = 9355;
static int TcpPort = 9354;
static string ServiceNamespace = "ServiceBusDefaultNamespace";
public static void PushAsync(T msg)
{
ServerFQDN = System.Net.Dns.GetHostEntry(string.Empty).HostName;
//Service Bus connection string
var connBuilder = new ServiceBusConnectionStringBuilder { ManagementPort = HttpPort, RuntimePort = TcpPort };
connBuilder.Endpoints.Add(new UriBuilder() { Scheme = "sb", Host = ServerFQDN, Path = ServiceNamespace }.Uri);
connBuilder.StsEndpoints.Add(new UriBuilder() { Scheme = "https", Host = ServerFQDN, Port = HttpPort, Path = ServiceNamespace}.Uri);
//Create a NamespaceManager instance (for management operations) and a MessagingFactory instance (for sending and receiving messages)
MessagingFactory messageFactory = MessagingFactory.CreateFromConnectionString(connBuilder.ToString());
NamespaceManager namespaceManager = NamespaceManager.CreateFromConnectionString(connBuilder.ToString());
if (namespaceManager == null)
{
Console.WriteLine("\nUnexpected Error");
return;
}
//create a new queue
string QueueName = "ServiceBusQueueSample";
if (!namespaceManager.QueueExists(QueueName))
{
namespaceManager.CreateQueue(QueueName);
}
try
{
QueueClient myQueueClient = messageFactory.CreateQueueClient(QueueName);
string aaa = JsonConvert.SerializeObject(msg, Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver()
});
BrokeredMessage sendMessage1 = new BrokeredMessage(aaa);
sendMessage1.Properties.Add("UserName",Thread.CurrentPrincipal.Identity.Name);
sendMessage1.Properties.Add("TimeStamp", ApplicationDateTime.Now);
sendMessage1.Properties.Add("Type", msg.GetType().Name);
myQueueClient.Send(sendMessage1);
}
catch (Exception e)
{
var l = new Logger();
l.Log(LogEventEnum.WebrequestFailure, e.Message);
Console.WriteLine("Unexpected exception {0}", e.ToString());
throw;
}
}
}
This works flawlessly when I debug this locally. But when I publish the site in IIS and run, the namespaceManager.QueueExists(QueueName) call fails with an exception which says "401 Unauthorized error".
When I change the Application pool identity (in IIS) to an admin account this error does not occur. However, there is absolutely no way that I can make this happen when we go production.
Am I missing something? If so, what is it? Any help is greatly appreciated.
Did you read the security section in the docs, Chameera? http://msdn.microsoft.com/en-us/library/windowsazure/jj193003(v=azure.10).aspx
You seem to be running with the default security settings, meaning you only have admin accounts authorized. Review the documentation section and grant the requisite rights to the accounts you want to use in prod.
I'm having a problem with calling a web service request in C#.
The service and request are working fine in Soap UI with the option 'Authenticate Preemptively' enabled (File, Preferences, HTTP Settings). Without this setting enabled the service returns a 'Java.Lang.NullPointerException'.
The problem I'm having is that I do not know how to enable this setting in a C# context.
I have a .NET 3.5 class library which holds a so called service reference to the specific service. This is a simple code snippet;
try
{
CatalogService.CatalogChangeClient service = new CatalogService.CatalogChangeClient();
service.ClientCredentials.UserName.UserName = "fancydress";
service.ClientCredentials.UserName.Password = "47fda9cb4b51a9e";
service.ClientCredentials.SupportInteractive = true;
ProductUpdate[] products = new ProductUpdate[1];
products[0] = new ProductUpdate();
products[0].ProductCode = "00001";
products[0].ProductDescription = "TestProduct";
string result = service.UpdateProducts(products);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
Update after first reply.
The CatalogService.CatalogChangeClient class seems to implement the WCF abstract class
System.ServiceModel.ClientBase<TChannel>
End Update
Could anyone help me set this property?
You could try and override the GetWebRequest method from your generated client stub. I have used this once and that solved my problem.
Look at the following URL:
http://www.eggheadcafe.com/community/wcf/18/10056093/consuming-webservices-and-http-basic-authentication.aspx
Scroll a bit down.
Here's the code from the link:
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request;
request = (HttpWebRequest)base.GetWebRequest(uri);
if (PreAuthenticate)
{
NetworkCredential networkCredentials =
Credentials.GetCredential(uri, "Basic");
if (networkCredentials != null)
{
byte[] credentialBuffer = new UTF8Encoding().GetBytes(
networkCredentials.UserName + ":" +
networkCredentials.Password);
request.Headers["Authorization"] =
"Basic " + Convert.ToBase64String(credentialBuffer);
}
else
{
throw new ApplicationException("No network credentials");
}
}
return request;
}