Add Request header to WCF when using ConfigurationChannelFactory.CreateChannel - wcf-security

I need to add a Request Header to a WCF Request when using ConfigurationChannelFactory.CreateChannel.
I have already tried using OperationContextScope.
I have a function which is as shown below:
public O Execute<O>(Func<T, O> action, string configFilePath, string endpoint, StringDictionary headers)
{
bool closed = false;
T channel = default(T);
O output = default(O);
try
{
channel = this.GetChannel(configFilePath, endpoint);
if (headers != null && headers.Count > 0)
{
(channel as IClientChannel).Open();
using (new OperationContextScope(channel as IClientChannel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
foreach (DictionaryEntry header in headers)
{
requestMessage.Headers[header.Key.ToString()] = header.Value.ToString();
}
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
output = action(channel);
}
(channel as IClientChannel).Close();
}
else
{
(channel as IClientChannel).Open();
output = action(channel);
(channel as IClientChannel).Close();
}
closed = true;
}
finally
{
if (!closed && channel != null)
{
(channel as IClientChannel).Abort();
}
}
return output;
}
private T GetChannel(string configFilePath, string endpoint)
{
//Get the ChannelFactoryObject
ConfigurationChannelFactory<T> wcfClientFactory = null;
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFilePath };
wcfClientFactory = new ConfigurationChannelFactory<T>(endpoint, ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None), null);
return wcfClientFactory.CreateChannel();
}
Configuration file entry:
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />;clientCredentialType="Windows" negotiateServiceCredential="true" />
</security>
The above function is called from another .cs file, as shown below, passing Func<T,O> as an argument:
Execute<MyService.InformationResponse[]>=>IMyService.GetInformation(Request), ConfigPath, myServiceEndPoint, headers);
I am getting 400, BadRequest as the Service is expecting "Authorization" in the Request header, which it is not able to find.

We could use the WebOperationContext class to alter and add HTTP header, please refer to the below code segments.
IService service = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)service))
{
WebOperationContext.Current.OutgoingRequest.ContentType = "application/json; charset=utf-8";
WebOperationContext.Current.OutgoingRequest.Headers.Add("Authorization", "bearer xxxxxxxx");
service.GetData();
}
Result.
For details,
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.web.weboperationcontext?redirectedfrom=MSDN&view=netframework-4.8
Feel free to let me know if there is anything I can help with.

Related

Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'

further to this question, i have the same problem. PubFolder on Prem , users in O365
I have fetched and added the routing headers from Glen's post but still get the error
GetToken works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
GetX headers works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/public-folder-access-with-ews-in-exchange
--->> ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10))
Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'
static async System.Threading.Tasks.Task Test3()
{
string ClientId = ConfigurationManager.AppSettings["appId"];
string TenantId = ConfigurationManager.AppSettings["tenantId"];
string secret = ConfigurationManager.AppSettings["clientSecret"];
string uMbox = ConfigurationManager.AppSettings["userId"];
string uPwd = ConfigurationManager.AppSettings["userPWD"];
// Using Microsoft.Identity.Client 4.22.0
//https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth//
var cca = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(secret)
.WithTenantId(TenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
var authResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, uMbox);
AutodiscoverService autodiscoverService = GetAutodiscoverService(uMbox, uPwd);
GetUserSettingsResponse userResponse = GetUserSettings(autodiscoverService, uMbox, 3, UserSettingName.PublicFolderInformation, UserSettingName.InternalRpcClientServer);
string pfAnchorHeader= userResponse.Settings[UserSettingName.PublicFolderInformation].ToString();
string pfMailboxHeader = userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString(); ;
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
//get Public folder root
//Include x-anchormailbox header
Console.WriteLine("X-AnchorMailbox value for public folder hierarchy requests: {0}", pfAnchorHeader);
Console.WriteLine("X-PublicFolderMailbox value for public folder hierarchy requests: {0}", pfMailboxHeader);
//var test3 = GetMailboxGuidAddress(ewsClient, pfAnchorHeader, pfMailboxHeader, uMbox);
///https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests <summary>
ewsClient.HttpHeaders.Add("X-AnchorMailbox", userResponse.Settings[UserSettingName.PublicFolderInformation].ToString());
//ewsClient.HttpHeaders.Add("X-AnchorMailbox", "SharedPublicFolder#contoso.com");
ewsClient.HttpHeaders.Add("X-PublicFolderMailbox", userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString());
try
{
var pubfolders = ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
public static AutodiscoverService GetAutodiscoverService(string username, string pwd)
{
AutodiscoverService adAutoDiscoverService = new AutodiscoverService();
adAutoDiscoverService.Credentials = new WebCredentials(username, pwd);
adAutoDiscoverService.EnableScpLookup = true;
adAutoDiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
adAutoDiscoverService.PreAuthenticate = true;
adAutoDiscoverService.TraceEnabled = true;
adAutoDiscoverService.KeepAlive = false;
return adAutoDiscoverService;
}
public static GetUserSettingsResponse GetUserSettings(
AutodiscoverService service,
string emailAddress,
int maxHops,
params UserSettingName[] settings)
{
Uri url = null;
GetUserSettingsResponse response = null;
for (int attempt = 0; attempt < maxHops; attempt++)
{
service.Url = url;
service.EnableScpLookup = (attempt < 2);
response = service.GetUserSettings(emailAddress, settings);
if (response.ErrorCode == AutodiscoverErrorCode.RedirectAddress)
{
url = new Uri(response.RedirectTarget);
}
else if (response.ErrorCode == AutodiscoverErrorCode.RedirectUrl)
{
url = new Uri(response.RedirectTarget);
}
else
{
return response;
}
}
throw new Exception("No suitable Autodiscover endpoint was found.");
}
Your code won't work against an OnPrem Public folder tree as EWS in Office365 won't proxy to an OnPrem Exchange Org (even if hybrid is setup). (Outlook MAPI is a little different and allows this via versa setup but in that case it never proxies either it just makes a different connection to that store and its all the Outlook client doing this).
Because your trying to use the client credentials oauth flow for that to work onPrem you must have setup hybrid modern authentication https://learn.microsoft.com/en-us/microsoft-365/enterprise/hybrid-modern-auth-overview?view=o365-worldwide. Then you need to acquire a token with an audience set to the local OnPrem endpoint. (this is usually just your onPrem ews endpoint's host name but it should be one of the service principal names configured in your hybrid auth setup Get-MsolServicePrincipal). So in your code you would change
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
to
var ewsScopes = new string[] { "https://OnPrem.whatever.com/.default" };
which will then give you a token with an audience set for the onprem server then you need to send the EWS request to that endpoint so change that eg
ewsClient.Url = new Uri("https://OnPrem.whatever.com/EWS/Exchange.asmx");
if Hybird Modern Auth is setup then you need to default back to use Integrated or Basic Authenticaiton.

Reverse proxy in .NET Core Middleware “set-cookie” response does not set in browser and not showing in HttpResponseMessage

Here I am making a reverse proxy server to bypass an ASP.NET web application (following this tutorial). I am trying to read the session ID cookie from HttpResponseMessage. I used a cookie container as well but am unable to find it. Implemented in ASP.NET core invoke method, session is working properly but unable to catch session ID in request or response.
public async Task Invoke(HttpContext context, IBrowserDetector detector)
{
//context.Session.SetString(SessionKeyName, "The Doctor");
var browser = detector.Browser;
var targetUri = BuildTargetUri(context.Request);
if (context.Request.Method != HttpMethod.Get.Method)
{
var remoteIp = context.Connection.RemoteIpAddress;
//var gg= context.Request.Headers.ContainsKey.;
var clienttdatetime = context.Request.Headers["Date"].ToString();
//_logger.LogDebug("Request from Remote IP address: {RemoteIp}", remoteIp);
var badIp = true;
var bytes = remoteIp.GetAddressBytes();
//var testIp = IPAddress.Parse(address);
//if (testIp.GetAddressBytes().SequenceEqual(bytes))
//{
// badIp = false;
// break;
//}
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
IPAddress remoteIpAddress = context.Request.HttpContext.Connection.RemoteIpAddress;
string result = "";
if (remoteIpAddress != null)
{
// If we got an IPV6 address, then we need to ask the network for the IPV4 address
// This usually only happens when the browser is on the same machine as the server.
if (remoteIpAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
remoteIpAddress = System.Net.Dns.GetHostEntry(remoteIpAddress).AddressList
.First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
}
result = remoteIpAddress.ToString();
}
if (badIp)
{
//_logger.LogWarning(
// "Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
//context.Response.StatusCode = StatusCodes.Status403Forbidden;
//return;
}
}
if (targetUri != null)
{
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
IEnumerable<Cookie> responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
foreach (Cookie cookie_ in responseCookies)
Console.WriteLine(cookie_.Name + ": " + cookie_.Value);
// ExtractCookiesFromResponse(responseMessage);
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
//if(responseMessage.RequestMessage.RequestUri.ToString()== "http://localhost:51125/Menu.aspx")
//{
//Uri uri = new Uri("http://localhost:5000/login.aspx");
//Build the request
//Uri site = targetUri;
// HttpWebRequest request = (HttpWebRequest)WebRequest.Create(site);
// CookieContainer cookiesq = new CookieContainer();
// request.CookieContainer = cookiesq;
// //Print out the number of cookies before the response (of course it will be blank)
// Console.WriteLine(cookiesq.GetCookieHeader(site),"1");
// //Get the response and print out the cookies again
// using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
// {
// Console.WriteLine(cookiesq.GetCookieHeader(site), "2");
// }
// Console.ReadKey();
//}
var cookie = context.Request.Cookies["ASP.NET_SessionId"];
}
return;
}
await _nextMiddleware(context);
}
------------------------------------------------------------------------------------
public static IDictionary<string, string> ExtractCookiesFromResponse(HttpResponseMessage response)
{
IDictionary<string, string> result = new Dictionary<string, string>();
IEnumerable<string> values;
if (response.Headers.TryGetValues("Set-Cookie", out values))
{
SetCookieHeaderValue.ParseList(values.ToList()).ToList().ForEach(cookie =>
{
result.Add(cookie.Name.ToString(), cookie.Value.ToString());
});
}
return result;
}
As far as I can see, you created the HttpClientHandler but didn't use it to build the HttpClient to make your request. You are still using the static _httpClient that nothing knows about the cookie container you created.
This should be the reason why you get the CookieContainer still empty.
Take a look here to learn how to get cookies from an HttpResponseMessage.
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
_httpClient = new HttpClient(handler);
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
//var responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
IEnumerable<Cookie> responseCookies = cookies.GetCookies(targetUri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
if(cookie.Name=="ASP.NET_SessionId")
{
Console.WriteLine(cookie.Name + ": " + cookie.Value);
context.Response.Headers.Add("Set-Cookie", cookie.Name+"="+cookie.Value);
}
}

WebAPI call hangs when return a large amount of data

I have a web api call that I recently added to my app. I returns a complete list of all countries, states and cities in the app (currently 486 rows) I perform this call when all of the reference data for my application loads (I have a base loading page and call the function in my startup class to load all the data there). The challenge is that the call to get all my countries.... hangs and eventually I get "The operation was canceled" error. If I modify my stored procedure that selects the data from the database on the server to only return say 20 rows, it runs fine. Any suggestions?
Below is the code from the startup class:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace GBarScene
{
class StartUpClass
{
public event GeneralDataLoad BaseDataLoadComplete;
public async Task<GBSStartUpEventArgs> ProcessStartup()
{
GBSStartUpEventArgs lobj_EventArgs;
lobj_EventArgs = new GBSStartUpEventArgs();
App.InStartUpDataLoad = true;
try
{
if (!App.IsGeolocationEnabled)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoLocationServicesMessage");
}
else if (!App.InternetIsAvailable)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoInternetConnectionFound");
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
}
else
{
Debug.WriteLine("Process StartUp");
await Task.Delay(500);
//Reset values
ViewModelObjects.DayOfWeek.DataLoadProcessed = false;
ViewModelObjects.Languages.DataLoadProcessed = false;
if (await ViewModelObjects.DayOfWeek.LoadData() == false)
// //try it once more
await ViewModelObjects.DayOfWeek.LoadData();
Debug.WriteLine("GBar After DayofWeek Load");
await ViewModelObjects.Languages.LoadData();
Debug.WriteLine("GBar After Languages Load");
if ((ge_AppMode)ViewModelObjects.AppSettings.AppMode == ge_AppMode.CitySelected)
{
//We need to reload the NearbyCities and set the selected one
await ViewModelObjects.NearbyCities.LoadData();
}
Debug.WriteLine("Before load of coutries");
await ViewModelObjects.CountryStateCity.LoadData();
Debug.WriteLine("After load of coutries");
Debug.WriteLine("Count: " + ViewModelObjects.CountryStateCity.CountryItems_ForList.Count.ToString());
ViewModelObjects.NumberOfResults.LoadData();
ViewModelObjects.Perspectives.LoadData();
ViewModelObjects.SearchRadiuses.LoadData();
ViewModelObjects.UseMetric.LoadData();
while (!ViewModelObjects.DayOfWeek.DataLoadProcessed && !ViewModelObjects.Languages.DataLoadProcessed && !App.IsGeolocationEnabled)
{
await Task.Delay(100);
}
if (App.BaseDataLoadError)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("ErrorLoadingReferenceData");
}
}
Debug.WriteLine("StartUp Process Ended");
BaseDataLoadComplete(this, lobj_EventArgs);
}
catch (Exception ex)
{
App.ProcessException(ex);
}
App.InStartUpDataLoad = false;
return lobj_EventArgs;
}
}
}
This is the helper class that makes all the WebAPI calls:
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace GBarScene
{
public class WebAPICaller: IDisposable
{
HttpClient iobj_HTTPClient = null;
public void Dispose()
{
if (iobj_HTTPClient != null)
iobj_HTTPClient.Dispose();
}
public async Task<string> HTTPGetWebServiceAsync(string ps_URI)
{
string ls_Response = "";
string ls_JSONData = "";
string ls_Prefix = "";
try
{
iobj_HTTPClient = await GetClient();
switch (Device.RuntimePlatform)
{
case Device.Android:
ls_Prefix = App.APIStandardPrefix;
break;
//case Device.Android:
// ls_Prefix = App.APISecurePrefix;
// break;
//case Device.Windows:
//case Device.WinPhone:
// ls_Prefix = App.APISecurePrefix;
// break;
default:
ls_Prefix = App.APISecurePrefix;
break;
}
Debug.WriteLine("before api call");
iobj_HTTPClient.BaseAddress = new Uri(ls_Prefix);
ls_JSONData = await iobj_HTTPClient.GetStringAsync(ps_URI);
Debug.WriteLine("after api call");
ls_Response = System.Net.WebUtility.HtmlDecode(ls_JSONData);
}
catch (Exception ex)
{
Debug.WriteLine("api call error");
App.ProcessException(ex);
}
return ls_Response;
}
public async Task<bool> HTTPPostWebService(string ps_URI, object pobj_BodyObject)
{
HttpResponseMessage lobj_HTTPResponse = null;
bool lb_Response = false;
HttpContent lobj_Content = null;
try
{
if (iobj_HTTPClient != null)
iobj_HTTPClient = await GetClient();
iobj_HTTPClient.BaseAddress = new Uri(App.APISecurePrefix);
lobj_Content = new StringContent(JsonConvert.SerializeObject(pobj_BodyObject == null ? "" : pobj_BodyObject));
lobj_Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
lobj_HTTPResponse = await iobj_HTTPClient.PostAsync(ps_URI, lobj_Content);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Exception lobj_Exception = new Exception(lobj_HTTPResponse.ToString());
lobj_Exception.Source = "HTTPGetWebService for: " + ps_URI;
App.ProcessException(lobj_Exception);
}
else
{
lb_Response = true;
}
}
catch (Exception ex)
{
App.ProcessException(ex);
}
finally
{
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
//Debug.WriteLine("WebAPICaller-CallWebService-1: Done");
}
return lb_Response;
}
private async Task<HttpClient> GetClient()
{
HttpClient lobj_HTTPClient = null;
if (lobj_HTTPClient == null)
{
lobj_HTTPClient = new HttpClient();
lobj_HTTPClient.DefaultRequestHeaders.Add("Accept", "application/json");
lobj_HTTPClient.MaxResponseContentBufferSize = 2147483647;
lobj_HTTPClient.Timeout = new TimeSpan(0,0,0,0,60000);
}
return lobj_HTTPClient;
}
}
}
Sorry I forget to include the method in the CountryStateCity view model that calls the webapi helper class.
public async Task<bool> LoadData()
{
string ls_Response = "";
string ls_WorkURI = "";
WebAPICaller lobj_WebAPICaller = null;
bool lb_DataLoaded = false;
try
{
IsDataLoaded = false;
//Debug.WriteLine("City Data Load");
lobj_WebAPICaller = new WebAPICaller();
ls_WorkURI = ic_CoutryStateCityAPIUrl.Replace("{Language}", "EN");
ls_Response = await lobj_WebAPICaller.HTTPGetWebServiceAsync(ls_WorkURI);
if (ls_Response.Trim().Length == 0)
{
AddErrorEntry();
}
else
{
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>();
StateItems_ForList = new ObservableCollection<GBSState_ForList>();
CityItems_ForList = new ObservableCollection<GBSCity_ForList>();
iobj_CountryStateCity = JsonConvert.DeserializeObject<ObservableCollection<GBSCountryStateCity>>(ls_Response);
//Now load the display lists
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>(
(from lobj_Country in iobj_CountryStateCity
select new GBSCountry_ForList()
{
ID = lobj_Country.Country_Code,
Value = lobj_Country.Country_Name_Text
}).Distinct().ToList());
CountryItems_ForList.Insert(0, new GBSCountry_ForList
{
ID = "XX",
Value = "Base Value"
});
lb_DataLoaded = true;
}
}
catch (Exception ex)
{
AddErrorEntry();
App.ProcessException(ex);
}
finally
{
IsDataLoaded = true;
if (lobj_WebAPICaller != null)
lobj_WebAPICaller.Dispose();
}
return lb_DataLoaded;
}
So after much time, I believe I figured out what the problem is. The problem started to manifest itself again with smaller amounts of data and I could not figure out why. The problem appeared. The issue appears to be the IP address I was using. (I was using the IP address of the actual laptop I was hosting both the App and WebAPIs on.) It appears you have to use one of the other network adaptors for the emulator to have this work reliably.
Here are the steps I used to resolved this:
I launched my Windows 10 mobile emulator.
Click on the >> (Tools) icon in the tool bar of the emulator.
Click on the Network tab of the Additional Tools window.
Look in the list for the network adaptor labeled Desktop Adaptor #1 and copy the IP address.
Edit the Applicationhost.config file in the folder of the WebAPI project.
Find the entry in the file for site name="XXXXX" where XXXXX is the name of the Visual Studio project you are hosting your WebAPIs in.
Within the section of the entry for your WebAPI project, add a binding for the IP address you copied from in step 4. It should look something like this:
<binding protocol="http" bindingInformation="*:56952:169.254.69.220" />
Where 56952 is the port my IIS Express is hosting the WebAPIs on and 169.254.69.220 is the IP address I copied from step 4. After adding this, I was able to connect to locally hosted WebAPIs in IIS Express.
Hope this helps.

How to make test payments on Eway in asp.net mvc project?

I started working in a project where the payment for eway is already configured in this project. I cannot figure out from the eway website on how to configure it to make test payments.
The current settings in web.config are:
<appSettings>
<add key="PaymentGateway" value="2"/>
<add key="EwayRedirectUrl" value="http://localhost:54053/#/Memberships"/>
<add key="EwayApiKey" value="60CF3Ce97nRXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"/>
<add key="EwayPassword" value="API-P4ss"/>
<add key="EwayEnvironment" value="Sandbox"/>
</appSettings>
The following doc in the eway website says that using this Test Credit Card: 4444333322221111 I can make dummy payments.
Am I unable to make test payments because it is a live gateway and not a test gateway? Can someone help me make test payments please?
EDIT:
I have some javascript code that encrypts the card and then passes to the controller. Is the problem here? Maybe the encryption isn't done properly?
function (card, membershipId, autoRenew) {
var encryptCard = angular.copy(card);
if (card !== null && !encryptCard.Token) {
encryptCard.CardNumber = eCrypt.encryptValue(card.CardNumber, key);
encryptCard.Cvv = eCrypt.encryptValue(card.Cvv, key);
}
var defer = $q.defer();
$http.post("/Inbox/ProcessMembershipRenewPayment", parseJsonObj({ card: encryptCard, membershipId: membershipId, autoRenew:autoRenew }))
.then(
function(response) {
var data = response.data;
if (data.Status) {
defer.resolve(data.Message);
} else {
defer.reject(data.Message);
}
},
function(err) {
defer.reject(err);
}
);
return defer.promise;
}
I am getting some squiggly lines under 'eCrypt' saying: Use of implicitly declared variable 'eCrypt'.
In the cshtml I included the link to the js file:
<script src="https://secure.ewaypayments.com/scripts/eCrypt.js"></script>
In case encryption is not the problem, this is the method that requests payment to eway:
private IRapidClient _ewayClient;
private readonly string _rapidEndpoint = ConfigurationManager.AppSettings["EwayEnvironment"];
public void Initialize(string apiKey, string password)
{
_ewayClient = RapidClientFactory.NewRapidClient(apiKey, password, _rapidEndpoint);
}
public EwayPaymentResponse Pay(EwayPaymentRequest request)
{
var ewayResponse = new EwayPaymentResponse {Status = false};
try
{
Transaction transaction = new Transaction
{
Customer = new Customer()
{
CardDetails = new CardDetails()
{
Name = request.Customer.CardDetails.Name,
Number = request.Customer.CardDetails.Number,
ExpiryMonth = request.Customer.CardDetails.ExpiryMonth,
ExpiryYear = request.Customer.CardDetails.ExpiryYear,
CVN = request.Customer.CardDetails.CVN
}
},
PaymentDetails = new PaymentDetails()
{
CurrencyCode = "AUD",
TotalAmount = Convert.ToInt32(request.Payment.TotalAmount * 100)
},
TransactionType = TransactionTypes.Purchase,
RedirectURL = ConfigurationManager.AppSettings["EwayRedirectUrl"],
};
CreateTransactionResponse response = _ewayClient.Create(PaymentMethod.Direct, transaction);
if (response.TransactionStatus.Status != null && (bool) response.TransactionStatus.Status)
{
ewayResponse.Status = true;
return ewayResponse;
}
if (response.Errors != null)
{
foreach (var errorCode in response.Errors)
{
ewayResponse.ErrorMessages.Add(RapidClientFactory.UserDisplayMessage(errorCode, "EN"));
}
return ewayResponse;
}
ewayResponse.ErrorMessages.Add("Invalid credit card information.");
return ewayResponse;
}
catch (Exception e)
{
ewayResponse.ErrorMessages.Add(e.Message);
return ewayResponse;
}
}
The payment method here is direct.

I can sign but am unable to verify SignedXML document in .NET 4.0 using custom SHA256 encryption

Long story short, I can sign documents just fine. But verifying them returns false, every time. I've looked through the first 5 or 6 pages of google and bing for a solution, but every "fix" I've found fails to help. Any help would be appreciated, as I've been trying to tackle this for a little over a week now. Also, the private keys of these certs are NOT exportable, and can't be. I'm not sure if that matters or not. Also, I opted not to include ValidateCertificate for now because it's never once made it that far. It always fails at CheckSignature(). Thank you.
HEre's what I'm using to generate my certs:
makecert -a SHA256 -n "CN=JEA2.me" -pe -r -len 2048 -sy 24 -sky signature -sv jeame2.pvk jeame2.cer
certmgr /add jeame2.cer /s /r localmachine root
makecert -sk "jea2.me" -iv jeame2.pvk -n "CN=JEA2IIS.me" -eku 1.3.6.1.4.1.311.10.3.12 -pe -sy 24 -ss my -sr localmachine -len 2048 -sky signature -ic Jeame2.cer IIS-ServerCert-Jeame2.cer
And from here I'm installing them directly into Local machine -> Trusted Root Certification Authorities
private static X509Certificate2 CheckXmldsigSignature(XmlDocument document)
{
X509Certificate2 certificate = null;
try
{
XmlNodeList nodeList = document.GetElementsByTagName("Signature", Xmldsigns);
if (nodeList.Count != 1)
{
Logger.ErrorFormat("Found {0} signature elements in file", nodeList.Count);
throw new InvalidOperationException(
"The XML document must have a single element with local name: \"Signature\" and namespace URI: " + Xmldsigns);
}
else
{
Logger.DebugFormat("Found Signature element successfully");
}
RSAPKCS1SHA256SignatureDescription.Register();
var signatureElement = (XmlElement)nodeList[0];
var signedXml = new SignedXml(document);
signedXml.LoadXml(signatureElement);
var keyInfoX509 =
(KeyInfoX509Data)
(from KeyInfoClause kic in signedXml.KeyInfo where kic is KeyInfoX509Data select kic).Single();
if (keyInfoX509.Certificates.Count != 1)
{
var msg = "The signature must contain information for one certificate.";
Logger.Error(msg);
throw new InvalidOperationException(msg);
}
else
{
Logger.DebugFormat("Extracted X509 certificate data successfully");
}
certificate = (X509Certificate2)keyInfoX509.Certificates[0];
bool validSignature = signedXml.CheckSignature(); //was null parameters. This too does not work.
if (!validSignature)
{
var msg = " SignedXml.CheckSignature returned false.";
throw new InvalidOperationException(msg);
}
else
{
Logger.DebugFormat("SignedXml.CheckSignature returned true.");
}
}
catch (Exception ex)
{
ScriptPro.Common.Logging.LogEx.LogException(Logger, ex);
throw;
}
return certificate;
}
private static Stream SignSHA256Stream(X509Certificate2 certificate, Stream stream)
{
if (certificate == null)
{
Logger.Error("certificate argument is null");
throw new ArgumentNullException("certificate");
}
if (stream == null)
{
Logger.Error("stream argument is null");
throw new ArgumentNullException("stream");
}
RSAPKCS1SHA256SignatureDescription.Register();
var document = new XmlDocument();
document.PreserveWhitespace = true; // May not be necessary.
document.Load(stream);
XmlNode root = document.DocumentElement;
XmlNodeList nodeList = document.GetElementsByTagName("Signature", Xmldsigns);
while (nodeList.Count > 0)
{
root.RemoveChild(nodeList[0]);
}
Reference reference = new Reference(string.Empty);
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
CspParameters csp = new CspParameters(24);
csp.Flags = CspProviderFlags.UseMachineKeyStore;
csp.KeyContainerName = "XML_DISG_RSA_KEY";
RSACryptoServiceProvider key = new RSACryptoServiceProvider(csp);
key.PersistKeyInCsp = false;
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
SignedXml sxml = new SignedXml(document);
sxml.KeyInfo = keyInfo;
sxml.SigningKey = key;
sxml.SignedInfo.SignatureMethod = Xmldsigns256;
sxml.AddReference(reference);
sxml.ComputeSignature();
XmlElement xmlDigitalSignature = sxml.GetXml();
if (document.DocumentElement == null)
{
document.AppendChild(document.ImportNode(xmlDigitalSignature, true));
}
else
{
document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature, true));
}
if (document.FirstChild is XmlDeclaration)
{
document.RemoveChild(document.FirstChild);
}
MemoryStream outStream = new MemoryStream();
document.Save(outStream);
return outStream;
}
private static void SignSHA256File(X509Certificate2 certificate, FileInfo file)
{
if (certificate == null)
{
Logger.Error("certificate argument is null");
throw new ArgumentNullException("certificate");
}
if (file == null)
{
Logger.Error("file argument is null");
throw new ArgumentNullException("file");
}
if (!file.Exists)
{
Logger.ErrorFormat("File {0} does not exist.", file.Name);
throw new ArgumentException("File must exist.", "file");
}
if (file.IsReadOnly)
{
Logger.ErrorFormat("File {0} is read only.", file.Name);
throw new ArgumentException("File is read only.", "file");
}
FileStream stream = file.OpenRead();
string s = string.Empty;
using (StreamReader reader = new StreamReader(stream))
{
s = reader.ReadToEnd();
}
MemoryStream stream2 = new MemoryStream(Encoding.Default.GetBytes(s));
Stream inStream = SignSHA256Stream(certificate, stream2);
XmlDocument document = new XmlDocument();
inStream.Seek(0L, SeekOrigin.Begin);
document.Load(inStream);
Logger.InfoFormat("Saving {0}", file.FullName);
document.Save(file.FullName);
}
public class RSAPKCS1SHA256SignatureDescription : SignatureDescription
{
private const int PROV_RSA_AES = 24;
public RSAPKCS1SHA256SignatureDescription()
{
this.KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider";
this.DigestAlgorithm = "System.Security.Cryptography.SHA256CryptoServiceProvider"; // use System.Security.Cryptography.SHA256Managed for .NET 4.5
this.FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
this.DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
}
public static void Register()
{
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
var asymmetricSignatureDeformatter = (AsymmetricSignatureDeformatter)CryptoConfig.CreateFromName(DeformatterAlgorithm);
asymmetricSignatureDeformatter.SetKey(key);
asymmetricSignatureDeformatter.SetHashAlgorithm("SHA256");
return asymmetricSignatureDeformatter;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
var asymmetricSignatureFormatter = (AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
asymmetricSignatureFormatter.SetKey(key);
asymmetricSignatureFormatter.SetHashAlgorithm("SHA256");
return asymmetricSignatureFormatter;
}
}
}
Here are my two XML Files:
1.xml:
<node1>
<node2>
</node2>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>3nIr0blku+Nsu3FgibCxfQRGBtSmtZL4JGodmaU8blE= </DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>O3ihm7QwE/vh9VZ6CtdENAhB9Ve8jceATCgdJuaQkUHpPWxrG01TftUlrw9a/dQGfW48jJMPngwgcfqnbFspmEEGsBe1xoWQd6mdy2wVRBcQSjqdReNNzs0uQz3/1wPPk4Y2UO+fL+CVNzkIcMpne+t80c2eU4cHBa1WyL5qSlc=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICFDCCAX2gAwIBAgIQ2rStbEE1JJhHRLiuA4n/0jANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtKQUxDT1JOLm1lMjAeFw0xNjAzMzAxODE1MDFaFw0zOTEyMzEyMzU5NTlaMBkxFzAVBgNVBAMTDkpBTENPUk5JSVMubWUyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLJEzyRjaHLfK6zrg1Xdx2yO34d5sbd7ajFFVmb3zPKtVGmuJlCBPsDTC84pzHBTywVVApi3U2UwtuCh96rQu5r3nYUT/E46CtexWiFATyh0M9+wD/h3hZj1CQ0YHTEZWznOWWIdbNRAcp99tGSALrwjH2rEJhGHHpVn7otCNmZQIDAQABo2AwXjATBgNVHSUEDDAKBggrBgEFBQcDATBHBgNVHQEEQDA+gBAgVyu7w3c59jEjiSh/vma+oRgwFjEUMBIGA1UEAxMLSkFMQ09STi5tZTKCEFvqkxy0Sd+mSgbqvsCEqKcwDQYJKoZIhvcNAQELBQADgYEAAcM6GlR3UpjIY4TWWuMiSyqiUiAGgg3JetiUXj1EVZ7TZVvyoVA1L/wd8ZHt+nZu1UtJmJ8sU7eu55TMVcX/xu7QoYsp6JtbPp5abLI6rnOCwDfyorrjM4S8Rm2RCO3PhL0NC9i9QBPfNV15FEbFpeqHZGw/xmyGzEv3EWxEESE=</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</node1>
2.xml:
<metadata>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>wc+6kgUoF9TE7KL1OQXm0EzAIYZuVVc6w3zOKsIY8yU= </DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>MDJn2QLG65LChsJOAN9zKmq4Br5JFSncaTMOmmsmL+DY4xcZt7e4VfI6/IehBkBUzDLeUJHWoE9sp7tVmArBiq/ZFm/ScB2/SRAAD+/NS0XxnxTPjvwu0JsmupNFJ364r/k31TYhI6TBmiCBIdZ6/3qV8LNPtS0iVrMkyhFw6L8=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICFDCCAX2gAwIBAgIQ2rStbEE1JJhHRLiuA4n/0jANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtKQUxDT1JOLm1lMjAeFw0xNjAzMzAxODE1MDFaFw0zOTEyMzEyMzU5NTlaMBkxFzAVBgNVBAMTDkpBTENPUk5JSVMubWUyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLJEzyRjaHLfK6zrg1Xdx2yO34d5sbd7ajFFVmb3zPKtVGmuJlCBPsDTC84pzHBTywVVApi3U2UwtuCh96rQu5r3nYUT/E46CtexWiFATyh0M9+wD/h3hZj1CQ0YHTEZWznOWWIdbNRAcp99tGSALrwjH2rEJhGHHpVn7otCNmZQIDAQABo2AwXjATBgNVHSUEDDAKBggrBgEFBQcDATBHBgNVHQEEQDA+gBAgVyu7w3c59jEjiSh/vma+oRgwFjEUMBIGA1UEAxMLSkFMQ09STi5tZTKCEFvqkxy0Sd+mSgbqvsCEqKcwDQYJKoZIhvcNAQELBQADgYEAAcM6GlR3UpjIY4TWWuMiSyqiUiAGgg3JetiUXj1EVZ7TZVvyoVA1L/wd8ZHt+nZu1UtJmJ8sU7eu55TMVcX/xu7QoYsp6JtbPp5abLI6rnOCwDfyorrjM4S8Rm2RCO3PhL0NC9i9QBPfNV15FEbFpeqHZGw/xmyGzEv3EWxEESE=</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</metadata>
public static bool VerifyXmldsigSignature(FileInfo file, bool useSHA256 = false)
{
Logger.InfoFormat("Checking Digital Signature and Certificate on {0}", file.FullName);
bool validCertificate = false;
if (file == null)
{
Logger.Error("file argument is null");
throw new ArgumentNullException("file");
}
if (!file.Exists)
{
Logger.ErrorFormat("File {0} does not exist.", file.Name);
throw new ArgumentException("File must exist.", "file");
}
try
{
var document = new XmlDocument();
document.PreserveWhitespace = true; document.Load(file.FullName);
DateTime timestamp = DateTime.UtcNow;
bool respectCertExpiration = HasTimestamp(document);
if (respectCertExpiration)
{
timestamp = CheckXadesTimestamp(document);
}
var certificate = CheckXmldsigSignature(document);
validCertificate = ValidateCertificate(certificate, timestamp, respectCertExpiration);
Logger.InfoFormat("Digital Signature and Certificate passed verification on {0}", file.FullName);
}
catch (Exception ex)
{
string message = string.Format("{0} failed signature verification.", file.FullName);
throw;
}
Looking at the code, there are two places that I can find that might be an issue:
Certificate Validity.
Using CheckSignature() without any parameters requires the signing certificate to be signed by a trusted root authority. As you're already extracting the certificate that is part of the signature I suggest that for testing you change the call to
bool validSignature = signedXml.CheckSignature(certificate);
Note that you know only validates that the Xml is signed by the certificate info in the file. You have no validation that the signature actually is done by any specific party. I assume you are doing that in the calling function as it returns the certificate.
Whitespace
In the signing routine you set PreserveWhitespace=true. That means that the whitespace will be included in the hash calculation of the signature. Make sure you set PreserveWhitespace=true when you load the document for validation too (that part is not included in the posted code, so I don't know).
References
Finally, you're code is vulnerable to Xml Signature Wrapping attacks, because you are not properly checking the references of the signature. Please see this blog post of mine for examples.
Anders, thanks for your suggestions. I finally got my code to verify after several weeks of playing around with it, and I finally got something to work so I wanted to share it with you guys. The verify method is unchanged, and the Xades stuff is custom and not necessary to verify files signed with my code. Lastly, I used the following 2 urls as my starting point, but dozens of pages from google also helped:
https://blogs.msdn.microsoft.com/winsdk/2015/11/14/using-sha256-with-the-signedxml-class/
https://gist.github.com/sneal/f35de432115b840c4c1f#file-rsapkcs1sha256signaturedescription
private static Stream SignSHA256Stream(X509Certificate2 certificate, Stream stream)
{
if (certificate == null)
{
Logger.Error("certificate argument is null");
throw new ArgumentNullException("certificate");
}
if (stream == null)
{
Logger.Error("stream argument is null");
throw new ArgumentNullException("stream");
}
RSAPKCS1SHA256SignatureDescription.Register();
var document = new XmlDocument();
document.Load(stream);
XmlNode root = document.DocumentElement;
XmlNodeList nodeList = document.GetElementsByTagName("Signature", Xmldsigns);
// nodeList is actively updated so we delete element [0] until there are none left.
while (nodeList.Count > 0)
{
root.RemoveChild(nodeList[0]);
}
Reference reference = new Reference(string.Empty);
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
SignedXml sxml = new SignedXml(document);
sxml.KeyInfo = keyInfo;
sxml.SigningKey = certificate.PrivateKey;
sxml.SignedInfo.SignatureMethod = Xmldsigns256;
sxml.AddReference(reference);
sxml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
sxml.ComputeSignature();
XmlElement xmlDigitalSignature = sxml.GetXml();
if (document.DocumentElement == null)
{
document.AppendChild(document.ImportNode(xmlDigitalSignature, true));
}
else
{
document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature, true));
}
if (document.FirstChild is XmlDeclaration)
{
document.RemoveChild(document.FirstChild);
}
MemoryStream outStream = new MemoryStream();
document.Save(outStream);
return outStream;
}

Resources