Maintain ASP.Net membership passwords during machine key change - asp.net

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

Related

Has anyone used Sage Pay with asp.net Webpages

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.

How to read App Setting key in Web config Connection string?

It is possible to use app keys for all the connection string inputs and read those on connection string like bellow
<add name="DefaultConnection" connectionString="Data Source=$(Server);Initial Catalog=$(Catalog);User ID=$(User);Password=$(Password)" providerName="System.Data.SqlClient" />
<add key="$(Server)" value="xxxx" />
<add key="$(Catalog)" value="xxxx" />
<add key="$(User)" value="xxxx" />
<add key="$(Password)" value="xxxx" />
As #Ertürk Öztürk already say - it's not possible.
If you searching for more or less clean way to do it i suggest you to use SqlConnectionStringBuilder or DbConnectionStringBuilder if you using not MSSQL data base.
In your code it will be like this with SqlConnectionStringBuilder:
//create connection string builder
System.Data.SqlClient.SqlConnectionStringBuilder connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
//set all properties from your WebConfig
connectionStringBuilder.DataSource = ConfigurationManager.AppSettings["Server"];
connectionStringBuilder.InitialCatalog = ConfigurationManager.AppSettings["Catalog"];
connectionStringBuilder.UserID = ConfigurationManager.AppSettings["User"];
connectionStringBuilder.Password = ConfigurationManager.AppSettings["Password"];
//not you can get rigth formatted connection string
var connectionString = connectionStringBuilder.ConnectionString;
It's not possible. Actually you don't need to do this, that's why it's not possible. Because you can change the other parts of web.config same like AppSettings.
ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString =
String.Format("Data Source={0};Initial Catalog={1};UserID={2};Password={3}",
"server", "db", "ID", "Pass");

SqlException when creating a new item in Asp.Net MVC 3 app

In my "web store" mvc app I want to add items to database. Items table has CreatedBy field and it is a foreign key from User table UserId field. Everything was working before I put the database into the App_Data folder. Now I get the SqlException when trying to create a new Item:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Item_contains_User". The conflict occurred in database "C:\USERS\ALEKSEY\REPOS\2\WEBSTORE\WEBSTORE\APP_DATA\WEBSTORE.MDF", table "dbo.Users", column 'UserId'.
Here is the Create method of ItemRepository class:
public Item CreateItem(int productId, Guid userId)
{
var item = new Item
{
ProductId = productId,
CreatedBy = userId,
};
_dataContext.Items.InsertOnSubmit(item);
_dataContext.SubmitChanges(); // in this line the exception occures !
return item;
}
Here is the controller method Create:
[HttpGet]
public ViewResult Create()
{
var p = _productRepository.CreateProduct("", "", 0, "", "", "");
var userId = (Guid)Membership.GetUser().ProviderUserKey;
var item = _itemsRepository.CreateItem(p.ProductId, userId);
// some code
return View(model);
}
Besides, I use Linq to Sql model drag an' drop approach.
Here is the changed web.config connection string part:
<connectionStrings>
<add name="WebStoreConnectionString" connectionString="Data Source=(LocalDB)\v11.0;
AttachDbFilename=|DataDirectory|\WebStore.mdf;Integrated Security=True;Connect Timeout=30"
providerName="System.Data.SqlClient" />
<add name="DefaultConnection" connectionString="Data Source=|DataDirectory|\aspnet.sdf"
providerName="System.Data.SqlServerCe.4.0" />
As I said everything was working before I moved the database to App_Data file. I also tried to remove the dependency between Items and Users tables - the exact same exception.
Any help would be appropriate. Thanks in advance!
Edits:
Ok, now I really broke the dependency between Items and Users tables and no exception occures. But! I have to somehow know who has created each product, so breaking the dependency is not an option. I also tried to remove all code that initializes the CreatedBy field.
Any ideas??
Edits (part 2):
The second comment below gives a great advise! I found that all users that are created are stored now in the aspnet.sdf database!!!
But if I remove the connection string "DeafaultConnection":
<add name="DefaultConnection" connectionString="Data Source=|DataDirectory|\aspnet.sdf"
providerName="System.Data.SqlServerCe.4.0" />
I will get ConfigurationErrorsException:
"The connection name 'DefaultConnection' was not found in the applications
configuration or the connection string is empty."
in the folowing line:
var userId = (Guid)Membership.GetUser().ProviderUserKey;
Ok, as I guessed the issue was in the configuration. Each provider (for some reason) in the connection string had "DefaultConnection". I changed it to "WebStoreConnectionString". And now everything works!
p.s. thanks #w0lf, he pushed the thoughts in the right direction)

Custom MembershipProvider in .NET 4.0

There are a few threads here at so about this matter but most of them are outdated and the reference links in them are even more outdated.
I got this website which I need to connect to an external sql server (mssql) with it's own table structure, using the default asp.net membership provider structure is not an option. The table layout is really simple and the usertable looks like this (it's called Individuals)
Individuals
- UserGuid (uniqueidentifier/guid, unique)
- Name (varchar)
- Password (varchar)
- HasAccess (tinyint/ 1 or 0)
- DateTime (datetime)
- Log (xml)
The required functionality is simply to log someone in, the rest is not necessary :)
I followed some guides but most of them are outdated and very complex. Unfortunately the msdn examples follows this pattern and the documentation is not very good.
So if anyone got some resources showing how to, or are willing to post codesamples or similar here I'd appreciate it.
Thanks!
It's very simple really:
Create a new Class file (if you're not using a multi-layered system, in your project's Models folder) let's called MyMembershipProvider.cs
Inherit that class from System.Web.Security.MembershipProvider
automatically create the needed methods (period + space in the inherit class)
Done!
All methods will have the NotImplementedException exception, all you need to do is edit each one and put your own code. For example, I define the GetUser as shown below:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
return db.GetUser(username);
}
dbis my Database Repository that I added into the class as
MyServicesRepository db = new MyServicesRepository();
there, you will find the GetUser method as:
public MembershipUser GetUser(string username)
{
OS_Users user = this.FindUserByUsername(username);
if (user == null)
return
new MembershipUser(
providerName: "MyMembershipProvider",
name: "",
providerUserKey: null,
email: "",
passwordQuestion: "",
comment: "",
isApproved: false,
isLockedOut: true,
creationDate: DateTime.UtcNow,
lastLoginDate: DateTime.UtcNow,
lastActivityDate: DateTime.UtcNow,
lastPasswordChangedDate: DateTime.UtcNow,
lastLockoutDate: DateTime.UtcNow);
return
new MembershipUser(
providerName: "MyMembershipProvider",
name: user.username,
providerUserKey: null,
email: user.email,
passwordQuestion: "",
comment: "ANYTHING you would like to pass",
isApproved: true,
isLockedOut: user.lockout,
creationDate: user.create_date,
lastLoginDate: user.lastLoginDate,
lastActivityDate: user.lastActivityDate,
lastPasswordChangedDate: user.lastPasswordChangedDate,
lastLockoutDate: user.lastLockoutDate);
}
Do this for all the methods you use (debug the project and see which ones you need) - I only use some, not all as I don't really care about methods like ChangePasswordQuestionAndAnswer, DeleteUser, etc
just make sure that in your web.config you add the new Membership as:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear/>
<add name="MyMembershipProvider" type="Your.NameSpace.MyMembershipProvider" connectionStringName="OnlineServicesEntities"
enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
You have a nice Video Tutorial from Chris Pels (dated 2007 but still mostly valid) and code for this as well, though Video Tutorial is in VB, but let's you understand the steps...
http://www.asp.net/general/videos/how-do-i-create-a-custom-membership-provider
I did not only create my own Membership Provider but I created my Roles Provider as well, witch as you can see from above code, is as simple as the MemberShip and let's you, in your application use things like:
[Authorize(Roles = "Partner, Admin")]
public ActionResult MyAction()
{
}
and
#if (Roles.IsUserInRole(Context.User.Identity.Name, "Admin"))
{
<div>You're an ADMIN, Congrats!</div>
}
What is automagically create the needed methods (period + space in the inherit class)
You can either right-click, or have the cursor on the name and press Control + . and then space.
There are a few threads here at so
about this matter but most of them are
outdated and the reference links in
them are even more outdated.
Since the introduction of ASP.NET in Framework 1.0 the Page.User/CurrentSession.User/IPrincipal/IIdentity model is unchanged. In Framework 2.0 the Membership provider was added. Those "outdated" reference remain valid guidance. MSDN

Create Password and Passwordsalt

I have an existing table that has 100 users and passwords. The data type is a varchar.
I just created an asp.net mvc application and I want to convert the password to aspnet_membership table.
How do I convert varchar password on SQL level as "Password" and "Passwordsalt" in aspnet_membership table?
Password & PasswordSalt part are not processed and created at "SQL Level"
If you look closely to the asp.net membership database - tables / stored procedures / other objects. Then you will find that there are two stored procedures (sp for short) to create User in asp.net membership database tables.
aspnet_Membership_CreateUser
aspnet_Users_CreateUser
These sps will create user entry in aspnet_Membership & aspnet_Users table respectively.
ASP.Net membership works on the web.config file settings that you setup.
An example default webconfig entry will something like this:
<authentication mode="Forms"> // If you are using Form authentication
<forms loginUrl="~/Account/Login.aspx" timeout="2880" />
</authentication>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
enablePasswordRetrieval="false" passwordFormat="Encrypted" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
In this settings section the attribute "passwordFormat" sets the way your user password is stored.
Options are - Clear (0), Hashed (1), Encrypted (2)
By default it will be having hashed value - or if u have not specified passwordFormat.
In clear text the password will be saved as - Text clear - readable.
With the Hashed option the password will not be (Encrypted), only encoded using a Hashing alogorithm
With the Encrypted option the password will be Encrypted and then encoded.
Encrypted option u specifies a non-auto generated "machine key"
To get one see: Get a non-autogenerated machine key
Password salt is a randomly generated string which is used to Encrypt and encode the password along with the Validation & Decryption Key.
If you want to overide the encryption method of asp.net membership provider and encode youself, (if using custome membership provider), you can do something like this:
private string EncodePassword(byte passFormat, string passtext, string passwordSalt)
{
if(passFormat.Equals(0)) // passwordFormat="Clear" (0)
return passtext;
else{
byte[] bytePASS = Encoding.Unicode.GetBytes(passtext);
byte[] byteSALT = Convert.FromBase64String(passwordSalt);
byte[] byteRESULT = new byte[byteSALT.Length + bytePASS.Length + 1];
System.Buffer.BlockCopy(byteSALT, 0, byteRESULT, 0, byteSALT.Length);
System.Buffer.BlockCopy(bytePASS, 0, byteRESULT, byteSALT.Length, bytePASS.Length);
if(passFormat.Equals(1)) // passwordFormat="Hashed" (1)
{
HashAlgorithm ha = HashAlgorithm.Create(Membership.HashAlgorithmType);
return (Convert.ToBase64String(ha.ComputeHash(byteRESULT)));
}
else // passwordFormat="Encrypted" (2)
{
MyCustomMembership myObj = new MyCustomMembership();
return(Convert.ToBase64String(myObj.EncryptPassword(byteRESULT)));
}
}
}
Example usage:
string passSalt = // Either generate a random salt for that user, or retrieve the salt from database if the user is in edit and has a password salt
EncodePassword(/* 0 or 1 or 2 */, passwordText, passSalt);
I hope this helps.
Its not possible at a SQL level, but with some C# code there are 2 posible techniques.
Simplest is to write a process to read through your existing table, and call Membership.CreateUser for each of the users, and the membership provider will create the user records for you, including the password & salt.
Alternatively, create yourself a dummy user, then wrote a process to change the password of the dummy user to the value from your existing users, and read the value from the aspnet_membership table. I have code that does this if you're interested.
HashAlgorithm ha = HashAlgorithm.Create(Membership.HashAlgorithmType);
How to check the ha is null or not if null means and how to throw the exception

Resources