System.Security.Cryptography.HMACSHA1 is Com visible. How should I call ComputeHash? - vba

I want to create an HMAC SHA1 signature of some text in Microsoft Access
The .net object System.Security.Cryptography.HMACSHA1 is com visible, so I thought I could use that.
I can create the object and set the key, but when I come to call the compute function to get the hash, I am getting the error run time error 5 "Invalid Procedure Call or Argument"
Public Function sign(Key As String, Message As String)
Dim asc, enc
Dim MessageBytes() As bytes, HashBytes() As bytes
Set asc = CreateObject("System.Text.UTF8Encoding")
Set enc = CreateObject("System.Security.Cryptography.HMACSHA1")
enc.Key = asc.GetBytes_4(Key)
MessageBytes = asc.GetBytes_4(Message)
HashBytes = enc.ComputeHash(MessageBytes)
sign = EncodeBase64(HashBytes)
Set asc = Nothing
Set enc = Nothing
End Function
I copied the Encoding bit from the web and I note there that GetBytes has transmuted into GetBytes_4. Is this name mangling? and do I need to do something similar to ComputeHash? and if so what (I tried _n where n = 1 to 6 to no avail).
If not What am I doing wrong?

I found the answer to my question in this question
Base64 HMAC SHA1 String in VBA
I thought I had tried enc.ComputeHash_2, but maybe I didn't because it seems to work

Related

How do I compute HMACSHA256 in an Open Office VBasic Script?

Admittely I understand that I'm using a VBA script, but I cannot find information to confirm that this specific call in question is not specifically supported for OpenOffice. I currently have a ComputeHMACSha256 function that seems to be referenced several places online for computing Sha 256 hashes with a key string (albeit with Excel, NOT Open Office). However, when I attempt to run ComputeHash or the overload ComputeHash_2, I get this error:
I've got NET.2 and 3 installed and configured from what I can see in the Turn Windows feature screen:
And here is the code I'm using, taken from here:
Function CommandButton1_Click()
MsgBox("Hello World")
call UsageExample()
Call makeJWT()
End Function
Function makeJWT()
rsString = "{'alg': 'RS256','typ': 'JWT'}"
res = ComputeHMACSHA256("test", rsString)
MsgBox(res)
End Function
Function ComputeHMACSHA256(rsaKey As String, body As String) As String
Dim encoder As Object, crypto As Object
Dim hash() As Byte, hmacsha As String, i As Long
' compute HMACSHA256
Set encoder = CreateObject("System.Text.UTF8Encoding")
Set crypto = CreateObject("System.Security.Cryptography.HMACSHA256")
crypto.Key = encoder.GetBytes_4(rsaKey)
hash = crypto.ComputeHash_2(encoder.GetBytes_4(body))
' convert to an hexa string
hmacsha = String(64, "0")
For i = 0 To 31
Mid$(hmacsha, i + i + (hash(i) > 15) + 2) = Hex(hash(i))
Next
ComputeHMACSHA256 = LCase(hmacsha)
End Function

Create SHA256 COM Object in Access VBA

I'm trying to create a SHA256 object from MS Access VBA.
I'm running Access 2016 on a Windows machine with .NET 4.8.
Public Function Base64_HMACSHA256(ByVal sTextToHash As String, ByVal sSharedSecretKey As String) As String
Dim asc As Object, enc As Object
Dim TextToHash() As Byte
Dim SharedSecretKey() As Byte
Set asc = CreateObject("System.Text.UTF8Encoding")
'Set enc = CreateObject("System.Security.Cryptography.HMACSHA256") 'THIS SUCCESSFULLY CREATES THE OBJECT
'Set enc = CreateObject("System.Security.Cryptography.SHA256") 'IHF 02/03/22 'CAN'T CREATE OBJECT
'Set enc = CreateObject("System.Security.Cryptography.SHA256CryptoServiceProvider") 'IHF 02/03/22 'CAN'T CREATE OBJECT
'Set enc = CreateObject("System.Security.Cryptography.RSACryptoServiceProvider") 'CAN'T CREATE OBJECT
TextToHash = asc.Getbytes_4(sTextToHash)
SharedSecretKey = asc.Getbytes_4(sSharedSecretKey)
enc.Key = SharedSecretKey
Dim bytes() As Byte
bytes = enc.ComputeHash_2((TextToHash))
Base64_HMACSHA256 = EncodeBase64(bytes)
Set asc = Nothing
Set enc = Nothing
End Function
I ended up doing it all a totally different way, so I never figured this out.
The appropriate class, as noted in the comments, is System.Security.Cryptography.SHA256Managed. Wikibooks has an example.
However, I prefer using the WinAPI CNG api directly (docs). This has some flexibility, performance and security advant.ages. See an example

Hashing or Encoding functions in VB.NET

I have the following functions in a VB script:
Function DecryptSHA1(ByVal strOriginalString As String) As String
If strOriginalString = "" Then Return ""
Dim dEC_data() As Byte = Convert.FromBase64String(strOriginalString)
Dim dEC_Str As String = ASCIIEncoding.ASCII.GetString(dEC_data)
DecryptSHA1 = dEC_Str
End Function
Function EncryptSHA1(ByVal strOriginalString As String) As String
If strOriginalString = "" Then Return ""
Dim shaM As New SHA1Managed
Convert.ToBase64String(shaM.ComputeHash(Encoding.ASCII.GetBytes(strOriginalString)))
Dim eNC_data() As Byte = ASCIIEncoding.ASCII.GetBytes(strOriginalString)
Dim eNC_str As String = Convert.ToBase64String(eNC_data)
EncryptSHA1 = eNC_str
End Function
I don't code in VB but as much as I can see the functions have nothing to do with encrypting SHA1 but rater Base64 encoding and decoding. Am I wrong? The only thing that can make me think about hashing is the Dim shaM As New SHA1Managed line in the EncryptSHA1() function but the DecryptSHA1() function doesn't use it anymore. Are these simple Base64 encode/decode functions or more?
The only thing these functions do is converting to and from base64.
In the EncryptSHA1 function there is some SHA1 hashing code, but it is not actually used.
So there is no hashing nor encryption going on which makes the functions quite confusing.

VB .NET - Length of the data to decrypt is invalid

I'm trying to do an exercise about encryption and decryption. I already have the following working code to encrypt (I know that ECB is bad, I won't use it in real life, I promise):
Dim hashmd5 As MD5CryptoServiceProvider
Dim des As TripleDESCryptoServiceProvider
Dim keyhash As Byte()
Dim buff As Byte()
Try
hashmd5 = New MD5CryptoServiceProvider
keyhash = hashmd5.ComputeHash(ASCIIEncoding.ASCII.GetBytes("exe67rci89"))
des = New TripleDESCryptoServiceProvider
des.Mode = CipherMode.ECB
des.Key = keyhash
buff = ASCIIEncoding.ASCII.GetBytes(Me.txtPwd.Text)
Me.txtPwd.Text = Convert.ToBase64String(des.CreateEncryptor().TransformFinalBlock(buff, 0, buff.Length))
Catch ex As System.Exception
Throw ex
End Try
Now I'm trying to decrypt the output of this function. I tried to use the same code, just substituting the encryption line with:
Me.txtPwd.Text = Convert.ToBase64String(des.CreateDecryptor().TransformFinalBlock(buff, 0, buff.Length))
But it doesn't work, because I receive the error "Length of the data to decrypt is invalid". I already tried many solutions found here on stack overflow, but no one works. Can someone help me to solve this problem? Thanks in advance!
You're not reversing enough of the transformation. What's now in txtPwd is a base-64 string, not a plain string to be converted using the ASCII encoding. And conversely, what you're going to produce in decryption are bytes that should be interpreted as ASCII characters.
So what you want is:
buff = Convert.FromBase64String(Me.txtPwd.Text)
Me.txtPwd.Text = ASCIIEncoding.ASCII.GetString(des.CreateDecryptor().TransformFinalBlock(buff, 0, buff.Length))
What you're trying to decrypted isn't exactly the same as what came out of the actual encrypt:
In your encrypt you convert the encrypted bytes to a base 64 string using Convert.ToBase64String
You need to convert the string in the txtPwd field back into the byte array that was the result of the original encryption. See the matching Convert.FromBase64String method.
You might be able to view what I mean better if you split the original encrypt line up:
dim encBytes = des.CreateEncryptor().TransformFinalBlock(buff, 0, buff.Length)
Me.txtPwd.Text = Convert.ToBase64String(encBytes)
This way you can see the actual encrypt results, and validate that what you pass in is the same.

SSRS Decrypting database column throws error "Value cannot be null. Parameter name: inputBuffer"

We have a database field that is encrypted in a C# MVC app using Entity Framework. That part works fine for encrypting and decrypting. We need to show this data decrypted in an SSRS report.
I've taken the Decrypt method and converted it to VB.Net and placed in the code behind of the report. The error occurs when calling the StreamReader's ReadToEnd method. We've found some references online about there being an issue trying to close a stream that's already closed. This stream is only being read from one time and we're reading the entire stream so it should not be closed at that point.
I tried reading Length - 1 bytes from the cryptoStream and that did not throw an error. If I try to read Length bytes, I get the same error. If I read all but one byte and then read only the last byte I get the same error. Stack trace and code are below. Any help would be greatly appreciated.
Stacktrace:
at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at ReportExprHostImpl.CustomCodeProxy.Decrypt(String ciphertext)
Code:
public function Decrypt(byval ciphertext as string) as string
dim message as string
dim key as string
dim saltSize as integer
saltsize = 32
key = "KEYGOESHERE"
dim allTheBytes as byte()
allTheBytes = Convert.FromBase64String(ciphertext)
dim saltBytes as byte()
saltBytes = new byte(saltsize) {}
dim ciphertextBytes as byte()
ciphertextBytes = new byte(allTheBytes.Length - saltSize) {}
dim i as integer
for i = 0 to saltsize - 1
saltBytes(i) = allTheBytes(i)
next i
for i = saltSize to (allTheBytes.Length - 1)
ciphertextBytes(i - saltSize) = allTheBytes(i)
next i
try
using keyDerivationFunction as System.Security.Cryptography.Rfc2898DeriveBytes = new System.Security.Cryptography.Rfc2898DeriveBytes(key, saltBytes)
dim keyBytes as byte()
keyBytes = keyDerivationFunction.GetBytes(32)
dim ivBytes as byte()
ivBytes = keyDerivationFunction.GetBytes(16)
using aesManaged as System.Security.Cryptography.AesManaged = new System.Security.Cryptography.AesManaged()
aesManaged.Padding = System.Security.Cryptography.PaddingMode.PKCS7
aesManaged.Mode = System.Security.Cryptography.CipherMode.CBC
using decryptor as System.Security.Cryptography.ICryptoTransform = aesManaged.CreateDecryptor(keyBytes, ivBytes)
using memoryStream as System.IO.MemoryStream = new System.IO.MemoryStream(ciphertextBytes)
using cryptoStream as System.Security.Cryptography.CryptoStream = new System.Security.Cryptography.CryptoStream(memoryStream, decryptor, System.Security.Cryptography.CryptoStreamMode.Read)
using streamReader as System.IO.StreamReader = new System.IO.StreamReader(cryptoStream)
message = streamReader.ReadToEnd()
return message
end using
end using
end using
end using
end using
end using
catch e as Exception
return e.StackTrace
end try
end function
This sounds like a padding error. First of all, AES in CBC mode requires an exact message size for decryption, N times the blocksize (of 16 bytes). If the message has this size then AES CBC ciphertext will always decrypt. Any other problem can only be related to padding really. So what happens is that the last deciphered plain text does not have the correct padding, and an exception occurs.
Now comes the fun part; this error occurs when:
the binary AES key is not identical;
if the algorithm or mode is incorrect or if the wrong padding algorithm is used;
if one of the last two blocks is mangled;
if the last part of the encryption is missing in action.
In other words, if something is wrong, it is likely that it will turn up during the removal of the padding bytes. If you are unlucky, the last incorrect block still ends with a byte value of 01 hexadecimal, and padding succeeds, showing that padding is no substitution for integrity protection.