Calling .NET Cryptography from VBA - vba

I am trying to generate an SHA-1 hash in VBA (which I am not very familiar with). In .NET it is pretty straightforward, using System.Security.Cryptography. One method is as follows:
using System.Security.Cryptography;
...
byte[] data;
// fill data here
SHA1Managed sha = new SHA1Managed();
bytes[] hash = sha.ComputeHash(data);
How would you go about calling this from VBA? I got as far as this
Dim oSHA As Object
Set oSHA = CreateObject("System.Security.Cryptography.SHA1Managed")
but I can't figure out how to pass a Byte array to ComputeHash
hash = oSHA.ComputeHash(oBytes)
This throws an error about the parameter being incorrect. How do you convert it to a format accepted by .NET?
Note that I do NOT want to use VBA calculations for SHA-1 (too slow, and I don't want to reinvent the wheel). It would also be nice if I didn't have to write any wrappers for the .NET portion.

From System.Security.Cryptography.HashAlgorithm:
ComputeHash_1(inputStream As stream)
ComputeHash_2(buffer As byte())
ComputeHash_3(buffer As byte(), offset As int, count As int)

ComputeHash is overloaded, you need to specify which one you want to call, in this case:
hash = oSHA.ComputeHash_2(bytes)

Related

Import txt file data and store it in Multidimensioanl array in Vb.net

Sorry if the problem is so basic, I'm a bit used to python not VB.net
I'm trying to read text file data (numbers) and store it in array/list
# Sample of text
1.30e+03,1.30e+03,1.30e+03
5.4600e+02,2.7700e+02,2.8000e+02
# PS: I can control the output of the numbers to have delimiter = ',' or space between numbers, whatever is easier to import
I wrote the following code to read string data and store it. yet, I don't know how to have a multidimensional array (2D or 3D) instead of 1D string (e.g. for the text above, it would be 2x3 array)
' Import Data
Comp_path = FinalPath & "Components_colors.txt"
reader = New StreamReader(Comp_path)
Dim W As String = ""
Dim wArray(10) As String
Dim i As Integer = 0
Do Until reader.Peek = -1
W = reader.ReadLine()
wArray(i) = W
i += 1
Loop
Moreover, I don't know the length of the text file, so I can't determine the length of the array like I did in the code above for the string wArray
For a file like this, you should turn to NuGet for a dedicated CSV parser. There are parsers built into .NET you could also use, but pulling one off of NuGet will also let you parse the values directly into something other than a string.
But if you really don't want to do that you can start with this (assuming Option Infer):
Public Function ImportData(filePath As String) As IEnumerable(Of Double())
Dim lines = File.ReadLines(filePath)
Return lines.Select(Function(line) line.Split(",").Select(AddressOf Double.Parse).ToArray())
End Function
And use it like this:
Comp_path = FinalPath & "Components_colors.txt"
Dim result = ImportData(Comp_path)
Note this code doesn't actually do any meaningful work yet. It doesn't even read the file. What it does is give you an object (result) that you can use with a For Each loop or linq operations. It will read the file in a just-in-time way, parsing out the data for each line as it goes. If you want an array (or List, which you should use in .Net more often), you can append a ToList() call to the end:
Comp_path = FinalPath & "Components_colors.txt"
Dim result = ImportData(Comp_path).ToList()
But you should try to avoid doing that it. It's much less efficient in terms of memory use. The first sample will only ever need to keep one line of the file in memory at a time. Adding ToArray() or ToList() needs to load the entire file.
Some more notes:
Many newer dynamic platforms like Python don't actually use real arrays in the formal computer science sense (fixed block of contiguous memory). Rather, they use collections, and just call them arrays. .Net has collections, too, but when you declare an array, you get an array. This has nice benefits for performance, but if you don't know you want that or how to take advantage of it you're probably better off asking for a generic List most of the time instead.
Thanks to cultural/internationalization issues, parsing numeric (or date) values to string and back again is much slower and more error-prone than you've believed in the past, especially coming from a dynamic platform. It is slow on these other platforms, too, but they want you to pretend it isn't. The first introduction to a strongly-typed platform like .Net can feel stifling in this area, but once you understand the performance and and reliability benefits, you won't want to go back.
In strongly-typed platforms it is very important to understand the data types you are working with at every level of an expression. Otherwise, building and reading statements like the Return line in my answer will be way more difficult and frustrating than it needs to be.

Equivalent of "Dim As String * 1" VB6 to VB.NET

I have some VB6 code that needs to be migrated to VB.NET, and I wanted to inquire about this line of code, and see if there is a way to implement it in .NET
Dim strChar1 As String * 1
Intellisense keeps telling me that an end of statement is expected.
That's known as a "fixed-length" string. There isn't an exact equivalent in VB.NET.
Edit: Well, OK, there's VBFixedStringAttribute, but I'm pretty sure that exists solely so that automated migration tools can more easily convert VB6 code to VB.NET for you, and it's not really the ".NET way" to do things. Also see the warnings in the article for details on why this still isn't exactly the same thing as a fixed-length string in VB6.
Generally, fixed-length strings are only used in VB6 if you are reading fixed-size records from a file or over the network (i.e. parsing headers in a protocol frame).
For example, you might have a file that contains a set of fixed-length records that all have the format (integer, 1-character-string, double), which you could represent in VB6 as a user-defined type:
Public Type Record
anInteger As Integer
aSingleCharacter As String * 1
aDouble As Double
End Type
This way, VB6 code that reads from the file containing records in this format can read each fixed-sized record stored in the file, and in particular, it will only read 1 byte for aSingleCharacter. Without the * 1, VB6 would have no idea how many characters to read from the file, since a String can normally have any number of characters.
In VB.NET, you can do one of the following, depending on your needs:
If the length matters (i.e. you need to read exactly one byte from some data source, for example) consider using an array instead, such as
Dim aSingleByteArray(1) As Byte
Alternatively, you could use one of the Stream classes. In particular, if you are reading data from a network socket or a file, consider using NetworkStream or FileStream, respectively. A Stream is meant for byte-for-byte access (i.e. raw binary access). StreamReader is a related class that simplifies reading data when it is text-based, so that might be good if you are reading a text file, for example. Otherwise (if dealing with binary data), stick with one of the Stream classes.
If the length doesn't matter, you could just use a "normal" String. That is to say:
Dim aNormalString As String
Which answer is "correct" really depends on why it was declared that way in the original VB6 code.
The fixed length strings has been deprecated in VB.NET because there are several better options.
Since your fixed length string is just one character long, you can use the Char type in this case, as Mark suggested.
Dim strChar1 As Char
Seeing as you're doing a VB6 migration, I'd definitely consider VBFixedStringAttribute as well as the other options listed by Mike Spross, but, in this case, because it is a single character, a Char may be an option in this case too.
As mentioned elsewhere VBFixedString is only acknowledged by the Get and Put VB I/O API. So the best solution (other than rewriting your code that references the "fixed length string") is to write your own equivalent of Microsoft.VisualBasic.Compatibility.VB6.FixedLengthString. See this answer for more details.
VBFixedStringAttribute Class
<VBFixedString(1)> Dim strChar1 As String
ALthough this question was asked ages ago, VB.NET actually has a native fixed-length string -- <VbFixedArray(9)> Public fxdString As Char() 'declare 10-char fixed array. Doing this with scalars actually creates VB6-style Static Arrays.

Save array to file

I have a string array that is populated with thousands of entries. I am wanting the quickest way to save this data to disk and to also load it back up.
Currently I am looping through the array and appending this data to a string and then saving this string. This takes a long time.
What is the fastest/most efficient way to do this?
Thanks
Write
Dim FileName as string=Application.StartupPath & "\myarray.txt"
IO.File.WriteAllLines(FileName,myarray)
Read
Dim FileName as string=Application.StartupPath & "\myarray.txt"
Dim myarray() As String = File.ReadAllLines(FileName)
How important is it that the resulting file be human-readable? If it's mostly there to be read later by the program, you should definitely use serialization:
public void SaveArray (string[] array)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
using (FileStream file = System.IO.File.Create(_filename))
{
serializer.Serialize(file, array);
}
}
Edit:
To summarize the advantages of this method over the other two suggested below, the first being using a StringBuilder to concatenate a big string and then saving it to disk, and the second using WriteAllLines to write the string array to disk:
The first method will completely lose fidelity to the original list of strings, since it will glob them together into one string. Can you separate them later? Are there well-known delimiters between them? Not necessarily. It's more efficient than just calling + between them, but it's not a good way to store them.
The second method is better, since each string will be saved to a different line, and can be read back with ReadAllLines, but you're still on shaky ground here. What happens if any string contains the NewLine character? It will be one string when written, but two different strings when read back. Again, calling WriteAllLines/ReadAllLines relies on newlines being good delimiters, when that's not necessarily the case.
Using a serializer - and it doesn't matter if it's the NetDataContractSerializer, BinaryFormatter or any other - will maintain full fidelity with your original data structure, with only a little bit of overhead.
Try using StringBuilder
See the msdn reference for more
Why stringbuilder?
When dealing with large files. It is advisible to use the StringBuilder class rather than the regular string class.
Appending strings together, requires the old string to be copied into a new string.
String Builders are buffers that can change size easily and you can append and delete faster.
You should loop through your data using a string builder which is much faster than ordinary string.
then create a stream writer/ reader (when you want to load the file) from IO library and create the file on your disk.

How do I save a Byte Array to a Database in vb.net

I have a byte array of a file and I need to save it into my database in a field that has been set aside of type image.
However I have a problem my data access class takes a sql string and commits it to the database for example.
"EXECUTE stored proc #parm1, #parm2, #parm3"
However the problem is I cannot figure out how to transfer the byte array to string so that I can add it as an argument.
I hope this make sense.
I also understand that I can build parameters in com objects but I do not want to do this as it will disrupt my whole data access class and I am not prepared to do this at the moment.
Thanks for any help.
In SQL statements, you can use the hexadecimal notation "0x1323235..." to represent binary data but it's not really a good way to deal with it. You should be using parameters:
sqlCmd.Parameters.AddWithValue("#parameterName", byteArrayInstance)
Answering question of how to burn byte array into a string.
Convert the byte array to a string
byte[] b = new byte[100];
string s = System.Text.ASCIIEncoding.ASCII.GetString(b);

MD5CryptoServiceProvider ComputeHash Issues between VS 2003 and VS 2008

I have a database application that generates a MD5 hash and compares the hash value to a value in our DB (SQL 2K). The original application was written in Visual Studio 2003 and a deployed version has been working for years.
Recently, some new machines on the .NET framework 3.5 have been having unrelated issues with our runtime. This has forced us to port our code path from Visual Studio 2003 to Visual Studio 2008.
Since that time the hash produced by the code is different than the values in the database.
The original call to the function posted in code is:
RemoveInvalidPasswordCharactersFromHashedPassword(Text_Scrub(GenerateMD5Hash(strPSW)))
I am looking for expert guidance as to whether or not the MD5 methods have changed since VS 2K3 (causing this point of failure), or where other possible problems may be originating from.
I realize this may not be the best method to hash, but utimately any changes to the MD5 code would force us to change some 300 values in our DB table and would cost us a lot of time. In addition, I am trying to avoid having to redeploy all of the functioning versions of this application.
I am more than happy to post other code including the RemoveInvalidPasswordCharactersFromHashedPassword function, or our Text_Scrub if it is necessary to recieve appropriate feedback.
Thank you in advance for your input.
Public Function GenerateMD5Hash(ByVal strInput As String) As String
Dim md5Provider As MD5
' generate bytes for the input string
Dim inputData() As Byte = ASCIIEncoding.ASCII.GetBytes(strInput)
' compute MD5 hash
md5Provider = New MD5CryptoServiceProvider
Dim hashResult() As Byte = md5Provider.ComputeHash(inputData)
Return ASCIIEncoding.ASCII.GetString(hashResult)
End Function
You are basically asking if the MD5 implementation in .Net 1.1 was broken.
I do not think so. I think the problem lies elsewhere.
I don't think the .Net MD5 hash code have changed in VisualStudio 2008.
But I think that:
Return ASCIIEncoding.ASCII.GetString(hashResult)
You are converting binary data to ASCII, and loosing characters, maybe the problem is in a new database driver. And probably you will need to change your stored values and start using a blob field or converting to base64 and using a text field.
Trying to be more productive than my comment ...
You could try using an independent md5 hashing algorithm to verify the encoding, there are some web based ones or use openssl.