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.
Related
Our system use Access VBA function Chr() to get ASCII code to generate barcode. All of computers (include Windows 10, Windows 8 and Windows 7) result of Chr() is correct while one of Windows 8.1 result is different.
For example
All computers return of Chr(209) is “Ñ” which is correct. Return of Ch(65) is “A” which is correct.
One computer return of Chr(209) is “?” which is incorrect. Return of Ch(65) is “A” which is correct.
I try to use ChrW and ChrB. Both still do not return proper ASCII. Not sure why that computer return is different. Test results from computers and seems that computer cannot return correct ASCII for 128~256 codes.
ASCII codes were supposed to be a defined standard but that standard keeps changing. Depending on your region or your software versions you could have a mismatch in what you would expect to be returned. This is why you will see items like <?xml version=“1.0” encoding=“utf-8”?>. This tells the rest of the devices reading it the base coding that you are using.
In regards to MSACCESS I have not read how to get around this by setting the standard you wish to use. You can use the STRCONV() function to bring your code into the standard you wish.
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/strconv-function
More reading:
What is ANSI format?
How to convert MS Access database encoding to UTF-8?
For a list of all the ASCII standards please see:
http://www.iana.org/assignments/character-sets/character-sets.xhtml
From Jim L answer and searched more info, I found the problem/solution. Return code will be correct, if I change System Locale to English.
Step to change system Locale
1. Control Panel
2. Region and Language
3. Administrative
4. Change System Locale to English
5. Restart computer
This is a solution. But, I cannot control each computer Locale and I cannot assume each computer Locale is English.
I have implemented this http://jacob.jkrall.net/totp/ in vbscript.
My code given the same hex gives the right 6-digit otp, so that part is working.
I've also verified the HMAC-SHA-1. encoding against an online generator, http://www.freeformatter.com/hmac-generator.html#ad-output, same input gives same output.
My time is the same as http://www.currenttimestamp.com/
I've generated a qrcode at http://www.qr-koder.dk/ with the string otpauth://totp/$LABEL?secret=$SECRET and the google authenticator app reads the code and starts outputting the 6 digit code changing every 30 seconds.
BUT THE CODES FROM THE APP DOES NOT MATCH THE 6-DIGIT CODE THE VBSCRIPT GENERATES!
I've even tried trunc(time/30) +/-7500 steps to see if it was a timezone/daylight saving problem, to no avail.
As the other parts of the routine to generate the 6 digits seem to work I've come to the conclusion I don't understand this:
the url on the qr-code is
otpauth://totp/$LABEL?secret=$SECRET
with the explanation
LABEL can be used to describe the key in your app, while SECRET is the
16-character base32-encoded shared secret, which is now known to both
the client and the server.
So when I calculate HMAC-SHA-1(SECRET, time()/30)
should the SECRET be the same string given to both the app and the calculation?
If I select a secret of 1234567890, the base32 is GEZDGNBVGY3TQOJQ according to http://emn178.github.io/online-tools/base32_encode.html.
Should I then take
HMAC-SHA-1("1234567890", time()/30)
or
HMAC-SHA-1("GEZDGNBVGY3TQOJQ", time()/30)
?
I believe I've tried both, and neither works.
The system unix time is correct.
I guess the problem might be with the secret in your HMAC-SHA-1 function. It very much depends on what the HMAC-SHA-1 expects.
Your string "123456790" might be a binary string. Is it an ascii representation or utf8? I.e. is this string 10 bytes or 20 bytes long?
I recommend getting the input string in your VBScript right.
On the other hand, instead of writing your own VBScript, you can also use a ready made solution like the privacyIDEA authentication server, which is open source and also comes with TOTP.
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)
I have following sample code
Dim q As Char = "a"
Dim res As String = CStr(AscW(q))
res contains correctly "97" but when I use AscW(q) in watch or immediate it returns message: Cannot convert to 'Integer'.
Is it a bug or Visual Studio or am I doing something not correctly?
How can I see a character code in Immediate.
Note
The code presented is just an example. I found the problem when trying to see Character code in the Watch Window.
For a workaround, how about the command
? System.Text.Encoding.Unicode.GetBytes(q)
I personally believe that any acceptable VB.Net code should be acceptable in the Immediate window and really don't understand why AscW is causing errors when VB.Net offers no equivalent (e.g. in C#, but not VB.Net, you can cast a Char variable to an Integer to get the character code).
You are doing everything right (and the outputs will be OK in any case), although you are using old VB code. If you need functionalities like AscW (, Asc, ChrW, etc.), you would have to rely on this "old code" to get what you want (directly or via Microsoft.VisualBasic.Strings which, btw, does not show a different behaviour). But, in any other case, you should avoid the utilisation of this old code.
Test these two lines in the Immediate Window:
Dim res As String = CStr(5)
res = 5.ToString()
As you can see, you get an "error" (VS 2010, right-click on the line and select "QuickWatch") in the first line (old version), but not in the second one (.NET version).
Thus, the behaviour you observed can be considered as an inoffensive bug (no real effects in the execution) more or less understandable if you analyse the situation (you are asking a certain language (VB.NET) to support all its own features and the ones from an old language (VB); with the old one, some secondary functionalities might not be perfect).
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.