В последнее время вопрос о защите программ и данных стал очень актуальным как для программистов, так и для простых передовых пользователей, а случаи незаконного обращения к личной виртуальной информации человека не единичны. Конечно, существует множество разнообразных криптографических пакетов, но, как правило, они все направлены на защиту текстовой информации, когда как многие, порой даже профессиональные программные продукты, абсолютно лишены какой-либо защиты персональных настроек и данных пользователей, что зачастую для самого юзера создает лишнюю головную боль, а разработчика этого программного обеспечения приводит к потере драгоценных клиентов, а порой и к судебным баталиям.
Конечно, данные проблемы вызывают неприятные ощущения у обеих сторон конфликта. Но конфликт является борьбой двух противоположных убеждений и мнений. Значит, если программист решит написать качественный программный продукт, чтобы не создавать головных болей ни себе, ни своему пользователю, то, возможно, такие бы столкновения и не возникали.
Однако, после продолжительных поисков методов защиты информации на VB я не пришел к подходящему
примеру, полностью подходящему для моих условий. Мне самому требовалось защитить
записи ini-файлов моего компьютерного администратора, чтобы перед записью строки файл расшифровывался, добавлялась
или изменялась сама строка и файл кодировался. Все примеры, которые я находил, были довольно расплывчаты и
просты. Мне же требовалась довольно крепкая защита файлов с использованием прочного алгоритма и без создание temp-овых файлов.
Оставалось одно: приступить к написанию собственного метода защиты, чем я позже и занялся. В основе моего "криптопакета" лежали очень интересный пример из книги Microsoft Press "VB 5.0 Мастерская разработчика". Пример состоял из непосредственно класса защиты и функций шифрования-дешифрования.
Для начала рассмотрим основной класс защиты информации. Назовем его "CIPHER":
' Код класса CIPHER
Option Explicit
Private msKeyString As String
Private msText As String
'~~~.KeyString
Public Property Let KeyString(sKeyString As String)
msKeyString = sKeyString
Initialize
End Property
'~~~.Text
Public Property Let Text(sText As String)
msText = sText
End Property
Public Property Get Text() As String
Text = msText
End Property
'~~~.DoXor
Public Sub DoXor()
Dim intC As Integer
Dim intB As Integer
Dim lngI As Long
For lngI = 1 To Len(msText)
intC = Asc(Mid(msText, lngI, 1))
intB = Int(Rnd * 256)
Mid(msText, lngI, 1) = Chr(intC Xor intB)
Next lngI
End Sub
'~~~.Stretch
Public Sub Stretch()
Dim intC As Integer
Dim lngI As Long
Dim lngJ As Long
Dim intK As Integer
Dim lngA As Long
Dim sB As String
lngA = Len(msText)
sB = Space(lngA + (lngA + 2) \ 3)
For lngI = 1 To lngA
intC = Asc(Mid(msText, lngI, 1))
lngJ = lngJ + 1
Mid(sB, lngJ, 1) = Chr((intC And 63) + 59)
Select Case lngI Mod 3
Case 1
intK = intK Or ((intC \ 64) * 16)
Case 2
intK = intK Or ((intC \ 64) * 4)
Case 0
intK = intK Or (intC \ 64)
lngJ = lngJ + 1
Mid(sB, lngJ, 1) = Chr(intK + 59)
intK = 0
End Select
Next lngI
If lngA Mod 3 Then
lngJ = lngJ + 1
Mid(sB, lngJ, 1) = Chr(intK + 59)
End If
msText = sB
End Sub
'~~~.Shrink
Public Sub Shrink()
Dim intC As Integer
Dim intD As Integer
Dim intE As Integer
Dim lngA As Long
Dim lngB As Long
Dim lngI As Long
Dim lngJ As Long
Dim lngK As Long
Dim sB As String
lngA = Len(msText)
lngB = lngA - 1 - (lngA - 1) \ 4
sB = Space(lngB)
For lngI = 1 To lngB
lngJ = lngJ + 1
intC = Asc(Mid(msText, lngJ, 1)) - 59
Select Case lngI Mod 3
Case 1
lngK = lngK + 4
If lngK > lngA Then lngK = lngA
intE = Asc(Mid(msText, lngK, 1)) - 59
intD = ((intE \ 16) And 3) * 64
Case 2
intD = ((intE \ 4) And 3) * 64
Case 0
intD = (intE And 3) * 64
lngJ = lngJ + 1
End Select
Mid(sB, lngI, 1) = Chr(intC Or intD)
Next lngI
msText = sB
End Sub
Private Sub Initialize()
Dim intI As Integer
Randomize Rnd(-1)
For intI = 1 To Len(msKeyString)
Randomize Rnd(-Rnd * Asc(Mid(msKeyString, intI, 1)))
Next intI
End Sub
Не буду разбирать построчно класс, это все прекрасно описано в самом учебнике по программированию. Да и мне кажется, что это
не столь важно начинающим.
Далее нам требуется форма, в которой мы укажем путь к файлу и пароль для шифрования. Само собой, эта форма будет
иметь Visible = False и не будет вызываться из других модулей. Назовем ее "Admin". Добавим на нее два Textbox'а: txtFile и txtPassword1
в которых мы разместим путь к файлу и пароль для шифрования:
' Код формы Admin
Option Explicit
Private Sub Form_Load()
' Вставляем в txtFile путь к файлу настроек
txtFile.Text = App.Path + "\" + "options.ini"
End Sub
Private Sub txtFile_Change()
Dim lFileLen As Long
Dim sHead As String
' Проверяем наличие файла
On Error Resume Next
lFileLen = Len(Dir(txtFile.Text))
' Проверяем на наличие ошибок в имени файла
If Err <> 0 Or lFileLen = 0 Or Len(txtFile.Text) = 0 Then
Exit Sub
End If
' Проверяем по строке [Secret] в начале файла,
' что он зашифрован нашим классом
Open txtFile.Text For Binary As #1
sHead = Space(8)
Get #1, , sHead
Close #1
End Sub
Sub Encrypt()
Dim sHead As String
Dim sT As String
Dim sA As String
Dim cphX As New Cipher
Dim n As Long
Open txtFile.Text For Binary As #1
'Load entire file into sA
sA = Space$(LOF(1))
Get #1, , sA
Close #1
' Подготовка к шифрованию через функцию Hash
sT = Hash(Date & Str(Timer))
sHead = "[Secret]" & sT & Hash(sT & txtPassword1.Text)
' Шифрование
cphX.KeyString = sHead
cphX.Text = sA
cphX.DoXor
cphX.Stretch
sA = cphX.Text
' Запись результатов
Open txtFile.Text For Output As #1
Print #1, sHead
' Окончание
n = 1
Do
Print #1, Mid(sA, n, 70)
n = n + 70
Loop Until n > Len(sA)
Close #1
End Sub
Sub Decrypt()
Dim sHead As String
Dim sA As String
Dim sT As String
Dim cphX As New Cipher
Dim n As Long
' Проверяем по строке [Secret] в начале файла,
' что он зашифрован нашим классом
Open txtFile.Text For Input As #1
Line Input #1, sHead
Close #1
' Проверяем пароль
sT = Mid(sHead, 9, 8)
If InStr(sHead, Hash(sT & txtPassword1.Text)) <> 17 Then
Beep
Exit Sub
End If
' Открываем файл
Open txtFile.Text For Input As #1
Line Input #1, sHead
Do Until EOF(1)
Line Input #1, sT
sA = sA & sT
Loop
Close #1
' Дешифрование
cphX.KeyString = sHead
cphX.Text = sA
cphX.Shrink
cphX.DoXor
sA = cphX.Text
' Удаляем файл
Kill txtFile.Text
Open txtFile.Text For Binary As #1
Put #1, , sA
Close #1
End Sub
Function Hash(sA As String) As String
Dim cphHash As New Cipher
cphHash.KeyString = sA & "123456"
cphHash.Text = sA & "123456"
cphHash.DoXor
cphHash.Stretch
cphHash.KeyString = cphHash.Text
cphHash.Text = "123456"
cphHash.DoXor
cphHash.Stretch
Hash = cphHash.Text
End Function
Public Function Code()
Refresh
Encrypt
txtFile_Change
End Function
Public Function DeCode()
Refresh
Decrypt
txtFile_Change
End Function
Думаю тут вы во всем разобрались. Теперь, чтобы зашифровать файл настроек нужно вызвать
Call Admin.Code, а чтобы расшифровать -
Call Admin.Decode.
Остается нам написать функцию чтения-записи ini-файлов. Создадим модуль и назовем его "modINI" и вставим следующий код:
' Код модуля modINI
' Чтение и запись строк в файлы инициализации
Declare Function GetPrivateProfileString& Lib _
"kernel32" Alias "GetPrivateProfileStringA" (ByVal _
lpszSection$, ByVal lpszKey$, ByVal lpszDefault$, _
ByVal lpszReturnBuffer$, ByVal cchReturnBuffer&, _
ByVal lpszFile$)
Declare Function WritePrivateProfileString Lib _
"kernel32" Alias "WritePrivateProfileStringA" (ByVal _
lpApplicationName As String, ByVal lpKeyName As String, _
ByVal lpString As String, ByVal lplFileName As String) As Long
' Чтение из файла инициализации
Public Function GetINI(INIfile As String, Section As String, _
Key As String, Default As String)
' Дешифруем
Call Admin.DeCode
' Читаем данные
Dim temp As String * 256
Dim length As Integer
temp = Space$(256)
length = GetPrivateProfileString(Section, Key, _
Default, temp, 255, INIfile)
GetINI = Left$(temp, length)
' Шифруем
Call Admin.Code
End Function
' Запись в файл инициализации
Public Sub SetINI(INIfile As String, Section As String, _
Key As String, Value As String)
' Дешифруем
Call Admin.DeCode
' Записываем данные
Dim n As Integer
Dim temp As String
temp = Value
For n = 1 To Len(Value)
If Mid$(Value, n, 1) = vbCr Or Mid$(Value, n, 1) = vbLf _
Then Mid$(Value, n) = ""
Next n
n = WritePrivateProfileString(Section, Key, temp, INIfile)
' Шифруем
Call Admin.Code
End Sub
Для записи в ini-файл используйте строку:
SetINI Admin.txtFile.text, "Option", "Thing", Object
' Пример: SetINI Admin.txtFile.text, "SETUP", "Check1", Check1.Value
Для чтения из ini-файла используйте строку вида:
Object = GetINI(Admin.txtFile.text, "Option", "Thing", "Off")
' Где "Off" это вывод по умолчанию, если данной строки в файле нет
' Пример: Check1.Value = GetINI(Admin.txtFile.text, "SETUP", "Check1", "0")
Вот и все. Теперь вы можете не беспокоиться о защите настроек своей программы, где вы можете хранить не только сохраненные программные опции но и непосредственно личные пользовательские пароли и электронные ключи.
В дополнение хочу сказать, что данный код можно дополнить некоторыми полезными вставками. Например при первом запуске программы прописываем в реестре строку, которая будет означать, что файл настроек создан и успешно работает. И если же он будет удален, то программа блокируется и требует системного пароля.
Также, если администратору требуется самостоятельно отредактировать файл настроек, можно вставить данный кусок кода и не зубудьте поставить у формы использующей этот код KeyPreview = True:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Dim lFileLen As Long
Dim sHead As String
On Error Resume Next
lFileLen = Len(Dir(Admin.txtFile.Text))
If Err <> 0 Or lFileLen = 0 Or Len(Admin.txtFile.Text) = 0 Then
Exit Sub
End If
Open Admin.txtFile.Text For Binary As #1
sHead = Space(8)
Get #1, , sHead
Close #1
If KeyCode = vbKeyF12 Then
' Тут мы выключаем все, что записывает информацию
Call Admin.DeCode
Beep
ElseIf KeyCode = vbKeyF11 Then
If sHead <> "[Secret]" Then
' Тут мы обратно включаем запись настроект и шифруем
Call Admin.Code
Beep
End If
Тут как видно из примера при нажатии клавиши F12 информация будет дешифровываться и можно
будет отредактировать самостоятельно файл настроек, а при нажатии F11 информация будет переведена
в первоначальное состаяние.
Конечно, эти вариации можно продолжать и продолжать, а пока довольствуйтесь предоставленной вам информацией и защищенными электронными данными :).