Can I use DPAPI (or something like it) in VBA? - excel

another in my beginnerish series of questions about VBA.
I am in the process of writing an Excel add-in in VBA, and the add-in uses a local configuration file.
This file needs to contain a password for a remote service.
Obviously, it is less than ideal to store this password as plaintext. But I am looking for an algorithm that can encode/decode text so it at least doesn't look like plaintext in the configuration file.
I came across a reference to Windows DPAPI but I'm not sure whether this is an appropriate solution for Excel VBA. I also am not sure how I can use this API from within VBA, as I've only found references to using it with .NET. Visual Studio is unavailable to this project.
So the 2-part question is this:
1) If it is possible to use DPAPI from within VBA, can I have an example of its use?
2) If it is not possible to use DPAPI in VBA, do you have any suggestions for how to store text in some encoded fashion that is reversible?
The solution must work in Excel 2003 and later, if it matters.
Thank you once again.

The solution must work in Excel 2003 and later, if it matters.
For Excel VBA, I suggest using the CAPICOM Library.
Download the file from here. Once it is installed, follow these instructions for registering the Dll.
32 bit OS
Copy the file Capicom.dll from the C:\Program Files\Microsoft CAPICOM 2.1.0.2 SDK\Lib to C:\Windows\System32
Next on Start Menu | Run , type this
Regsvr32 C:\Windows\System32\Capicom.dll
64 bit OS
Copy the file Capicom.dll from the C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Lib\X86 to C:\Windows\SysWOW64
Next on Start Menu | Run , type this
Regsvr32 C:\Windows\SysWOW64\Capicom.dll
Now we are set to use it in our VBA Project
Paste this code in a module
Option Explicit
Sub Sample()
Dim TextToEncrypt As String, EncryptedText As String
Dim TextToDeCrypt As String, DeCryptedText As String
Dim KeyToEncrypt As String
TextToEncrypt = "Hello World"
KeyToEncrypt = "JoshMagicWord"
EncryptedText = EncryptString(TextToEncrypt, KeyToEncrypt)
DeCryptedText = DecryptString(EncryptedText, KeyToEncrypt)
Debug.Print "The string " & TextToEncrypt & " after encryption looks like this"
Debug.Print "-----------------------------------------------------------------"
Debug.Print EncryptedText
Debug.Print "-----------------------------------------------------------------"
Debug.Print "The above string after decrypting looks like this"
Debug.Print "-----------------------------------------------------------------"
Debug.Print DeCryptedText
End Sub
Public Function EncryptString(strText As String, ky As String) As String
Dim Cap As Object
Dim cryptIt
Set Cap = CreateObject("CAPICOM.EncryptedData")
Cap.Algorithm = 3
Cap.SetSecret ky
Cap.Content = strText
EncryptString = Cap.Encrypt
End Function
Public Function DecryptString(strText As String, ky As String) As String
Dim Cap As Object
Dim cryptIt
Set Cap = CreateObject("CAPICOM.EncryptedData")
Cap.Algorithm = 3
Cap.SetSecret ky
Cap.Decrypt strText
DecryptString = Cap.Content
End Function
The function EncryptString encrypts the string and the function DecryptString decrypts the string. See snapshot of results when you run the above Sub Sample

Related

PDDoc.Save Adobe Acrobat method not working in Excel VBA

Our office runs Arobat DC Pro and Excel 2016, we have been using the code below (scaled down version) in Excel VBA for years to save active Adobe PDF documents (that is, the open PDF doc that most recently had focus).
Since recently upgrading Arobat DC Pro to a newer version, the Acrobat PDDoc.Save method no longer works. It does not throw an error, it just doesn't save the active PDF.
I have had our IT dept. do an uninstall/reinstall of Acrobat PRO on a couple of computers but code still does not work.
Note, the Adobe Acrobat reference library is selected in VBA.
Any suggestions on how to fix?
Sub SaveActivePDF()
Dim AcroApp As Acrobat.CAcroApp
Dim PdDoc As Acrobat.CAcroPDDoc
Dim avdoc As Acrobat.CAcroAVDoc
Dim boolWasSaved As Boolean
Set AcroApp = CreateObject("AcroExch.App")
Set avdoc = AcroApp.GetActiveDoc
Set PdDoc = avdoc.GetPDDoc
DayTime = Format(Now, "yymmddhmmss")
Username = Environ("USERNAME")
PdfNewPath = "C:\Users\" & Username & "\Desktop\TEST PDF " & DayTime & ".pdf"
boolWasSaved = PDDoc.Save(PDSaveFull, PdfNewPath) '<-- NOT WORKING
If boolWasSaved = True Then
MsgBox "PDF WAS SAVED!"
Else: MsgBox "ERROR - PDF not saved"
End If
End Sub
For the record, access to the Acrobat library was blocked by sofware updates that changed default settings.
The issue was resolved as follows: Open any PDF > Edit > Preferences > Security (Enhanced) > ** UNCHECK ** check box "Enable Protected Mode at startup (Preview)" > exit all PDFs.
VBA idle->press "F2"-> search "AcroPDDoc"->locate its method "Save"
Function Save(nType As Integer, sFullPath As String) As Boolean
In Adobe's acrobatsdk_iacguide.pdf, description of Parameter nType is a little bit confusing. Not sure what integer is for PDSaveFull
nType is a logical OR of one or more of the following flags: PDSaveIncremental — Write changes only, not the complete file. This
will always result in a larger file, even if objects have been
deleted.
PDSaveFull — Write the entire file to the filename specified by
szFullPath.
PDSaveCopy — Write a copy of the file into the file specified by
szFullPath, but keep using the old file. This flag can only be
specified if PDSaveFull is also used.
PDSaveCollectGarbage — Remove unreferenced objects; this often reduces
the file size, and its usage is encouraged. This flag can only be
specified if PDSaveFull is also used.
PDSaveLinearized — Save the file optimized for the web, providing hint
tables. This allows the PDF file to be byte-served. This flag can only
be specified if PDSaveFull is also used.

Multiplatform Windows and MAC computer indentify

is there any multiplatform method to identify user account, system or entire computer? Something like:
CreateObject("Scripting.FileSystemObject").GetDrive("C:\").SerialNumber
It may use some combinations of username and other variables to build (quite) unique ID text/number. Maybe should I use Environ? But where can I find complete list multiplatform Environ working variables? It may use OS identification also.
EDIT some starting code to clarify my question
Sub GenerateID()
#If Mac Then
'------- Mac ---------------
MsgBox MacIdNumber
#Else
'------- Windows -----------
MsgBox WindowsIdNumber
#End If
End Sub
Function MacIdNumber() As Long
Dim str1, str2, str3 ' str4, str5...
str1 = VBA.Environ$("USER")
str2 = Application.Version
str3 = Application.OperatingSystem
' and here I am looking for another ideas
' str4 = ...???
MacIdNumber = CreateUniqueId(str1, str2, str3)
End Function
Function WindowsIdNumber() As Long
Dim str1, str2, str3, str4 ', str5...
str1 = CreateObject("Scripting.FileSystemObject").GetDrive("C:\").SerialNumber
str2 = VBA.Environ$("USERNAME")
str3 = Application.Version
str4 = Application.OperatingSystem
' and here I am looking for another ideas
' str5 = ...???
' but maybe for Windows disc SerialNumber is enough
WindowsIdNumber = CreateUniqueId(str1, str2, str3, str4)
End Function
Function CreateUniqueId(ParamArray str() As Variant) As Long
' here I'll make checksum
' some calculations...
End Function
I am trying to get as unique ID strings as possible (but I know it probably won't be 100% unique).
It must work in reliable way for MAC 2016 and higher and Windows versions of course. So If you give an idea with apple script- I must be sure that no endless loop/ error occurs.
I am Windows user and I am not able to test it on MAC right now.
Is this what you are trying? tested it on both Excel 2016 (Mac/Windows)
Option Explicit
Sub GenerateID()
#If Mac Then
'------- Mac ---------------
MsgBox GetUniqueID("Mac")
#Else
'------- Windows -----------
MsgBox GetUniqueID("Win")
#End If
End Sub
Private Function GetUniqueID(sys As String) As String
Select Case sys
Case "Mac"
'~~> Ref: http://www.rondebruin.nl/mac/mac003.htm
Dim AppleScript As String
AppleScript = "set uuid to do shell script ""system_profiler SPHardwareDataType" & _
" | awk '/Hardware UUID:/ {print $NF}'"""
On Error Resume Next
GetUniqueID = MacScript(AppleScript)
On Error GoTo 0
Case "Win"
GetUniqueID = CreateObject("Scripting.FileSystemObject").GetDrive("C:\").SerialNumber
End Select
End Function
Screenshot
Looks like the answer is "no".
According to https://learn.microsoft.com/en-us/office/vba/api/overview/office-mac
Office 2016 for Mac is sandboxed
Unlike other versions of Office apps that support VBA, Office 2016 for
Mac apps are sandboxed.
Sandboxing restricts the apps from accessing resources outside the app
container. This affects any add-ins or macros that involve file access
or communication across processes. You can minimize the effects of
sandboxing by using the new commands described in the following
section.
What this says to me is that you are going to have to use a different, native, scripting language to get the information you want in to Excel.

SHA512 hash in Excel

i have a code for generating SHA512 hash of a string in excel, but on some notebook, it runs into error thanks to a net framework related problem. I have came accross a solution recently; when I open the "Me" expression in the locals window, the code can run without any error.
Locals:
Is there any chance to insert an additional code which can expand the "Me" expression in locals when i start running my VBA code in excel?
Sub sha512_kodolas()
Sheets("a").Range("a1").Value = h512(Sheets("b").Range("c1").Value)
End Sub
Function h512(ByVal S As String) As String
'https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha512managed.aspx
Static UTF8 As Object, SHA As Object
Dim Data, Temp, i As Long
If SHA Is Nothing Then
Set UTF8 = CreateObject("System.Text.UTF8Encoding")
Set SHA = CreateObject("System.Security.Cryptography.SHA512Managed")
End If
Data = SHA.ComputeHash_2(UTF8.GetBytes_4(S))
ReDim Temp(LBound(Data) To UBound(Data)) As String
For i = LBound(Data) To UBound(Data)
Temp(i) = Right$("0" & Hex(Data(i)), 2)
Next
h512 = Join(Temp, "")
End Function
could you try this code: https://github.com/krijnsent/crypto_vba -> download the excel and try the ComputeHash_C function? It's also carrying out the SHA512 encryption, but in a slightly different way from yours, so I'm curious to know if it also crashes?
I tried the suggestion from the author above and it works like a charm: "could you try this code: https://github.com/krijnsent/crypto_vba -> download the excel and try the ComputeHash_C function? It's also carrying out the SHA512 encryption, but in a slightly different way from yours, so I'm curious to know if it also crashes?".
Simply clone the Github project, copy the following functions into your Excel workbook:
Function ComputeHash_C(Meth As String, ByVal clearText As String, ByVal key As String, Optional OutType As String) As Variant
Function ConvToBase64String(vIn As Variant) As Variant
Function ConvToHexString(vIn As Variant) As Variant
Function Base64Decode(ByVal base64String)
Function Base64Encode(inData)
It works perfectly well! Thanks for sharing this code!
Using the Excel file from https://github.com/krijnsent/crypto_vba, it works for me if I use commas rather than semicolons in the formula: =ComputeHash_C("SHA512",A3,"","STRHEX"). This is just a small variation on what Luc Krolls has posted.

Editing "text connection" paths

I have an Excel file that pulls data from multiple CSV files via the "Connections" menu. The problem I'm running into is I need to be able to change the path to the CSV files from within VBA.
After repeated Binging (that's almost a bad word) I came across some solutions, but they involve an SQL connection rather than a text connection. Since the files are CSV, Excel makes it a text connection, and thus there is no ODBC connection string to modify (I get an error when trying to modify it from VBA). I've also dug through the MSDN docs to no avail.
Does anyone know of a way to change a "Text" connection path in Excel, from within VBA?
Also, since I'm on the topic, is it possible to have relative paths to files as opposed to the full file path (such as "\data\some_report.csv" rather than "c:\somedir\data\some_report.csv")?
As you mentioned...
I came across some solutions, but they involve an SQL connection rather than a text connection.
So use .TextConnection.Connection instead of .ODBCConnection.Connection :)
Here is a quick example. Please amend it as applicable.
Sub Sample()
Dim Conn As Variant
Dim ConString As String
Dim oldPath As String, NewPath As String
NewPath = "C:\MyPath.Csv"
Set Conn = ActiveWorkbook.Connections.Item(1)
Debug.Print Conn.TextConnection.Connection
'==> TEXT;C:\Users\Siddharth\Desktop\Delete Later\Output.csv
ConString = Conn.TextConnection.Connection
oldPath = Split(ConString, ";")(1)
ConString = Replace(ConString, oldPath, NewPath)
Conn.TextConnection.Connection = ConString
End Sub

Unable to create simple VBA function in Excel Macro and call it

I'm trying to create a macro that can collect data from an excel spreadsheet in the local active workbook and then create header file which I would later incorporate into my project. But for the life of me I must be missing something so DUMB that I can't create a working function that returns a string (which would construct a C++ structure) to the calling function. I've simplified the example code to is absolute bare minimum to isolate the problem but I still cannot figure out what I'm doing wrong. I'm not an expert at VBA but I know how to create code and I can't narrow down what VBA is unhappy about. I keep getting "compile error, syntax error." Please copy the following code into your module and see if compiles properly for you. If you know where I went wrong please let me know. Much Appreciated!!!
Sub CREATE_FACTORY_SETTING_HEADER()
Dim FS, TSsource
Set FS = CreateObject("Scripting.FileSystemObject")
Dim TSout
Set TSout = FS.Createtextfile("HeaderFile.h", True)
Dim fileHeading As String
fileHeading = "File Heading for Header file"
Dim fileBody As String
fileBody = "Some initial file body lines"
fileBody = fileBody & createStructBody
TSout.Write fileHeading & fileBody
TSout.Close
End Sub
Public Function createStructBody() As String
Dim structBody As String
structBody = "Hey I'm a struct body, but I can't be returned for some reason"
Return structBody
End Function
Both VBA and VBScript use 'assignment to function' (instead of 'return' or 'result of last statement') to return results from functions. So
Public Function createStructBody() As String
createStructBody = "Hey I'm a string and can be returned."
End Function

Resources