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
4:
5: Public Class LoginForm
6:
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
14:
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
75:
76: Private Function SecureStringToString(ByVal value As SecureString) As String
77: Dim valuePtr As IntPtr = IntPtr.Zero
78:
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.
|