I am interested if it’s possible to do string encryption/decryption using Excel Visual Basic and some cryptographic service provider.
I have found a walk-through Encrypting and Decrypting Strings in Visual Basic, but it seems it’s valid for standalone Visual Basic only.
So would you suggest me another encryption method or show how the walk-through could be adopted for Excel Visual Basic?
braX
11.5k5 gold badges20 silver badges33 bronze badges
asked Sep 24, 2009 at 10:53
The link you provide shows how to perform string encryption and decryption using VB.NET, and thus, using the .NET Framework.
Currently, Microsoft Office products cannot yet use the Visual Studio Tools for Applications component which will enable Office products to access the .NET framework’s BCL (base class libraries) which, in turn, access the underlying Windows CSP (cryptographic server provider) and provide a nice wrapper around those encryption/decryption functions.
For the time being, Office products are stuck with the old VBA (Visual Basic for Applications) which is based on the old VB6 (and earlier) versions of visual Basic which are based upon COM, rather than the .NET Framework.
Because of all of this, you will either need to call out to the Win32 API to access the CSP functions, or you will have to «roll-your-own» encryption method in pure VB6/VBA code, although this is likely to be less secure. It all depends upon how «secure» you’d like your encryption to be.
If you want to «roll-your-own» basic string encryption/decryption routine, take a look at these link to get you started:
Encrypt a String Easily
Better XOR Encryption with a readable string
vb6 — encryption function
Visual Basic 6 / VBA String Encryption/Decryption Function
If you want to access the Win32 API and use the underlying Windows CSP (a much more secure option), see these links for detailed information on how to achieve this:
How to encrypt a string in Visual Basic 6.0
Access to CryptEncrypt (CryptoAPI/WinAPI) functions in VBA
That last link is likely the one you’ll want and includes a complete VBA Class module to «wrap» the Windows CSP functions.
answered Sep 24, 2009 at 11:29
2
This code works well for me (3DES Encryption/Decryption):
I store INITIALIZATION_VECTOR and TRIPLE_DES_KEY as environment variables (obviously different values than those posted here) and get them using VBA Environ() function, so all sensitive data (passwords) in VBA code is encrypted.
Option Explicit
Public Const INITIALIZATION_VECTOR = "zlrs$5kd" 'Always 8 characters
Public Const TRIPLE_DES_KEY = ">tlF8adk=35K{dsa" 'Always 16 characters
Sub TestEncrypt()
MsgBox "This is an encrypted string: -> " & EncryptStringTripleDES("This is an encrypted string:")
Debug.Print EncryptStringTripleDES("This is an encrypted string:")
End Sub
Sub TestDecrypt()
MsgBox "u99CVItCGiMQEVYHf8+S22QbJ5CPQGDXuS5n1jvEIgU= -> " & DecryptStringTripleDES("u99CVItCGiMQEVYHf8+S22QbJ5CPQGDXuS5n1jvEIgU=")
End Sub
Function EncryptStringTripleDES(plain_string As String) As Variant
Dim encryption_object As Object
Dim plain_byte_data() As Byte
Dim encrypted_byte_data() As Byte
Dim encrypted_base64_string As String
EncryptStringTripleDES = Null
On Error GoTo FunctionError
plain_byte_data = CreateObject("System.Text.UTF8Encoding").GetBytes_4(plain_string)
Set encryption_object = CreateObject("System.Security.Cryptography.TripleDESCryptoServiceProvider")
encryption_object.Padding = 3
encryption_object.key = CreateObject("System.Text.UTF8Encoding").GetBytes_4(TRIPLE_DES_KEY)
encryption_object.IV = CreateObject("System.Text.UTF8Encoding").GetBytes_4(INITIALIZATION_VECTOR)
encrypted_byte_data = _
encryption_object.CreateEncryptor().TransformFinalBlock(plain_byte_data, 0, UBound(plain_byte_data) + 1)
encrypted_base64_string = BytesToBase64(encrypted_byte_data)
EncryptStringTripleDES = encrypted_base64_string
Exit Function
FunctionError:
MsgBox "TripleDES encryption failed"
End Function
Function DecryptStringTripleDES(encrypted_string As String) As Variant
Dim encryption_object As Object
Dim encrypted_byte_data() As Byte
Dim plain_byte_data() As Byte
Dim plain_string As String
DecryptStringTripleDES = Null
On Error GoTo FunctionError
encrypted_byte_data = Base64toBytes(encrypted_string)
Set encryption_object = CreateObject("System.Security.Cryptography.TripleDESCryptoServiceProvider")
encryption_object.Padding = 3
encryption_object.key = CreateObject("System.Text.UTF8Encoding").GetBytes_4(TRIPLE_DES_KEY)
encryption_object.IV = CreateObject("System.Text.UTF8Encoding").GetBytes_4(INITIALIZATION_VECTOR)
plain_byte_data = encryption_object.CreateDecryptor().TransformFinalBlock(encrypted_byte_data, 0, UBound(encrypted_byte_data) + 1)
plain_string = CreateObject("System.Text.UTF8Encoding").GetString(plain_byte_data)
DecryptStringTripleDES = plain_string
Exit Function
FunctionError:
MsgBox "TripleDES decryption failed"
End Function
Function BytesToBase64(varBytes() As Byte) As String
With CreateObject("MSXML2.DomDocument").createElement("b64")
.DataType = "bin.base64"
.nodeTypedValue = varBytes
BytesToBase64 = Replace(.Text, vbLf, "")
End With
End Function
Function Base64toBytes(varStr As String) As Byte()
With CreateObject("MSXML2.DOMDocument").createElement("b64")
.DataType = "bin.base64"
.Text = varStr
Base64toBytes = .nodeTypedValue
End With
End Function
Source code taken from here: https://gist.github.com/motoraku/97ad730891e59159d86c
Note the difference between the original code and my code, that is additional option encryption_object.Padding = 3 which forces VBA to not perform padding. With padding option set to 3 I get result exactly as in C++ implementation of DES_ede3_cbc_encrypt algorithm and which is in agreement with what is produced by this online tool.
answered Jan 2, 2018 at 1:35
OGCJNOGCJN
3733 silver badges9 bronze badges
1
This code works fine in VBA and can easily be moved to VB.NET
Avoids dealing with not «normal» characters. You decide in AllowedChars what characters to allow.
Public Function CleanEncryptSTR(MyString As String, MyPassword As String, Encrypt As Boolean) As String
'Encrypts strings chars contained in Allowedchars
'MyString = String to decrypt
'MyPassword = Password
'Encrypt True: Encrypy False: Decrypt
Dim i As Integer
Dim ASCToAdd As Integer
Dim ThisChar As String
Dim ThisASC As Integer
Dim NewASC As Integer
Dim MyStringEncrypted As String
Dim AllowedChars As String
AllowedChars = "&0123456789;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
If Len(MyPassword) > 0 Then
For i = 1 To Len(MyString)
' ThisASC = Asc(Mid(MyString, i, 1))
' ThisASC = IntFromArray(Asc(Mid(MyString, i, 1)), MyVector())
ThisChar = Mid(MyString, i, 1)
ThisASC = InStr(AllowedChars, ThisChar)
If ThisASC > 0 Then
ASCToAdd = Asc(Mid(MyPassword, i Mod Len(MyPassword) + 1, 1))
If Encrypt Then
NewASC = ThisASC + ASCToAdd
Else
NewASC = ThisASC - ASCToAdd
End If
NewASC = NewASC Mod Len(AllowedChars)
If NewASC <= 0 Then
NewASC = NewASC + Len(AllowedChars)
End If
MyStringEncrypted = MyStringEncrypted & Mid(AllowedChars, NewASC, 1)
Else
MyStringEncrypted = MyStringEncrypted & ThisChar
End If
Next i
Else
MyStringEncrypted = MyString
End If
CleanEncryptSTR = MyStringEncrypted
End Function
answered Apr 24, 2018 at 20:34
Create a Class Module called clsCifrado:
Option Explicit
Option Compare Binary
Private clsClave As String
Property Get Clave() As String
Clave = clsClave
End Property
Property Let Clave(value As String)
clsClave = value
End Property
Function Cifrar(Frase As String) As String
Dim Cachos() As Byte
Dim LaClave() As Byte
Dim i As Integer
Dim Largo As Integer
If Frase <> "" Then
Cachos() = StrConv(Frase, vbFromUnicode)
LaClave() = StrConv(clsClave, vbFromUnicode)
Largo = Len(clsClave)
For i = LBound(Cachos) To UBound(Cachos)
Cachos(i) = (Cachos(i) Xor LaClave(i Mod Largo)) + 34
Next i
Cifrar = StrConv(Cachos(), vbUnicode)
Else
Cifrar = ""
End If
End Function
Function Descifrar(Frase As String) As String
Dim Cachos() As Byte
Dim LaClave() As Byte
Dim i As Integer
Dim Largo As Integer
If Frase <> "" Then
Cachos() = StrConv(Frase, vbFromUnicode)
LaClave() = StrConv(clsClave, vbFromUnicode)
Largo = Len(clsClave)
For i = LBound(Cachos) To UBound(Cachos)
Cachos(i) = Cachos(i) - 34
Cachos(i) = (Cachos(i) Xor LaClave(i Mod Largo))
Next i
Descifrar = StrConv(Cachos(), vbUnicode)
Else
Descifrar = ""
End If
End Function
Now you can use it in your code:
to cipher
Private Sub btnCifrar_Click()
Dim Texto As String
Dim cCifrado As clsCifrado
Set cCifrado = New clsCifrado
'---poner la contraseña
If tbxClave.Text = "" Then
MsgBox "The Password is missing"
End Sub
Else
cCifrado.Clave = tbxClave.Text
End If
'---Sacar los datos
Texto = tbxFrase.Text
'---cifrar el texto
Texto = cCifrado.Cifrar(Texto)
tbxFrase.Text = Texto
End Sub
To descipher
Private Sub btnDescifrar_Click()
Dim Texto As String
Dim cCifrado As clsCifrado
Set cCifrado = New clsCifrado
'---poner la contraseña
If tbxClave.Text = "" Then
MsgBox "The Password is missing"
End Sub
Else
cCifrado.Clave = tbxClave.Text
End If
'---Sacar los datos
Texto = tbxFrase.Text
'---cifrar el texto
Texto = cCifrado.Descifrar(Texto)
tbxFrase.Text = Texto
End Sub
Cameron
3,0981 gold badge24 silver badges40 bronze badges
answered Mar 12, 2014 at 17:07
You can call pipe excel cell data through any shell script.
Install the GPL Bert (http://bert-toolkit.com/) R language interface for Excel.
Use the R script below in Excel to pipe cell data to Bash / perl / gpg / openssl.
c:> cat c:R322callable_from_excel.R
CRYPTIT <- function( PLAINTEXT, MASTER_PASS ) {
system(
sprintf("bash -c 'echo '%s' |
gpg --symmetric --cipher-algo blowfish --force-mdc --passphrase '%s' -q |
base64 -w 0'",
PLAINTEXT, MASTER_PASS),
intern=TRUE)
}
DECRYPTIT <- function( CRYPTTEXT, MASTER_PASS ) {
system(
sprintf("bash -c 'echo '%s'|
base64 -d |
gpg --passphrase '%s' -q |
putclip | getclip' ",CRYPTTEXT,MASTER_PASS),
intern=TRUE)
}
In Excel, you can try: C1=CRYPTIT(A1,A2) and C2=DECRYPTIT(C1,A2)
Optional: putclip saves decrypted text in clipboard.
Both functions types are: String -> String.
Usual caveats about escaping single-quotes in single-quoted strings.
answered Jun 1, 2016 at 13:59
moshmosh
1,37214 silver badges16 bronze badges
Here is a basic symmetric encryption/decryption example:
Sub testit()
Dim inputStr As String
inputStr = "Hello world!"
Dim encrypted As String, decrypted As String
encrypted = scramble(inputStr)
decrypted = scramble(encrypted)
Debug.Print encrypted
Debug.Print decrypted
End Sub
Function stringToByteArray(str As String) As Variant
Dim bytes() As Byte
bytes = str
stringToByteArray = bytes
End Function
Function byteArrayToString(bytes() As Byte) As String
Dim str As String
str = bytes
byteArrayToString = str
End Function
Function scramble(str As String) As String
Const SECRET_PASSWORD As String = "K*4HD%f#nwS%sdf032#gfl!HLKN*pq7"
Dim stringBytes() As Byte, passwordBytes() As Byte
stringBytes = stringToByteArray(str)
passwordBytes = stringToByteArray(SECRET_PASSWORD)
Dim upperLim As Long
upperLim = UBound(stringBytes)
ReDim scrambledBytes(0 To upperLim) As Byte
Dim idx As Long
For idx = LBound(stringBytes) To upperLim
scrambledBytes(idx) = stringBytes(idx) Xor passwordBytes(idx)
Next idx
scramble = byteArrayToString(scrambledBytes)
End Function
Be aware that this will crash if your given input string is longer than the SECRET_PASSWORD. This is just an example to get started with.
answered Dec 12, 2014 at 16:03
MathKidMathKid
1,7231 gold badge20 silver badges21 bronze badges
1
Sub Main()
Dim Key As String
Dim Msg As String
Dim EncryptMsg As String
Dim DecryptMsg As String
Key = "мечта"
Msg = "реализация шифра методом вертикальной перестановки с ключом мечта"
EncryptMsg = VerticalShuffleEncrypt(Key, Msg)
DecryptMsg = VerticalShuffleDecrypt(Key, EncryptMsg)
' реализация шифра методом вертикальной перестановки с ключом мечта '
Cells(1, 1).Value = Msg
' иярт инеаил аеаш деайеосчерз аовкорн юмлифемтьптккмтациморл св оч '
Cells(2, 1).Value = EncryptMsg
' реализация шифра методом вертикальной перестановки с ключом мечта '
Cells(3, 1).Value = DecryptMsg
End Sub
Function VerticalShuffleEncrypt(ByVal Key As String, ByVal Msg As String) As String
Dim KeyArr As Object
Dim L As Long
Dim Col As String
Dim Res As String
L = Len(Msg) Len(Key)
If (Len(Msg) Mod Len(Key) <> 0) Then
L = L + 1
End If
Set KeyArr = CreateObject("System.Collections.ArrayList")
For I = 1 To Len(Key)
KeyArr.Add Mid(Key, I, 1)
Next I
KeyArr.Sort
Res = ""
For Each K In KeyArr
I = InStr(1, Key, K, vbTextCompare)
Col = ""
'For J = I To Len(Msg) Step L
' Res = Res & Mid(Msg, J, 1)
'Next J
For J = I To Len(Msg) Step Len(Key)
Col = Col & Mid(Msg, J, 1)
Next J
If (Len(Col) <> L) Then
Col = Col & " "
End If
Res = Res & Col
Next
VerticalShuffleEncrypt = Res
End Function
Function VerticalShuffleDecrypt(ByVal Key As String, ByVal Msg As String) As String
Dim KeyArr As Object
Dim IndexArr As Object
Dim L As Long
Dim Res As String
L = Len(Msg) Len(Key)
If (Len(Msg) Mod Len(Key) <> 0) Then
L = L + 1
End If
Set KeyArr = CreateObject("System.Collections.ArrayList")
Set IndexArr = CreateObject("System.Collections.ArrayList")
For I = 1 To Len(Key)
KeyArr.Add Mid(Key, I, 1)
Next I
KeyArr.Sort
For I = 1 To Len(Key)
IndexArr.Add KeyArr.IndexOf(Mid(Key, I, 1), 0) + 1
Next I
Res = ""
For I = 1 To L
For Each Index In IndexArr
Res = Res & Mid(Msg, ((Index - 1) * L) + I, 1)
Next
Next I
VerticalShuffleDecrypt = Res
End Function
Шифр вертикальной перестановки
м е ч т а
3 2 5 4 1
р е а л и
з а ц и я
ш и ф р
а м е т
о д о м
в е р т и
к а л ь н
о й п е
р е с т а
н о в к и
с к л
ю ч о м
м е ч т а
Результат:
Исходное сообщение:
реализация шифра методом вертикальной перестановки с ключом мечта
Зашифрованное сообщение:
иярт инеаил аеаш деайеосчерз аовкорн юмлифемтьптккмтациморл св оч
Восстановленное сообщение:
реализация шифра методом вертикальной перестановки с ключом мечта
В этой статье опубликованы различные вспомогательные функции на VBA, которые порой помогают в работе.
1. Функция формирования инициалов из имени и отчества
Function CropFIO(ByVal FIO As String) As String ' получает в качестве параметра текстовую строку с виде "Фамилия имя отчество" ' обрезает имя и отчество, оставляя лишь инициалы - в виде "Фамилия И. О." CropFIO = Application.Trim(FIO): arr = Split(CropFIO, " ") If UBound(arr) <> 2 Then Exit Function ' Если в ячейке не 3 слова - выход из процедуры CropFIO = Replace(CropFIO, " " & arr(1), " " & UCase(Left(arr(1), 1)) & ".") CropFIO = Replace(CropFIO, " " & arr(2), " " & UCase(Left(arr(2), 1)) & ".") End Function
2. Шифрование строк на VBA
Dim s(0 To 255) As Integer, kep(0 To 255) As Integer Public Function EnDeCrypt(ByVal plaintxt As String, ByVal Password As String) As String Dim temp As Integer, a As Integer, b As Integer, cipherby As Byte, cipher As String b = 0: For a = 0 To 255: b = b + 1: If b > Len(Password) Then b = 1 kep(a) = Asc(Mid$(Password, b, 1)): Next a For a = 0 To 255: s(a) = a: Next a: b = 0 For a = 0 To 255: b = (b + s(a) + kep(a)) Mod 256: temp = s(a): s(a) = s(b): s(b) = temp: Next a For a = 1 To Len(plaintxt): cipherby = EnDeCryptSingle(Asc(Mid$(plaintxt, a, 1))) cipher = cipher & Chr(cipherby): Next: EnDeCrypt = cipher End Function Public Function EnDeCryptSingle(plainbyte As Byte) As Byte Dim i As Integer, j As Integer, temp As Integer, k As Integer, cipherby As Byte i = (i + 1) Mod 256: j = (j + s(i)) Mod 256: temp = s(i): s(i) = s(j): s(j) = temp k = s((s(i) + s(j)) Mod 256): cipherby = plainbyte Xor k: EnDeCryptSingle = cipherby End Function ' примеры использования Sub Шифрование_с_расшифровкой() MsgBox EnDeCrypt(EnDeCrypt("123456", "passw"), "passw") End Sub Sub Тест_шифра() MsgBox EnDeCrypt("123456", "пароль") End Sub
3. Сортировка двумерного массива по нулевому столбцу
Public Function CoolSort(SourceArr As Variant) As Variant Dim Check As Boolean, iCount As Integer, jCount As Integer, nCount As Integer ReDim tmpArr(UBound(SourceArr, 2)) As Variant Do Until Check Check = True For iCount = LBound(SourceArr, 1) To UBound(SourceArr, 1) - 1 If Val(SourceArr(iCount, 0)) > Val(SourceArr(iCount + 1, 0)) Then For jCount = LBound(SourceArr, 2) To UBound(SourceArr, 2) tmpArr(jCount) = SourceArr(iCount, jCount) SourceArr(iCount, jCount) = SourceArr(iCount + 1, jCount) SourceArr(iCount + 1, jCount) = tmpArr(jCount) Check = False Next End If Next Loop CoolSort = SourceArr End Function
4. Преобразование строки в набор ASC кодов
Function String2CharCodes(ByVal txt$) As String sep = " & " For i = 1 To Len(txt) charcode = "Chr(" & Asc(Mid(txt, i, 1)) & ")" String2CharCodes = String2CharCodes & sep & charcode Next i String2CharCodes = Mid(String2CharCodes, Len(sep) + 1) End Function
5. Функции для определения нажатой клавиши
'============= Функции для определения нажатой клавиши ================================= Public Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As VirtualKeys) As Integer Public Enum VirtualKeys ' Virtual Keys, Standard Set VK_LBUTTON = &H1: VK_RBUTTON = &H2: VK_CANCEL = &H3: VK_MBUTTON = &H4 'VK_MBUTTON = &H4 - NOT contiguous with L RBUTTON VK_BACK = &H8: VK_TAB = &H9: VK_CLEAR = &HC: VK_RETURN = &HD VK_SHIFT = &H10: VK_CONTROL = &H11: VK_MENU = &H12: VK_PAUSE = &H13: VK_CAPITAL = &H14: VK_ESCAPE = &H1B VK_SPACE = &H20: VK_PRIOR = &H21: VK_NEXT = &H22: VK_END = &H23: VK_HOME = &H24 VK_LEFT = &H25: VK_UP = &H26: VK_RIGHT = &H27: VK_DOWN = &H28: VK_SELECT = &H29: VK_PRINT = &H2A VK_EXECUTE = &H2B: VK_SNAPSHOT = &H2C: VK_INSERT = &H2D: VK_DELETE = &H2E: VK_HELP = &H2F ' VK_A thru VK_Z are the same as their ASCII equivalents: 'A' thru 'Z' ' VK_0 thru VK_9 are the same as their ASCII equivalents: '0' thru '9' VK_NUMPAD0 = &H60: VK_NUMPAD1 = &H61: VK_NUMPAD2 = &H62: VK_NUMPAD3 = &H63: VK_NUMPAD4 = &H64 VK_NUMPAD5 = &H65: VK_NUMPAD6 = &H66: VK_NUMPAD7 = &H67: VK_NUMPAD8 = &H68: VK_NUMPAD9 = &H69 VK_MULTIPLY = &H6A: VK_ADD = &H6B: VK_SEPARATOR = &H6C: VK_SUBTRACT = &H6D: VK_DECIMAL = &H6E: VK_DIVIDE = &H6F VK_F1 = &H70: VK_F2 = &H71: VK_F3 = &H72: VK_F4 = &H73: VK_F5 = &H74: VK_F6 = &H75: VK_F7 = &H76 VK_F8 = &H77: VK_F9 = &H78: VK_F10 = &H79: VK_F11 = &H7A: VK_F12 = &H7B VK_F13 = &H7C: VK_F14 = &H7D: VK_F15 = &H7E: VK_F16 = &H7F: VK_F17 = &H80: VK_F18 = &H81 VK_F19 = &H82: VK_F20 = &H83: VK_F21 = &H84: VK_F22 = &H85: VK_F23 = &H86: VK_F24 = &H87 VK_NUMLOCK = &H90: VK_SCROLL = &H91 ' VK_L VK_R - left and right Alt, Ctrl and Shift virtual keys. ' Used only as parameters to GetAsyncKeyState() and GetKeyState(). ' No other API or message will distinguish left and right keys in this way. VK_LSHIFT = &HA0: VK_RSHIFT = &HA1: VK_LCONTROL = &HA2: VK_RCONTROL = &HA3: VK_LMENU = &HA4: VK_RMENU = &HA5 VK_ATTN = &HF6: VK_CRSEL = &HF7: VK_EXSEL = &HF8: VK_EREOF = &HF9: VK_PLAY = &HFA VK_ZOOM = &HFB: VK_NONAME = &HFC: VK_PA1 = &HFD: VK_OEM_CLEAR = &HFE End Enum '========================================================================================== Public Function KeyPressed(ByVal VKey As VirtualKeys) As Boolean KeyPressed = IIf(GetKeyState(VKey) < 0, True, False) End Function
6. Макрос для создания копии файла программы
(подразумевается наличие в программе глобальной константы или функции PROJECT_NAME)
Sub CreateBackup() On Error Resume Next: ThisWorkbook.Save BackupsPath = Replace(ThisWorkbook.FullName, ThisWorkbook.Name, PROJECT_NAME & " Backups") MkDir BackupsPath filename = PROJECT_NAME & "_BACKUP_" & Format(Now, "DD-MM-YYYY__HH-NN-SS") & ".xls" ThisWorkbook.SaveCopyAs BackupsPath & filename 'Debug.Print BackupsPath & filename End Sub
7. Изменяем форматирование цифр в ячейке (выделяем все цифры полужирным шрифтом)
Sub BoldingDigits(ByRef celll As Range) For i = 1 To celll.Characters.count letter = celll.Characters(start:=i, Length:=1).Text celll.Characters(start:=i, Length:=1).Font.Bold = IsNumeric(letter) Next End Sub
8. Поиск артикула (последовательности цифр заданной длины) в текстовой строке
Ссылка на примеры использования Regexp: script-coding.com/WSH/RegExp.html
Function FindDigits(ByVal txt$, ByVal DigitsCount%) As String ' ищет в строке txt$ подстроку цифр длиной DigitsCount% Set expres = CreateObject("VBScript.RegExp") expres.Pattern = Replace(String(DigitsCount%, "%"), "%", "[0-9]") If expres.test(txt$) Then FindDigits = expres.Execute(txt$)(0).Value End Function
Function FindDigits(ByVal txt$, ByVal DigitsCount%) As String ' ищет в строке txt$ подстроку цифр длиной DigitsCount% Set RegExp = CreateObject("VBScript.RegExp"): RegExp.Global = True RegExp.Pattern = "[D]": txt$ = " " & RegExp.Replace(txt$, " ") & " " RegExp.Pattern = " [d]{" & DigitsCount% & "} " If RegExp.test(txt$) Then FindDigits = RegExp.Execute(txt$)(0).Value End Function
9. Добавление значений сразу во весь столбец двумерного массива
Sub AddValueIntoColumn(ByRef arr, ByVal ColumnIndex%, ByVal NewValue) ' добавляет значение NewValue в столбец ColumnIndex% всех строк ' переданного по ссылке двумерного массива arr For i = LBound(arr) To UBound(arr) arr(i, ColumnIndex%) = NewValue Next i End Sub
10. Использование Application.OnTime с задержкой меньше секунды
Sub ЗапускМакросаСНебольшойЗадержкой() ' по мотивам макроса ZVI_Timer ЗадержкаВСекундах = 0.3 ' в секундах НазваниеМакроса$ = "test" ' этот макрос будет запущен через 0.3 сек. ЗадержкаВЧасах$ = Replace(Format(CDbl(TimeSerial(0, 0, 1)) * ЗадержкаВСекундах, "0.000000000"), ",", ".") macro = "ON.TIME(NOW()+" & ЗадержкаВЧасах$ & ", """ & НазваниеМакроса$ & """)" ' формируем команду запуска ExecuteExcel4Macro macro ' macro = ON.TIME(NOW()+0.000003472, "test") End Sub
11. Преобразование коллекции в массив
Function Collection2Array(ByVal coll As Collection) As Variant ReDim arr(0 To coll.Count - 1): Dim i As Long For i = 1 To coll.Count: arr(i - 1) = coll(i): Next i Collection2Array = arr End Function
12. Разрешаем Excel доступ в Интернет путем отключения брандмауэра:
' включаем файрвол Windows (доступ в интернет ограничен) CreateObject("HNetCfg.FwMgr").LocalPolicy.CurrentProfile.FirewallEnabled = True ' отключаем файрвол Windows (доступ в интернет открыт) CreateObject("HNetCfg.FwMgr").LocalPolicy.CurrentProfile.FirewallEnabled = False
Перейти к содержимому
Шифровка:
Функция получает в качестве параметров открытый текст и смещения. Исходный текст разбивается на символы и получается ASCII код каждого символа. ASCII код символа смещается на величину смещения. С данного кода получается символ и добавляется к результирующей сроки возвращаемого функцией inchezar.
Function inchezar(inp As String, key As Integer)
Dim rez As String
Dim ls As Integer
Dim simv As String
Dim ks As Integer
Dim pr As String
key = key Mod 255
rez = «»
ls = Len(inp)
For i = 0 To ls — 1
simv = Mid(inp, i + 1, 1)
ks = Asc(simv)
pr = ks + key
If (pr > 255) Then pr = (pr Mod 255) + 31
rez = rez + Chr(pr)
Next i
inchezar = rez
End Function
Дешифровка:
Функция получает в качестве входных параметров зашифрованный текст и количество символов, на которое делается сдвиг. Аналогичными операциями как в функции inchezar (но со смещением кругу символа в другую сторону) получается открытый текст, который возвращается функцией outchezar.
Function outchezar(inp As String, key As Integer)
Dim rez As String
Dim ls As Integer
Dim simv As String
Dim ks As Integer
Dim pr As String
key = key Mod 255
rez = «»
ls = Len(inp)
For i = 0 To ls — 1
simv = Mid(inp, i + 1, 1)
ks = Asc(simv)
pr = ks — key
If (pr < 32) Then pr = (pr Mod 31) + 224
rez = rez + Chr(pr)
Next i
outchezar = rez
End Function
Защита ячеек шифром Виженера
Парольная защита листов в Microsoft Excel давно стала притчей во языцех. В том плане, что ее, по-сути, нет. С регулярностью примерно раз в месяц я получаю вопросы по почте на тему «как мне защитить мои данные на листе Excel от просмотра/изменения?» и каждый раз не знаю что ответить. Можно, конечно, дать ссылочку на статью с подробным описанием всех способов защиты ячеек и листов в Excel, но такая защита остановит только начинающего. В сети можно найти кучу платных и бесплатных программ для взлома такой защиты тупым перебором за считанные минуты.
В какой-то момент мне это надоело и я стал искать способы более надежной защиты данных в Excel собственными силами. Самым простым и удобным оказался шифр Виженера.
Принцип шифра Виженера
Одним из самых древних и простых в реализации является шифр Цезаря, который использовал его для тайной переписки. Суть его в том, что каждая буква исходного шифруемого сообщения сдвигается в алфавите на заданное количество символов. Так, например, если сдвиг равен 3, то буква А превратится в Г, буква Б — в Д и так далее:
Символы в конце алфавита (Э, Ю, Я), соответственно, будут превращаться его начало (А, Б, В).
Реализовать такой шифр просто, но стойкость его невелика — найти нужное число сдвига и дешифровать сообщение можно даже прямым перебором за 20-30 итераций, что займет даже у человека не больше часа, а у современного компьютера доли секунды. Поэтому еще в 15 веке был впервые придуман, а потом в 16 веке французским дипломатом Блезом Виженером официально представлен более совершенный метод на основе шифра Цезаря, получивший впоследствии название «шифр Виженера». Его принцип в том, что каждая буква в исходном шифруемом тексте сдвигается по алфавиту не на фиксированное, а переменное количество символов. Величина сдвига каждой буквы задается ключом (паролем) — секретным словом или фразой, которая используется для шифрования и расшифровки.
Допустим, мы хотим зашифровать фразу «КЛАД ЗАРЫТ В САДУ» используя слово ЗИМА в качестве ключа. Запишем это слово подряд несколько раз под исходной фразой:
Для удобства шифрования используем так называемый «квадрат Виженера» — таблицу, где в каждой строке алфавит сдвигается на одну позицию вправо:
Если взять строку с первой буквой ключа (З) и столбец с первой буквой исходного текста (К), то на их пересечении увидим букву «Т» — это и будет первая буква нашего зашифрованного сообщения. Затем процедура повторяется для всех остальных пар букв ключа и исходного сообщения по очереди и в результате мы получаем зашифрованный вариант нашей исходной фразы:
Заметьте, что одна и та же буква (например А) в исходном сообщений превратилась в разные буквы на выходе (Н, Й и Б), т.к. сдвиг при шифровании для них был разный. Именно поэтому вскрыть шифр Виженера простыми способами невозможно — вплоть до 19 века он считался невзламываемым и успешно использовался военными, дипломатами и шпионами многих стран, частности — конфедератами во время Гражданской войны в США.
Реализация формулами по квадрату Виженера
Если использовать готовый квадрат Виженера как в примере выше, то реализовать шифрование можно одной формулой с помощью функций ИНДЕКС (INDEX) и ПОИСКПОЗ (MATCH), как это было описано в статье про двумерный поиск в таблице. Выглядеть это может примерно так:
Логика этой формулы следующая:
- Первая функция ПОИСКПОЗ (подсвечена зеленым) ищет первую букву ключа (З) в зеленом столбце (B9:B40) и выдает порядковый номер ячейки, где она ее нашла, т.е. номер строки в квадрате Виженера по которому идет шифрование.
- Вторая функция ПОИСКПОЗ (подсвечена розовым) аналогичным образом ищет первую букву исходного сообщения (К) в красной строке и выдает порядковый номер столбца.
- Функция ИНДЕКС выдает содержимое ячейки из квадрата (C9:AH40) с пересечения строки и столбца с найденными номерами.
Реализация формулами по кодам символов
Легко сообразить, что в реальной жизни в документах могут использоваться не только буквы русского языка, но и латиница, цифры, знаки препинания и т.д. Делать квадрат Виженера с участием всех этих символов — та еще эпопея, но есть другой, гораздо более простой способ.
Внутри компьютера и операционной системы каждый символ имеет свой числовой код от 0 до 255 (его еще называют ASCII-кодом). Microsoft Excel имеет в своем стандартном наборе две функции, которые умеют с ними работать:
- Функция КОДСИМВ (CODE) — выдает числовой код символа, указанного в качестве аргумента. Например КОДСИМВ(«Ж») выдаст 198.
- Функция СИМВОЛ (CHAR) — выдает символ, соответствующий указанному в аргументе коду, т.е. наоборот СИМВОЛ(198) даст нам букву Ж.
Для применения шифра Виженера запишем наш исходный текст и ключ друг под другом как раньше и выведем коды каждой буквы с помощью функции КОДСИМВ:
Теперь сложим коды символов ключа и исходного текста, добавив функцию ОСТАТ (MOD), чтобы при превышении максимально допустимого количества символов (256) остаться в пределах 0-255:
Теперь осталось использовать функцию СИМВОЛ, чтобы вывести символы по полученным кодам и сформировать зашифрованное сообщение:
Само-собой, можно было бы обойтись и без дополнительных строк, уложив все функции в одну формулу для компактности:
Расшифровка производится совершенно аналогично, только знак «плюс» в формуле меняется на «минус»:
Для шпионских игр шифрование такими спецсимволами, конечно, не очень удобно — так и представляю себе глаза радистки Кэт при попытке передать третий и пятый символы нашей шифровки Но нам их, отстреливаясь из именного ТТ во время погони, на бумажке не писать, так что для наших целей — сойдет.
Макросы для шифрования-дешифрования
Ну, а теперь самое интересное. Чтобы применить шифр Виженера в реальной жизни лучше будет воспользоваться простым макросом, который проводит все описанные в предыдущем пункте операции с каждой ячейкой текущего листа автоматически. Откройте редактор Visual Basic с помощью сочетания клавиш Alt+F11 или кнопкой Visual Basic на вкладке Разработчик (Developer). Вставьте новый модуль с помощью команды меню Insert — Module и скопируйте туда текст наших макросов:
'Шифрование текущего листа Sub Encrypt() Dim Pass$, Key$ Pass = InputBox("Введите ключ для шифрования:") Key = WorksheetFunction.Rept(Pass, 100) For Each cell In ActiveSheet.UsedRange Out = "" Txt = cell.Formula For i = 1 To Len(Txt) Out = Out & Chr((Asc(Mid(Txt, i, 1)) + Asc(Mid(Key, i, 1))) Mod 256) Next i cell.Value = Out Next cell End Sub 'Дешифрация текущего листа Sub Decrypt() Dim Pass$, Key$ Pass = InputBox("Введите ключ для расшифровки:") Key = WorksheetFunction.Rept(Pass, 100) For Each cell In ActiveSheet.UsedRange Out = "" Txt = cell.Value For i = 1 To Len(Txt) Out = Out & Chr((Asc(Mid(Txt, i, 1)) - Asc(Mid(Key, i, 1)) + 256) Mod 256) Next i cell.Formula = Out Next cell End Sub
Первый макрос запрашивает у пользователя ключ и шифрует все ячейки текущего листа. Второй макрос производит обратную операцию дешифрования. Запустить получившиеся макросы можно с помощью сочетания клавиш Alt+F8 или кнопки Макросы (Macros) на вкладке Разработчик (Developer). Выглядеть все это может примерно так:
Важные нюансы
- ВНИМАНИЕ! Если вы внимательно прочитали статью, то должны четко понимать — не существует легкого способа узнать или подобрать ключ! Есть несколько методик взлома шифра Виженера, но все они весьма сложны для неспециалиста и не дают 100% гарантии. Если вы забудете ключ — потеряете данные навсегда с большой вероятностью. Если что — я вас предупредил.
- При шифровании не нарушаются формулы, ссылки и форматирование — после дешифрации все отлично работает.
- Если при дешифрации вы неправильно введете ключ, то получите бессмысленную «кашу» из спецсимволов вместо своего текста (т.к. сдвиг кодов будет неправильным). Тогда придется откатиться на шаг назад повторным шифрованием с тем же паролем и потом снова попробовать расшифровать документ еще раз (на этот раз используя правильный ключ).
Ссылки по теме
- 4 способа защиты данных в Microsoft Excel
- Суперскрытый лист в книге Excel
- Выборочное отображение листов книги пользователям по паролю
This VBA macro is hopefully a bit more interesting than the last, and shows how I created a message encryptor/decryptor in Excel using VBA/macros.
The lesson covers these specific areas of VBA usage:
– For…Next loop (including nested loops)
– If…ElseIf…Then Statement
– Excel functions: CHAR() / LEN() / VLOOKUP()
– VBA functions: Mod / Mid / Asc / Chr
Download the Message Encryptor & Decryptor here to follow along with the video lesson.
Comments are closed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
Function MyEncode$(txt$, key$) Dim i&, j&, n&, m&, l&, s1$, s2$ n = Len(key) 'длина ключа If n = 0 Then Exit Function 'если ключ нулевой длины, то выходим из функции l = Len(txt) m = -Int(-l / n) ReDim nKeys&(1 To n) For i = 1 To n 'определяем последовательность столбцов по ключу s1 = Mid$(key, i, 1) For j = 1 To n s2 = Mid$(key, j, 1) If s1 > s2 Or (s1 = s2 And j <= i) Then nKeys(i) = nKeys(i) + 1 Next j, i ReDim out$(1 To n * m) For i = 1 To Len(txt) 'шифруем строку out(nKeys((i - 1) Mod n + 1) * m + (i - 1) n - m + 1) = Mid(txt, i, 1) Next i MyEncode = Join(out, "") End Function Function MyDecode$(txt$, key$) Dim i&, j&, d&, k&, n&, m&, l&, nTmp&, sTmp$, outTxt$ n = Len(key) 'длина ключа If n = 0 Then Exit Function 'если ключ нулевой длины, то выходим из функции l = Len(txt) m = -Int(-l / n) d = l Mod n ReDim nKeys&(1 To n), sKeys(1 To n) For i = 1 To n 'определяем последовательность столбцов по ключу nKeys(i) = i sKeys(i) = Mid$(key, i, 1) For j = 1 To i - 1 If sKeys(i) < sKeys(j) Then sTmp = sKeys(i): sKeys(i) = sKeys(j): sKeys(j) = sTmp nTmp = nKeys(i): nKeys(i) = nKeys(j): nKeys(j) = nTmp End If Next j, i ReDim out$(1 To n, 1 To m) For i = 1 To n 'дешифруем строку For j = 1 To m + (nKeys(i) > d And d > 0) k = k + 1 out(nKeys(i), j) = Mid$(txt, k, 1) Next j, i For j = 1 To m For i = 1 To n outTxt = outTxt & out(i, j) Next i, j MyDecode = outTxt End Function |