You have probably noticed by now that you did not build an authentication mechanism for the sales force application. Users need to be authenticated before they access their hand-held mobile devices for of the following reasons:
To implement authentication, you must first create the login screen. Create the following form and lay out the controls appropriately on the form.
The login screen works in the following way:
One-way encryption, as opposed to two-way encryption, produces a value that cannot be decrypted (in the other direction) to produce the original value. It is hence useful for password verification. This is done in the following steps:
To produce a one-way encrypted value or a hash value from a string of bytes, you can use SHA256, which is a commonly known hashing algorithm.
SHA, short for Secure Hashing Algorithm is a set of algorithms developed by the National Security Agency (NSA) to produce a hash output (or "fingerprint") from an input message of any size. SHA256 is a variant of SHA that outputs a 256-bit sized hash.
There are other algorithms too. A few of them are listed as follows, together with their differences.
Algorithm name |
Details |
---|---|
|
Low security, fast speed |
|
Moderate security, medium speed |
|
High security, low speed |
|
High security, low speed |
|
Very high security, low speed |
|
Moderate security, medium speed. Take note that MD5 is already obsolete, and should no longer be used. |
As we will focus on SHA256, let's see how you can write a function to encrypt a string in SHA256. Before you can use SHA256, you must first import the following namespaces:
using System.Security.Cryptography;
using System.Text;
public string SHA256Encrypt(string EncString)
{
string SHAString = null;
byte[] EncStringBytes = null;
UTF8Encoding Encoder = new UTF8Encoding();
EncStringBytes = Encoder.GetBytes(EncString);
//Create the SHA256 hash
SHA1CryptoServiceProvider SHAHasher = new SHA1CryptoServiceProvider();
EncStringBytes = SHAHasher.ComputeHash(EncStringBytes);
SHAString = BitConverter.ToString(EncStringBytes);
SHAString = SHAString.Replace("-", "");
return SHAString;
}
Create an empty form, place a button on it, and in the click event of this button write the following code:
MessageBox.Show(SHA256Encrypt("HELLO TEST"));
When you launch this form and click the button, you will see that the message has been encrypted using SHA256. Take note that whatever the length of the original message, it will always produce a hash of the same length. The great thing about SHA256 encryption is that small changes in the original text will translate to large changes in the hash value. This makes it especially sensitive to any data change.
You will need to store the one-way encrypted password for the master account so that it can be used for future password verification. Although the password is in encrypted form, it is not a good idea to leave it around in a file on the mobile device.
The reason for this is that someone could potentially acquire this hash value, and then write a separate program to run a brute-force comparison by running words in a dictionary through the same SHA256 encryption and then comparing it with the hash value.
It is a good idea to store this password in the database. Your database has the added encryption functionality provided by both Oracle Lite and SQL Server C—this will make it a bit more difficult for unauthorized persons to retrieve the hash values.
Let's first create a new table in the database. You can do this via the Oracle mSQL tool or the QueryAnalyzer 3.5 tool.
CREATE TABLE UserLogons ( Username NVARCHAR(20), Password NVARCHAR(50) )
We will just use this table to store our master account details. It will be expected to store only one single record at any point in time. You will now need to add a few more functions (for the login process) to the IDataLibPlugin
interface.
public interface IDataLibPlugin
{
.
.
.
//This function will attempt to login the user by comparing
//his or her password against the hashed value in the
//database
bool Login(string Username, string Password);
//This method will check if this is the first login attempt
//after the application installation
bool IsFirstTimeLogin();
//This method will create the master login account and save
//its details to the UserLogons table
bool CreateLogin(string Username, string Password);
}
The next step would be to implement these functions, in both the Oracle Lite and SQL Server CE plugins. Let's take a look at the code for the SQL Server CE plugin (I'll leave you to do the same for the Oracle Lite plugin):
public bool Login(string Username, string Password) { SqlCeCommand _command; int _recordCount; _command = _globalConnection.CreateCommand(); //Do a compare against the hashed password stored in the //database. _command.CommandText = "SELECT COUNT(*) AS RecordCount FROM UserLogons WHERE Username=@Username and Password=@Password"; _command.Parameters.Add("@Username", Username); _command.Parameters.Add("@Password", Password); try { _recordCount = (int) (_command.ExecuteScalar()); } catch (Exception ex) { _recordCount = 0; throw (ex); } _command.Dispose(); _command = null; if (_recordCount == 0) { return false; } else { return true; } } public bool IsFirstTimeLogin() { SqlCeCommand _command; int _recordCount; _command = _globalConnection.CreateCommand(); //Checks if there is already a record in UserLogons table. _command.CommandText = "SELECT COUNT(*) AS RecordCount FROM UserLogons"; try { _recordCount = (int) (_command.ExecuteScalar()); } catch (Exception ex) { _recordCount = 0; throw (ex); } _command.Dispose(); _command = null; if (_recordCount == 0) { return true; } else { return false; } } public bool CreateLogin(string Username, string Password) { bool returnValue; SqlCeCommand _command; int _recordCount; _command = _globalConnection.CreateCommand(); //The password is encrypted outside and passed in to this //function _command.CommandText = "INSERT INTO UserLogons (Username, Password) VALUES (@Username, @Password)"; _command.Parameters.Add("@Username", Username); _command.Parameters.Add("@Password", Password); try { _recordCount = _command.ExecuteNonQuery(); if (_recordCount > 0) { returnValue = true; } else { returnValue = false; } } catch (Exception ex) { returnValue = false; throw (ex); } _command.Dispose(); _command = null; return returnValue; }
Now write the code for the Login
form. The highlighted code shows the code for the main logic of the login form.
public partial class Login { public Login() { InitializeComponent(); } private bool _firstTimeLogin = false; public void mnuExit_Click(System.Object sender, System.EventArgs e) { System.Windows.Forms.Application.Exit(); } public void mnuLogin_Click(System.Object sender, System.EventArgs e) { if (txtUsername.Text.Length == 0 || txtPassword.Text.Length == 0) { MessageBox.Show ("Please ensure you have specified both the login username and password"); return; } //Encrypt the password string _encryptedPassword; _encryptedPassword = SHA256Encrypt(txtPassword.Text); if (_firstTimeLogin == true) { GlobalArea.Application.CreateLogin(txtUsername.Text, _encryptedPassword); this.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.Close(); NavigationService.ShowDialog("MainMenu", null); } else { if (GlobalArea.Application.Login(txtUsername.Text, _encryptedPassword) == false) { txtUsername.Text = ""; txtPassword.Text = ""; MessageBox.Show("The username and password combination is not correct"); } else { this.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.Close(); NavigationService.ShowDialog("MainMenu", null); } } } public string SHA256Encrypt(string EncString) { string SHAString = null; byte[] EncStringBytes = null; UTF8Encoding Encoder = new UTF8Encoding(); EncStringBytes = Encoder.GetBytes(EncString); SHA1CryptoServiceProvider SHAHasher = new SHA1CryptoServiceProvider(); EncStringBytes = SHAHasher.ComputeHash(EncStringBytes); SHAString = BitConverter.ToString(EncStringBytes); SHAString = SHAString.Replace("-", ""); return SHAString; } public void Login_Load(object sender, System.EventArgs e) { //Only display the first-time login message if it is a //first time login if (GlobalArea.Application.IsFirstTimeLogin() == true) { _firstTimeLogin = true; } else { _firstTimeLogin = false; } lblFirstTimeMsg.Visible = _firstTimeLogin; } }
Instead of going straight to the menu, you must now load the login form as the first form of your application. You can easily do that by first adding a new navigation target in the SalesForceApp.NavigationService
class (highlighted as follows).
public static DialogResult ShowDialog(string DialogName, object Arg1)
{
DialogResult _dialogResult = 0;
switch (DialogName)
{
case "SetupDatasources":
PluginsSetup _PluginsSetup = new PluginsSetup();
_dialogResult = _PluginsSetup.ShowDialog();
_PluginsSetup.Close();
_PluginsSetup.Dispose();
_PluginsSetup = null;
break;
case "Login":
Login _login = new Login();
_dialogResult = _login.ShowDialog();
_login.Close();
_login.Dispose();
_login = null;
break;
case "MainMenu":
MainMenu _MainMenu = new MainMenu();
_dialogResult = _MainMenu.ShowDialog();
_MainMenu.Close();
_MainMenu.Dispose();
_MainMenu = null;
break;
.
.
.
}
}
As the last step, you will now need to modify the Main()
method in the SalesForceApp.Application
class. This will enable the application to launch the Login
form first whenever you start the application.
public class Application { public static void Main() { InitializeApp(); NavigationService.ShowDialog("Login", null); DeInitializeApp(); } . . . }
That's all there is to it! Now you can try your Login
form by running the SalesForceApp
project. The first time, it will prompt you for your password. You can see the record for the master account generated in the UserLogons
table if you use the Query Analyzer 3.5 tool to run a query off the SQL Server CE database. On all subsequent runs, you will be required to key in that same username and password to log on.
3.144.238.20