(NET) NET (2014)

Protect program by password.

To prevent program from unauthorized execution need to avoid store password in clear string in design time (code decompilation by ILSpy), and avoid to store clear password text in runtime (prevent from show memory debugger) there are two different choice.

First choice is avoid store password at all, create only MS5 hash, store it in Registry and compare stored hash with hash of entering text. Maybe this is a best way.

Decrypt password and hide it to secure string

But, if you already have crypto-service in your program (for example Encrypt sensitive data in DB by Rijndael symmetric algorithm) there are another way. You can store in Registry encrypted password, this is not worse then hash, than decrypt it, hide clear password to unmanaged memory and compare entering text with hided password.

Below you can see my code template of this solution. In line 1-19 I define constants and read Keys for decryption from Registry.

   1:  Imports System.Configuration
   2:  Imports System.Runtime.InteropServices
   3:  Imports System.Security
   5:  Public Class LoginForm
   8:      Public S As SecureString = New SecureString()
   9:      Public Shared Symm_Key As Byte() '= {&H30, &H7, &HFB, &HF, &H97, &H21, &HE7, &HB3, &HE9, &HE6, &H18, &HC6, &H84, &HF8, &H68, &H50, &H9C, &H5, &H1C, &H24, &HC6, &HDC, &H8, &H57, &HF, &HFD, &H96, &HC7, &HF3, &H2A, &H2B, &H15}
  10:      Public Shared Symm_IV As Byte() '= {&H68, &HCE, &HE6, &HB9, &H2, &H86, &HF, &H39, &HA1, &H8D, &HAB, &H1E, &H45, &H3A, &H95, &HC0}
  12:      Public Shared WithEvents MyRegistry As Registry
  15:      Private Sub LoginForm_Load(sender As Object, e As EventArgs) Handles Me.Load
  16:          MyRegistry = New Registry("MyProtectedProgram")
  17:          AddHandler MyRegistry.FirstStart, AddressOf FirstStart
  18:          Symm_Key = MyRegistry.GetValue(Of Byte())("Symm_Key")
  19:          Symm_IV = MyRegistry.GetValue(Of Byte())("Symm_IV")

Next portion of code is handle keyboard key Enter and compare it with secret hide password.

  65:      Private Sub PassTextBox_KeyDown(sender As Object, e As KeyEventArgs) Handles PassTextBox.KeyDown
  66:          If (e.KeyCode = Keys.Enter) Then
  67:              If PassTextBox.Text = SecureStringToString(S) Then
  68:                  Me.Visible = False
  69:                  StartFormInstance.Show()
  70:              Else
  71:                  Me.Close()
  72:              End If
  73:          End If
  74:      End Sub
  76:      Private Function SecureStringToString(ByVal value As SecureString) As String
  77:          Dim valuePtr As IntPtr = IntPtr.Zero
  79:          Try
  80:              valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value)
  81:              Return Marshal.PtrToStringUni(valuePtr)
  82:          Finally
  83:              Marshal.ZeroFreeGlobalAllocUnicode(valuePtr)
  84:          End Try
  85:      End Function

But how password in clear form has been loaded to unmanaged memory? Answer to this question is locate in string 21-37.

  21:          Dim CryptoStr1 As String = MyRegistry.GetValue(Of String)("LogonPass")
  22:          If CryptoStr1 Is Nothing Or Symm_Key Is Nothing Or Symm_IV Is Nothing Then
  23:              MsgBox("Registry key missing.")
  24:          Else
  25:              Dim Crypter As New Symmertric
  26:              Crypter.IV = Symm_IV
  27:              Crypter.Key = Symm_Key
  28:              If Crypter.DeCryptString(CryptoStr1) = "@?@" Then
  29:                  MsgBox("CryptoKey was changed, set it again." & vbCrLf & "Site password also has been lost.")
  30:                  Dim S As New Setting
  31:                  S.FirstStart = True
  32:                  S.ShowDialog()
  33:              Else
  34:                  For Each One As Char In Crypter.DeCryptString(CryptoStr1)
  35:                      S.AppendChar(One)
  36:                  Next
  37:              End If

This is not a best way of protection because you can set brake in line 34 and type in debugger ? Crypter.DeCryptString(CryptoStr1). Therefore best way to protection is store only MD5 hash to registry.

And next interesting moment in this code is event FirstStart. If this is first start of program, will be open a window to set password.

  56:      Sub FirstStart()
  60:          Dim S As New Setting
  61:          S.FirstStart = True
  62:          S.ShowDialog()
  63:      End Sub

   6:  Public Class Setting
  12:      Private Sub Setting_Load(sender As Object, e As EventArgs) Handles Me.Load
  20:                  Crypter.CreateKey()
  21:                  LoginForm.Symm_Key = Crypter.Key
  22:                  LoginForm.Symm_IV = Crypter.IV
  40:      End Sub
  55:      Dim IsPasswordSetting = False
  56:      Private Sub SetNewPasswordButton_Click(sender As Object, e As EventArgs) Handles SetNewPasswordButton.Click
  57:          If PasswordTextBox.Text <> "" Then
  58:              Dim Crypter As New Symmertric
  59:              Crypter.Key = LoginForm.MyRegistry.GetValue(Of Byte)("Symm_Key")
  60:              Crypter.IV = LoginForm.MyRegistry.GetValue(Of Byte)("Symm_IV")
  61:              Dim LogonPass As String = Crypter.EnCryptString(PasswordTextBox.Text)
  62:              LoginForm.MyRegistry.SetValue("LogonPass", LogonPass)
  63:              IsPasswordSetting = True
  64:          End If
  65:          TabControl1.SelectedTab = DatabaseTab
  66:      End Sub

Store only MD5 hash

Above version of code is not full secure and I have replace it to new version with MD5.

Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19
Link to this page: http://www.vb-net.com/SecureString/index.htm