Authenticate with ASP.NET membership provider using Qt and C++

5. July 2012 00:42 by Cameron in   //  Tags: , , , , , ,   //   Comments

Today, I was looking at a method of authenticating users in the IGA desktop application using the website's membership provider. I remembered a post I made a while back on Stack Overflow about how to authenticate users with Ignite OpenFire and in the answer, there was some Java code showing how to correctly hash a password with a salt and compare it with the membership table. I ventured into porting the algorithm to C++ and was successful! Here's the C++ code for anyone interested:

QString hashPassword(QString password, QString salt)
{

    // get utf-16 representation of password

    QByteArray passwordBytes((const char*) (password.utf16()), password.size() * 2);

    // get utf-16 representation of salt

    QByteArray saltBytes((const char*) (salt.utf16()), salt.size() * 2);

    // decode base64 salt byte array

    QByteArray saltDecodedBytes = QByteArray::fromBase64(saltBytes);

    QByteArray dst;

    dst.append(saltDecodedBytes);

    dst.append(passwordBytes);



    QByteArray hashed = QCryptographicHash::hash(dst, QCryptographicHash::Sha1);

    return QString::fromLatin1(hashed.toBase64().data());
}

Please note that in order for this to work, your passwords must be hashed. I had to convert any non-hashed passwords using an automated script that I found on Stack Overflow. It's pretty straight forward. Just add a second provider that uses hashed passwords and get the passwords from the encrypted/clear text provider and change them using the hashed provider. Be sure to set your default provider to the hashed provider so that you won't have to run this script at a later point. I had to modify the code that I found from Stack Overflow a bit to use a try catch block around the user.GetPassword() method as some passwords may already have been hashed and can't be decrypted. Here's the C# code for anyone interested:

void HashAllPasswords()
{          
	SqlMembershipProvider hashedProvider = (SqlMembershipProvider)Membership.Providers["HashedProvider"];
	SqlMembershipProvider encryptedProvider = (SqlMembershipProvider)Membership.Providers["EncryptedProvider"];
	if (encryptedProvider == null || hashedProvider == null) return;
	Dictionary<string, string> passwords = new Dictionary<string, string>();
	int unimportant;
	foreach (MembershipUser user in encryptedProvider.GetAllUsers(0, Int32.MaxValue, out unimportant))
	{
		try
		{
			passwords.Add(user.UserName, user.GetPassword());
		}
		catch (Exception e)
		{
		}
	}

	using (var conn = new SqlConnection(
		   ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
	{
		conn.Open();
		using (var cmd = new SqlCommand(
			   "UPDATE [aspnet_Membership] SET [PasswordFormat]=1", conn))
			cmd.ExecuteNonQuery();
	}

	foreach (var entry in passwords)
	{
		var resetPassword = hashedProvider.ResetPassword(entry.Key, null);
		hashedProvider.ChangePassword(entry.Key, resetPassword, entry.Value);
	}
}

Improving speed of iterating a result set from a QSqlQuery

4. July 2012 16:22 by Cameron in C++, Qt  //  Tags: , , , , , , , ,   //   Comments

Today, I had to figure out why it was taking so long to iterate a result set using a QSqlQuery. A query that I ran in about 1 second from SQL Server Management Studio was taking 15 seconds to complete from my C++ application. I found that if you don't set ForwardOnly to true using setForwardOnly(true), it will drastically decrease performance of iterating through the query's results. You should also prepare the query before executing. After applying those changes, I got my query to run in under a second! Huge difference! You can see an example of how to connect to a SQL Server database and query a table using the QODBC3 driver. The speed improvements should also apply to SQLite databases.

QString connectionTemplate = "DRIVER={SQL SERVER};SERVER=server;DATABASE=Users;";
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC3", "db");
db.setDatabaseName(connectionTemplate);
db.setUserName("sa");
db.setPassword("password");
if (db.open()) {
	qDebug() << "OK!";
	QSqlQuery * query = new QSqlQuery(db);
	query->setForwardOnly(true);
	query->prepare("SELECT [UserName]"
				   "  FROM [dbo].[Users]");
	QTime begin = QTime::currentTime();
	if(query->exec())      {
		while(query->next())
		{
			QString userName = query->value(0).toString();
			qDebug() << userName;
		}
		QTime end = QTime::currentTime();
		qDebug() << "finished in " + QVariant(end.second() - now.second()).toString() + " seconds";
	}
	db.close();
}
else {
	qDebug() << db.lastError().text();
}

Month List

Tag cloud