Has anyone used Sage Pay with asp.net Webpages - asp.net

Has anyone used Sage Pay with asp.net Webpages??
I have downloaded the integration kit from Sage Pay but this is made in webforms and i am having trouble converting it the WebPages format.
Sage Pay are no help so i'm hoping someone out there has done this. Or can point me in the right direction.

I have managed to do this using the class from the SagePay template you can download.
Put these files in your bin folder:-
SagePay.IntegrationKit.DotNet.dll
SagePay.IntegrationKit.DotNet.pdb
Put these files in your App_Code folder:-
SagePayConfiguration.cs
SagePayAPIIntegration.cs
SagePayFormIntegration.cs
You also need to add some stuff to your web.config file
<SagePayConfiguration>
<!--Mandatory
Set to TEST for the Test Server and LIVE for the live environment-->
<add key="sagepay.api.env" value="TEST" />
<!--Transaction Settings -->
<add key="sagepay.api.protocolVersion" value="3.00" />
<add key="sagepay.kit.vendorName" value="your Name" />
<add key="sagepay.kit.fullUrl" value="your url" />
<add key="sagepay.kit.currency" value="GBP" />
<!--Optional setting. It's recommended to set the siteFqdn value to the Fully
Qualified Domain Name of your server.
This should start http:// or https:// and should be the name by which our servers can call back to yours
i.e. it MUST be resolvable externally, and have access granted to the Sage Pay servers
examples would be https://yoursite or http://212.111.32.22/
NOTE: Do not include any URI path.
If you leave this value blank the kit will use the current host name-->
<add key="sagepay.kit.siteFqdn.LIVE" value="http://your web address" />
<add key="sagepay.kit.siteFqdn.TEST" value="http://your web address" />
<!--Mandatory. Usually PAYMENT. This can be DEFERRED or AUTHENTICATE if your Sage Pay
account supports those payment types
NB Ideally all DEFERRED transaction should be released within 6 days (according to card scheme rules).
DEFERRED transactions can be ABORTed before a RELEASE if necessary-->
<add key="sagepay.kit.defaultTransactionType" value="PAYMENT" />
<!--0 = If AVS/CV2 enabled then check them. If rules apply, use rules (default).
1 = Force AVS/CV2 checks even if not enabled for the account. If rules apply, use rules.
2 = Force NO AVS/CV2 checks even if enabled on account.
3 = Force AVS/CV2 checks even if not enabled for the account but DON'T apply any rules.-->
<add key="sagepay.kit.applyAvsCv2" value="0" />
<!--0 = If 3D-Secure checks are possible and rules allow, perform the checks and apply the authorisation rules. (default)
1 = Force 3D-Secure checks for this transaction if possible and apply rules for authorisation.
2 = Do not perform 3D-Secure checks for this transaction and always authorise.
3 = Force 3D-Secure checks for this transaction if possible but ALWAYS obtain an auth code, irrespective of rule base.-->
<add key="sagepay.kit.apply3dSecure" value="0" />
<!--FORM Protocol Only Settings
Set this value to the Encryption password assigned to you by Sage Pay -->
<add key="sagepay.kit.form.encryptionPassword.TEST" value="Your password" />
<add key="sagepay.kit.form.encryptionPassword.LIVE" value="Your password" />
<!--The Sage Pay server URLs to which customers will be sent for payment for each environment-->
<add key="sagepay.api.formPaymentUrl.LIVE" value="https://live.sagepay.com/gateway/service/vspform-register.vsp" />
<add key="sagepay.api.formPaymentUrl.TEST" value="https://test.sagepay.com/gateway/service/vspform-register.vsp" />
</SagePayConfiguration>
To make this easier to manage i have but this web.config file in the checkout folder so it is easy to keep updated.
I also created the following class to encrypt and dencrypt the data:-
you can call it what ever you want but it needs to be saved in the App_Code folder.
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.IO;
public static class EncryptionHelper
{
private static byte[] keyAndIvBytes;
static EncryptionHelper()
{
// You'll need a more secure way of storing this, I this isn't
// a real key
keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("123123123123123b");
}
public static string ByteArrayToHexString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string DecodeAndDecrypt(string cipherText)
{
string DecodeAndDecrypt = AesDecrypt(StringToByteArray(cipherText));
return (DecodeAndDecrypt);
}
public static string EncryptAndEncode(string plaintext)
{
return ByteArrayToHexString(AesEncrypt(plaintext));
}
public static string AesDecrypt(Byte[] inputBytes)
{
Byte[] outputBytes = inputBytes;
string plaintext = string.Empty;
using (MemoryStream memoryStream = new MemoryStream(outputBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
return plaintext;
}
public static byte[] AesEncrypt(string inputText)
{
byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q
byte[] result = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write))
{
cryptoStream.Write(inputBytes, 0, inputBytes.Length);
cryptoStream.FlushFinalBlock();
result = memoryStream.ToArray();
}
}
return result;
}
private static RijndaelManaged GetCryptoAlgorithm()
{
RijndaelManaged algorithm = new RijndaelManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 128;
algorithm.BlockSize = 128;
return algorithm;
}
}
I call the the class like so:-
string crypt = "blahblahblah";
string EncryptAndEncode = EncryptionHelper.EncryptAndEncode(crypt);
string DecodeAndDecrypt = EncryptionHelper.DecodeAndDecrypt(EncryptAndEncode);
When the transaction is complete i get the crypt with this code:-
IFormPaymentResult PaymentStatusResult = new DataObject();
if (Request.QueryString["crypt"] != null && !string.IsNullOrEmpty(Request.QueryString["crypt"]))
{
SagePayFormIntegration sagePayFormIntegration = new SagePayFormIntegration();
PaymentStatusResult = sagePayFormIntegration.ProcessResult(Request.QueryString["crypt"]);
}
you can then call the needed information from the calss like so
if (PaymentStatusResult.Status == ResponseStatus.NOTAUTHED)
{reason = "You payment was declined by the bank. This could be due to insufficient funds, or incorrect card details.";}
You can see all the fields in the Result.aspx of the SagePay template.

Related

Query web.config for a key - in values

Can any one let me know how to get a bool result, verifying a value exists in a web.config's key.
Scenario is,
I have this tag in my website...
<add key="isEnabled" value="False"/> for a website,
On this key value I keep my site 'on' and 'off' using
public static bool isEnabled = Convert.ToBoolean(WebConfigurationManager.AppSettings["isEnabled "]);
if(isEnabled)
{
//
}
now the requirement is got 3-4 websites now, want to change the above line to something like
<add key="SitesEnabled" value="1,4,5"/>
because i want to enable only 1st, 4th, 5th site
1 - is the static value for my 1st website, 2 - 2nd.....
But now how do I do on and off...my websites something like
public static bool OneSiteEnabled = Convert.ToBoolean(WebConfigurationManager.AppSettings[SitesEnabled="1"]); // true
public static bool TwoSiteEnabled = Convert.ToBoolean(WebConfigurationManager.AppSettings[SitesEnabled="2"]); //false
Please let me know ...Thanks
I would do it something like this:
using System.Linq;
var sitesEnabled =
ConfigurationManager.AppSettings["SitesEnabled"] != null
? ConfigurationManager.AppSettings["SitesEnabled"].Split(',')
: new string[0];
var oneSiteEnabled = sitesEnabled.Contains("1");
var twoSiteEnabled = sitesEnabled.Contains("2");

pass authentication header to image requests in iframe

I am sure that title of my question doesn't makes much sense, but i couldn't think better now.
Problem: My main task is to show all pages of a SSRS report in a popup inside one of the pages of a ASP.NET MVC application.
To achieve this I used below approach:
Add a jQuery popup in MyPage.cshtml(i need report contents inside this popup)
When this popup opens(on some client action), I make a jquery ajax request to second page proxyPage.aspx
On proxy page I make a webrequest to reportserver with network credentials and get report html
WebRequest request = WebRequest.Create( "http://MyReportServer/ReportServer?/ MyReportName&rs:Command=Render&rs:Format =HTML4.0&rc:Toolbar=false&Param1=blabla123");
request.Credentials = new NetworkCredential(MYUSERNAME, MYPASSWORD);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, System.Text.Encoding.UTF8);
string str = readStream.ReadToEnd();
Response.Write(str);
HTML from proxyPage I write in a div inside popup or using iframe to show full proxy page inside it.
Till here things go well and thereafter I get yet another problem for which I am writing this question
When report's HTML gets rendered in popup it makes request to report server to retrieve images embedded in report.
Since these requests to report server doesn't send network credentials as I did in step 3, I get prompt for entering credentials.
I need a approach through which these image request may somehow get authenticated by credentials that I have already supplied earlier.
SSRS will stream the resources in a location you specify.
private byte[] InternalRenderReport(Report report, List<ReportParameter> parameters, string format, int pageNumber, ref int totalPages, string virtualTempFolder, string physicalTempFolder)
{
CheckConnection();
byte[] result = null;
ReportExecution2005.ReportExecutionService _execService=new ReportExecution2005.ReportExecutionService();
sys = _systemService.GetCurrentSystem();
_execService.Url = sys.ReportingServices.ServiceRootURL+"/ReportExecution2005.asmx";
NetworkCredential credentials = new NetworkCredential(sys.ReportingServices.Credentials.UserName,
sys.ReportingServices.Credentials.Password,
sys.ReportingServices.Credentials.Domain);
_execService.Credentials=credentials;
ReportExecution2005.ParameterValue[] rsParams = null;
if (parameters != null)
{
rsParams = new ReportExecution2005.ParameterValue[parameters.Count];
int x = 0;
foreach (ReportParameter p in parameters)
{
rsParams[x] = new ReportExecution2005.ParameterValue();
rsParams[x].Name = p.ParameterName;
rsParams[x].Value = p.SelectedValue;
x++;
}
}
StringBuilder devInfo = new StringBuilder();
if (format.ToUpper().StartsWith("HTML"))
{
devInfo.Append("<DeviceInfo>");
devInfo.Append("<HTMLFragment>True</HTMLFragment>");
devInfo.Append("<Section>" + pageNumber.ToString() +"</Section>");
devInfo.Append("<StreamRoot>" + virtualTempFolder + "</StreamRoot>");
/*devInfo.Append("<Zoom>200</Zoom>");*/
devInfo.Append("</DeviceInfo>");
}
else
devInfo.Append("<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>");
string extension;
string mimeType;
string encoding;
string[] streamIDs = null;
ReportExecution2005.Warning[] warnings = null;
ReportExecution2005.ExecutionHeader execHeader = new ReportExecution2005.ExecutionHeader();
ReportExecution2005.ExecutionInfo rpt = _execService.LoadReport(report.ReportPath, null);
if(rsParams!=null)
_execService.SetExecutionParameters(rsParams, "en-us");
_execService.ExecutionHeaderValue = execHeader;
_execService.ExecutionHeaderValue.ExecutionID = rpt.ExecutionID;
//result = _execService.Render2(format, devInfo, ReportExecution2005.PageCountMode.Actual, out extension, out mimeType, out encoding, out warnings, streamIDs);
result = _execService.Render(format, devInfo.ToString(), out extension, out mimeType, out encoding, out warnings, out streamIDs);
if (format.ToUpper().StartsWith("HTML"))
{
// For each image stream returned by the call to render,
// render the stream and save it to the application root
string FilePath = physicalTempFolder;
byte[] image;
// For each image stream returned by the call to render,
// render the stream and save it to the application root
foreach (string streamID in streamIDs)
{
image = _execService.RenderStream("HTML4.0", streamID, null, out encoding, out mimeType);
FileStream stream = File.OpenWrite(FilePath + streamID);
stream.Write(image, 0, image.Length);
stream.Close();
}
}
rpt = _execService.GetExecutionInfo();
totalPages = rpt.NumPages;
return result;
}
This will return either raw HTML or the contents to push a file. I added a Temp folder to the solution to be deployed to the server. you can place a web.config file in the temp folder with the following contents to allow extension less contents as ssrs will render when using streams:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".*" mimeType="image/png" />
</staticContent>
<handlers>
<clear />
<add name="StaticFile" path="*" verb="*" type="" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" scriptProcessor="" resourceType="Either" requireAccess="Read" allowPathInfo="false" preCondition="" responseBufferLimit="4194304" />
</handlers>
</system.webServer>
</configuration>
Then use the following to functions to get the physical and Virtual Temp folders:
PhyscicalTempFolder= AppDomain.CurrentDomain.BaseDirectory + #"Temp\";
VirtualTempFolder=return Url.Content("~/Temp/");
And finally to clean up after each day you can add a powershell command similar to:
Remove-Item D:\xxx\WebApplications\ExternalReports\Temp\* -exclude *.config
Then add a .bat that calls the PS script:
powershell -command "& 'C:\xxx\Scripts\SSRSCleanTempFiles\SSRSCleanTempFiles.ps1'"
With this you can configure a scheduled task on the server to call the .bat file everyday and clean the temp folder of your application.

ASP.net why is application settings string only?

<appSettings>
<!-- Settings file for website! -->
<add key="DefaultCookieExpiryMins" value="30" />
</appSettings>
Why do I have to set everything as a string? Why can't I have different datatypes in there like int to help me stop having to cast everything?
This is why I prefer custom configuration sections over just putting key/value pairs in appSettings. See this MSDN article for more information on how to make your own configuration section. Once you have your own configuration section class, you should be able to access your settings as any data type you'd like.
I just use generics for things like this.
public static T GetConfigurationValue<T>(string keyName)
{
if (System.Configuration.ConfigurationManager.AppSettings[keyName] != null)
{
T result;
try
{
result = (T)Convert.ChangeType(System.Configuration.ConfigurationManager.AppSettings[keyName], typeof(T));
}
catch
{
return default(T);
}
return result;
}
throw new ArgumentException("A key with the name " + keyName + " does not exist in the current configuration.", keyName);
}
Usage: GetConfigurationValue<int>("DefaultCookieExpiryMins");

Maintain ASP.Net membership passwords during machine key change

Is there an utility or code sample that can decrypt with the old key, and then encrypt passwords with a new key for ASP.Net membership users?
None of the workarounds mentioned worked for me.
My solution is below. It involves first storing passwords in clear text and then reencrypting them again with new MachineKey.
Machine Key Change
This is my best guess at a solution, but I haven't had a chance to test it. It relies on the following settings for your current provider:
enablePasswordRetrieval="true" requiresQuestionAndAnswer="false" passwordFormat="Encrypted"
It also assumes that the new machinekey is already in the config file.
Create the following class (thanks to mootinator for the jumpstart on this)
using System.Reflection;
using System.Web.Configuration;
using System.Web.Security;
namespace MyNamespace
{
public class MySqlMembershipProvider : SqlMembershipProvider
{
protected override byte[] DecryptPassword(byte[] encodedPassword)
{
MachineKeySection section = (MachineKeySection)WebConfigurationManager.GetSection("system.web/machineKey");
section.DecryptionKey = "oldkey"; // TODO: Set your old key here
MethodInfo method = typeof(MachineKeySection).GetMethod("EncryptOrDecryptData", BindingFlags.Instance | BindingFlags.NonPublic);
return (byte[])method.Invoke(section, new object[] { encodedPassword, null, 0, encodedPassword.Length, 0, false, false });
}
}
}
In your web.config:
<membership defaultProvider="DefaultSqlMembershipProvider">
<providers>
<clear/>
<add name="DefaultSqlMembershipProvider" connectionStringName="MembershipConnectionString" enablePasswordRetrieval="true" requiresQuestionAndAnswer="false" applicationName="TODO" passwordFormat="Encrypted" type="System.Web.Security.SqlMembershipProvider"/>
<add name="MySqlMembershipProvider" connectionStringName="MembershipConnectionString" enablePasswordRetrieval="true" requiresQuestionAndAnswer="false" applicationName="TODO" passwordFormat="Encrypted" type="MyNamespace.MySqlMembershipProvider"/>
</providers>
</membership>
Change the passwords with the following code:
MembershipProvider retrievePasswordProvider = Membership.Providers["MySqlMembershipProvider"];
foreach (MembershipUser user in Membership.GetAllUsers())
{
MembershipUser retrievePassworedUser = retrievePasswordProvider.GetUser(user.UserName, false);
string password = retrievePassworedUser.GetPassword(); // get password using old key
user.ChangePassword(password, password); // change password to same password using new key
}
Let me know if that works for you.
I think you could do this by setting the key on the fly:
You might have to extend the SqlMembershipProvider (or whatever you use) to get access to the protected DecryptPassword method.
MachineKeySection section = (MachineKeySection)WebConfigurationManager.GetSection("system.web/machineKey");
section.DecryptionKey = "old";
// Read old password
section.DecryptionKey = "new";
// Store new password

Example of an asp.net application vulnerable to the Padding Oracle Attack?

Does anyone could put me a very basic example of an asp.net web application which is vulnerable to the padding oracle attack.
Try the steps at the following two sites to test your site.
http://blog.dotsmart.net/2010/09/22/asp-net-padding-oracle-detector/
http://www.troyhunt.com/2010/09/fear-uncertainty-and-and-padding-oracle.html
Hope that helps
I know it's a very late answer, but maybe someone will be looking for this info.
Old versions of ASP.NET were vulnerable to the Padding Oracle Attack. It is still possible to enforce the "old" behavior through some tweaks. I described them in detail on my blog and the sample code is on GitHub.
We will be attacking the VIEWSTATE field. First, you need to disable ViewState signing. To do that, make sure you have the following setting in the web.config file:
<appSettings>
<add key="aspnet:UseLegacyMachineKeyEncryption" value="true" />
</appSettings>
And a sample .ashx file vulnerable to the Padding Oracle Attack:
<%# WebHandler Language="C#" Class="EncryptionHandler" %>
using System;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Security;
using System.Text;
public class EncryptionHandler : IHttpHandler
{
static readonly byte[] secret = Encoding.UTF8.GetBytes("Some text to break.");
public void ProcessRequest(HttpContext context)
{
var viewState = context.Request.Form["VIEWSTATE"];
if (viewState == null) {
viewState = MachineKey.Encode(secret, MachineKeyProtection.Encryption);
context.Response.ContentType = "text/html";
context.Response.Write("<!doctype html><html><form action=\"/EncryptionHandler.ashx\" method=\"POST\">" +
"<input type=\"hidden\" name=\"VIEWSTATE\" value=\"" + viewState + "\" />" +
"<input type=\"submit\" value=\"Test\" /></form></html>");
return;
}
var v = MachineKey.Decode(viewState, MachineKeyProtection.Encryption);
context.Response.ContentType = "text/plain";
if (v.SequenceEqual(secret)) {
context.Response.Write("I know the secret");
} else {
context.Response.Write("Something is wrong with my secret.");
}
}
public bool IsReusable {
get {
return false;
}
}
}
Now, based on the HTTP code (HTTP 500 when the cipher is invalid) you may try attacking the site (as described here).

Resources