How to generate MD5 using VBScript in classic ASP? - asp-classic

I need to generate an MD5 in my application.
I've tried google but only find PHP code for MD5. I need to connect to a client system that validates using MD5 hash but their code is in PHP, mine is in Classic ASP using VBScript.
My server is .Net supported so I cannot use the PHP script. Is there any such MD5 code for VBScript in Classic ASP?

Update 2017-02-21 - Now with added HMACSHA256 for JWTs
Update 2016-07-05 - Now with added SHA1 and SHA256
Right, for all of you who have been struggling with this (like myself) and want to know, it is possible!
The following code is split up into several functions so that you can either MD5/sha1/sha256 a string, or a file.
I borrowed the functions GetBytes and BytesToBase64 from another stackexchange, and the code within stringToUTFBytes is based on another stackexchange.
function md5hashBytes(aBytes)
Dim MD5
set MD5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
MD5.Initialize()
'Note you MUST use computehash_2 to get the correct version of this method, and the bytes MUST be double wrapped in brackets to ensure they get passed in correctly.
md5hashBytes = MD5.ComputeHash_2( (aBytes) )
end function
function sha1hashBytes(aBytes)
Dim sha1
set sha1 = CreateObject("System.Security.Cryptography.SHA1Managed")
sha1.Initialize()
'Note you MUST use computehash_2 to get the correct version of this method, and the bytes MUST be double wrapped in brackets to ensure they get passed in correctly.
sha1hashBytes = sha1.ComputeHash_2( (aBytes) )
end function
function sha256hashBytes(aBytes)
Dim sha256
set sha256 = CreateObject("System.Security.Cryptography.SHA256Managed")
sha256.Initialize()
'Note you MUST use computehash_2 to get the correct version of this method, and the bytes MUST be double wrapped in brackets to ensure they get passed in correctly.
sha256hashBytes = sha256.ComputeHash_2( (aBytes) )
end function
function sha256HMACBytes(aBytes, aKey)
Dim sha256
set sha256 = CreateObject("System.Security.Cryptography.HMACSHA256")
sha256.Initialize()
sha256.key=aKey
'Note you MUST use computehash_2 to get the correct version of this method, and the bytes MUST be double wrapped in brackets to ensure they get passed in correctly.
sha256HMACBytes = sha256.ComputeHash_2( (aBytes) )
end function
function stringToUTFBytes(aString)
Dim UTF8
Set UTF8 = CreateObject("System.Text.UTF8Encoding")
stringToUTFBytes = UTF8.GetBytes_4(aString)
end function
function bytesToHex(aBytes)
dim hexStr, x
for x=1 to lenb(aBytes)
hexStr= hex(ascb(midb( (aBytes),x,1)))
if len(hexStr)=1 then hexStr="0" & hexStr
bytesToHex=bytesToHex & hexStr
next
end function
Function BytesToBase64(varBytes)
With CreateObject("MSXML2.DomDocument").CreateElement("b64")
.dataType = "bin.base64"
.nodeTypedValue = varBytes
BytesToBase64 = .Text
End With
End Function
'Special version that produces the URLEncoded variant of Base64 used in JWTs.
Function BytesToBase64UrlEncode(varBytes)
With CreateObject("MSXML2.DomDocument").CreateElement("b64")
.dataType = "bin.base64"
.nodeTypedValue = varBytes
BytesToBase64UrlEncode = replace(replace(replace(replace(replace(.Text,chr(13),""),chr(10),""),"+", "-"),"/", "_"),"=", "")
End With
End Function
Function GetBytes(sPath)
With CreateObject("Adodb.Stream")
.Type = 1 ' adTypeBinary
.Open
.LoadFromFile sPath
.Position = 0
GetBytes = .Read
.Close
End With
End Function
These can be used as follows:
BytesToBase64(md5hashBytes(stringToUTFBytes("Hello World")))
Produces: sQqNsWTgdUEFt6mb5y4/5Q==
bytesToHex(md5hashBytes(stringToUTFBytes("Hello World")))
Produces: B10A8DB164E0754105B7A99BE72E3FE5
For SHA1:
bytesToHex(sha1hashBytes(stringToUTFBytes("Hello World")))
Produces: 0A4D55A8D778E5022FAB701977C5D840BBC486D0
For SHA256:
bytesToHex(sha256hashBytes(stringToUTFBytes("Hello World")))
Produces: A591A6D40BF420404A011733CFB7B190D62C65BF0BCDA32B57B277D9AD9F146E
To get the MD5 of a file (useful for Amazon S3 MD5 checking):
BytesToBase64(md5hashBytes(GetBytes(sPath)))
Where sPath is the path to the local file.
And finally, to create a JWT:
'define the JWT header, needs to be converted to UTF bytes:
aHead=stringToUTFBytes("{""alg"":""HS256"",""typ"":""JWT""}")
'define the JWT payload, again needs to be converted to UTF Bytes.
aPayload=stringToUTFBytes("{""sub"":""1234567890"",""name"":""John Doe"",""admin"":true}")
'Your shared key.
theKey="mySuperSecret"
aSigSource=stringToUTFBytes(BytesToBase64UrlEncode(aHead) & "." & BytesToBase64UrlEncode(aPayload))
'The full JWT correctly Base 64 URL encoded.
aJWT=BytesToBase64UrlEncode(aHead) & "." & BytesToBase64UrlEncode(aPayload) & "." & BytesToBase64UrlEncode(sha256HMACBytes(aSigSource,stringToUTFBytes(theKey)))
Which will produce the following valid JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.7ofvtkn0z_pTl6WcqRTxw-4eSE3NqcEq9_3ax0YcuIQ

Here is a readable and downloadable version of MD5 as VBS script:
https://github.com/Wikinaut/md5.vbs
It's the code from http://chayoung.tistory.com/entry/VBScript-MD5 (thank you for this unique piece of code).

Thanks for all the links provided above, they were useful but this one I found really did the job if anybody ever needs it.
VBScript-MD5

I have no idea if this code even works, since I have no way of testing it. However, it seems to be what you are asking for.
http://www.bullzip.com/md5/vb/md5-vb-class.htm
Here is an interesting article by Jeff Attwood on hashes. He has some important things to say about MD5:
http://www.codinghorror.com/blog/2012/04/speed-hashing.html

First of all, thank you SgtWilko! :)
Based on your collected information, I've done one function for all (not for base64/Files).
Your code was very useful for me, but I was searching for a more PHP alike (simple) Function to deal with plain text and with a more explicit code.
Edited:
Based on the issue How to hash a UTF-8 string in Classic ASP, I come up with the ADODB.Stream solution. You can now use non-English characters.
Edited:
Parameter PlainText was changed to Target.
You can now use the HMAC versions.
Just use the Target parameter as an array.
Target(0) = PlainText
Target(1) = SharedKey
Thank you again SgtWilko ;)
Announcing the first SHA1 collision (Google Security Blog) February 23, 2017.
With this function you can hash the plain text into:
MD5, RIPEMD160, SHA1, SHA256, SHA384, SHA512, HMACMD5, HMACRIPEMD160, HMACSHA1, HMACSHA256, HMACSHA384 and HMACSHA512
If you need more you can find it in: System.Security.Cryptography Namespace
Function Hash(HashType, Target)
On Error Resume Next
Dim PlainText
If IsArray(Target) = True Then PlainText = Target(0) Else PlainText = Target End If
With CreateObject("ADODB.Stream")
.Open
.CharSet = "Windows-1252"
.WriteText PlainText
.Position = 0
.CharSet = "UTF-8"
PlainText = .ReadText
.Close
End With
Set UTF8Encoding = CreateObject("System.Text.UTF8Encoding")
Dim PlainTextToBytes, BytesToHashedBytes, HashedBytesToHex
PlainTextToBytes = UTF8Encoding.GetBytes_4(PlainText)
Select Case HashType
Case "md5": Set Cryptography = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider") '< 64 (collisions found)
Case "ripemd160": Set Cryptography = CreateObject("System.Security.Cryptography.RIPEMD160Managed")
Case "sha1": Set Cryptography = CreateObject("System.Security.Cryptography.SHA1Managed") '< 80 (collision found)
Case "sha256": Set Cryptography = CreateObject("System.Security.Cryptography.SHA256Managed")
Case "sha384": Set Cryptography = CreateObject("System.Security.Cryptography.SHA384Managed")
Case "sha512": Set Cryptography = CreateObject("System.Security.Cryptography.SHA512Managed")
Case "md5HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACMD5")
Case "ripemd160HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACRIPEMD160")
Case "sha1HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACSHA1")
Case "sha256HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACSHA256")
Case "sha384HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACSHA384")
Case "sha512HMAC": Set Cryptography = CreateObject("System.Security.Cryptography.HMACSHA512")
End Select
Cryptography.Initialize()
If IsArray(Target) = True Then Cryptography.Key = UTF8Encoding.GetBytes_4(Target(1))
BytesToHashedBytes = Cryptography.ComputeHash_2((PlainTextToBytes))
For x = 1 To LenB(BytesToHashedBytes)
HashedBytesToHex = HashedBytesToHex & Right("0" & Hex(AscB(MidB(BytesToHashedBytes, x, 1))), 2)
Next
If Err.Number <> 0 Then Response.Write(Err.Description) Else Hash = LCase(HashedBytesToHex)
On Error GoTo 0
End Function
These can be used as follows:
Hash("sha512", "Hello World")
Produces:
2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b
Hash("sha256", "Hello World")
Produces:
a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
Hash("md5", "muñeca")
Produces:
ea07bec1f37f4b56ebe368355d1c058f
Hash("sha512HMAC", Array("Hello World", "Shared Key"))
Produces:
28e72824c48da5a5f14b59246905d2839e7c50e271fc078b1c0a75c89b6a3998746bd8b2dc1764b19d312702cf5e15b38ce799156af28b98ce08b85e4df65b32

There is Javascript code that produces an MD5 checksum. One of them, derived from the Google closure library, is available here.
It's pretty easy to produce a Windows Script Component from the Javascript, then call that component from any COM-enabled language, including VB.
Here's a working example.

Related

ADODB.Stream ReadText error

I am using a downloaded pure ASP script to upload files. My form contains a textarea and a file upload component. It works fine when I enter regular text but it cannot handle when I copy and paste something from word having special characters. The error I am getting is:
Provider error '80070057'
The parameter is incorrect.
/forum/freeaspupload.asp, line 309
The part of my code which throws the error is:
Private Function ConvertUtf8BytesToString(start, length)
StreamRequest.Position = 0
Dim objStream
Dim strTmp
' init stream
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Charset = "utf-8"
objStream.Mode = adModeReadWrite
objStream.Type = adTypeBinary
objStream.Open
' write bytes into stream
StreamRequest.Position = start+1
StreamRequest.CopyTo objStream, length
objStream.Flush
' rewind stream and read text
objStream.Position = 0
objStream.Type = adTypeText
strTmp = objStream.ReadText
' close up and return
objStream.Close
Set objStream = Nothing
ConvertUtf8BytesToString = strTmp
End Function
Line 309 is the line:
strTmp = objStream.ReadText
Any idea how to fix it?
I know it's an old question but since there is no answer and I got the exact same problem and finally found a solution, I think it can be good to share it.
The problem is related to IIS version since it was working fine on IIS6 but stop to work when we moved to IIS8.5
See ReadText
By defaut ReadText parameter is -1 (adReadAll).
For some reason, this default parameter seems not working with IIS8.5. You have to put the length of the text that you want to read.
so ReadText(2000) will work fine.
Of course, you will have to figured out the maximum length or find a way to loop and read everything
By MSFT :
If NumChar is more than the number of characters left in the stream, only the characters remaining are returned. The string read is not padded to match the length specified by NumChar. If there are no characters left to read, a variant whose value is null is returned. ReadText cannot be used to read backwards.

Simple encrypt/decrypt functions in Classic ASP

Are there any simple encrypt/decrypt functions in Classic ASP?
The data that needs to be encrypted and decrypted is not super sensitive. So simple functions would do.
4guysfromrolla.com: RC4 Encryption Using ASP & VBScript
See the attachments at the end of the page.
The page layout looks a bit broken to me, but all the info is there. I made it readable it by deleting the code block from the DOM via bowser development tools.
Try this:
' Encrypt and decrypt functions for classic ASP (by TFI)
'********* set a random string with random length ***********
cryptkey = "GNQ?4i0-*\CldnU+[vrF1j1PcWeJfVv4QGBurFK6}*l[H1S:oY\v#U?i,oD]f/n8oFk6NesH--^PJeCLdp+(t8SVe:ewY(wR9p-CzG<,Q/(U*.pXDiz/KvnXP`BXnkgfeycb)1A4XKAa-2G}74Z8CqZ*A0P8E[S`6RfLwW+Pc}13U}_y0bfscJ<vkA[JC;0mEEuY4Q,([U*XRR}lYTE7A(O8KiF8>W/m1D*YoAlkBK#`3A)trZsO5xv#5#MRRFkt\"
'**************************** ENCRYPT FUNCTION ******************************
'*** Note: bytes 255 and 0 are converted into the same character, in order to
'*** avoid a char 0 which would terminate the string
function encrypt(inputstr)
Dim i,x
outputstr=""
cc=0
for i=1 to len(inputstr)
x=asc(mid(inputstr,i,1))
x=x-48
if x<0 then x=x+255
x=x+asc(mid(cryptkey,cc+1,1))
if x>255 then x=x-255
outputstr=outputstr&chr(x)
cc=(cc+1) mod len(cryptkey)
next
encrypt=server.urlencode(replace(outputstr,"%","%25"))
end function
'**************************** DECRYPT FUNCTION ******************************
function decrypt(byval inputstr)
Dim i,x
inputstr=urldecode(inputstr)
outputstr=""
cc=0
for i=1 to len(inputstr)
x=asc(mid(inputstr,i,1))
x=x-asc(mid(cryptkey,cc+1,1))
if x<0 then x=x+255
x=x+48
if x>255 then x=x-255
outputstr=outputstr&chr(x)
cc=(cc+1) mod len(cryptkey)
next
decrypt=outputstr
end function
'****************************************************************************
Function URLDecode(sConvert)
Dim aSplit
Dim sOutput
Dim I
If IsNull(sConvert) Then
URLDecode = ""
Exit Function
End If
'sOutput = REPLACE(sConvert, "+", " ") ' convert all pluses to spaces
sOutput=sConvert
aSplit = Split(sOutput, "%") ' next convert %hexdigits to the character
If IsArray(aSplit) Then
sOutput = aSplit(0)
For I = 0 to UBound(aSplit) - 1
sOutput = sOutput & Chr("&H" & Left(aSplit(i + 1), 2)) & Right(aSplit(i + 1), Len(aSplit(i + 1)) - 2)
Next
End If
URLDecode = sOutput
End Function
I know is a bit late for BrokenLink, but for the record and others like me who were looking for the same.
I found this https://www.example-code.com/vbscript/crypt_aes_encrypt_file.asp.
It needs to install a chilkat ActiveX component on WindowsServer. But this inconvenient becomes convenient when looking resources and processing time.
Its very easy to use, and the given example is pretty clear. To make it your own, just change the "keyHex" variable value and voilá.

simple yet secure encrypt / decrypt asp to asp.net

I have a ASP/VB6 web-app that logs in a user. I want to encrypt the users identity field and pass(querystring) it to a asp.net app and then decrypt it to do a db lookup.
I've googled it and found rot13, albeit not secure enough. I've also found some hits on MD5 / RC4, but did not find any good examples of encrypt / decrypt.
Any ideas on how to implement secure encryption/description logic?
I agree with #Brian - don't go doing your own crypto, the crypto is easy until you start doing the key management. Do use SSL/TLS unless you have a very VERY V.E.R.Y. good reason not to do so.
It's generally conceded that you should never decrypt such information, but rather compare encrypted to encrypted.
MD5, for example, can be used in this 'trapdoor' fashion. Encode the information, then store the MD5 hash. When you need to authenticate, encode the new information and compare hashes. The unencrypted information is never exposed or available.
If this doesn't work for your situation, then look into the Windows Crypto API, which offers alternatives that allow full-cycle encrypt / decrypt.
Here is a basic encryption example. You'll want to figure out your own key. I did mine this way to just add one more level of complexity (I hope). As Jim points out you could use this to encrypt a new password, then store the results. After the password is created instead of ever trying to decrypt this value (which is just the reverse), you would encrypt the entered password and compare it against the stored value.
'combine these constants to build the encryption key'
Private Const KEY1 = "abcde"
Private Const KEY2 = "fghij"
Private Const KEY3 = "klmno"
Private Const KEY4 = "pqrst"
Private Const KEY5 = "uvwxy"
Private Function Encrypt(ByVal s As String, ByVal EncryptionType As CAPICOM.CAPICOM_ENCODING_TYPE) As String
Dim oEN As New CAPICOM.EncryptedData
Dim intENCType As CAPICOM.CAPICOM_ENCRYPTION_ALGORITHM
Dim strSecret As String
Dim intTries As Integer
On Error GoTo errEncrypt
intENCType = CAPICOM_ENCRYPTION_ALGORITHM_AES ' try this first and fall back if not supported'
With oEN
startEncryption:
.Algorithm = intENCType
strSecret = KEY2 & KEY5 & KEY4 & KEY1 & KEY3
.SetSecret strSecret
strSecret = ""
.Content = s
' the first encryption type needs to be base64 as the .content property'
' can loose information if I try to manipulate a binary string'
.Content = StrReverse(.Encrypt(CAPICOM_ENCODE_BASE64))
strSecret = KEY1 & KEY4 & KEY3 & KEY2 & KEY5
.SetSecret strSecret
strSecret = ""
Encrypt = .Encrypt(EncryptionType)
End With
Set oEN = Nothing
Exit Function
errEncrypt:
If Err.Number = -2138568448 Then
' if this is the first time the step the encryption back and try again
If intTries < 1 Then
intTries = intTries + 1
intENCType = CAPICOM_ENCRYPTION_ALGORITHM_3DES
Resume startEncryption
End If
End If
Err.Raise Err.Number, Err.Source & ":Encrypt", Err.Description
strSecret = ""
Set oEN = Nothing
End Function

Best Regular Expression for Email Format Validation with ASP.NET 3.5 Validation

I've used both of the following Regular Expressions for testing for a valid email expression with ASP.NET validation controls. I was wondering which is the better expression from a performance standpoint, or if someone has better one.
- \w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*
- ^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$
I'm trying avoid the "exponentially slow expression" problem described on the BCL Team Blog.
UPDATE
Based on feedback I ended up creating a function to test if an email is valid:
Public Function IsValidEmail(ByVal emailString As String, Optional ByVal isRequired As Boolean = False) As Boolean
Dim emailSplit As String()
Dim isValid As Boolean = True
Dim localPart As String = String.Empty
Dim domainPart As String = String.Empty
Dim domainSplit As String()
Dim tld As String
If emailString.Length >= 80 Then
isValid = False
ElseIf emailString.Length > 0 And emailString.Length < 6 Then
'Email is too short
isValid = False
ElseIf emailString.Length > 0 Then
'Email is optional, only test value if provided
emailSplit = emailString.Split(CChar("#"))
If emailSplit.Count <> 2 Then
'Only 1 # should exist
isValid = False
Else
localPart = emailSplit(0)
domainPart = emailSplit(1)
End If
If isValid = False OrElse domainPart.Contains(".") = False Then
'Needs at least 1 period after #
isValid = False
Else
'Test Local-Part Length and Characters
If localPart.Length > 64 OrElse ValidateString(localPart, ValidateTests.EmailLocalPartSafeChars) = False OrElse _
localPart.StartsWith(".") OrElse localPart.EndsWith(".") OrElse localPart.Contains("..") Then
isValid = False
End If
'Validate Domain Name Portion of email address
If isValid = False OrElse _
ValidateString(domainPart, ValidateTests.HostNameChars) = False OrElse _
domainPart.StartsWith("-") OrElse domainPart.StartsWith(".") OrElse domainPart.Contains("..") Then
isValid = False
Else
domainSplit = domainPart.Split(CChar("."))
tld = domainSplit(UBound(domainSplit))
' Top Level Domains must be at least two characters
If tld.Length < 2 Then
isValid = False
End If
End If
End If
Else
'If no value is passed review if required
If isRequired = True Then
isValid = False
Else
isValid = True
End If
End If
Return isValid
End Function
Notes:
IsValidEmail is more restrictive about characters allowed then the RFC, but it doesn't test for all possible invalid uses of those characters
If you're wondering why this question is generating so little activity, it's because there are so many other issues that should be dealt with before you start thinking about performance. Foremost among those is whether you should be using regexes to validate email addresses at all--and the consensus is that you should not. It's much trickier than most people expect, and probably pointless anyway.
Another problem is that your two regexes vary hugely in the kinds of strings they can match. For example, the second one is anchored at both ends, but the first isn't; it would match ">>>>foo#bar.com<<<<" because there's something that looks like an email address embedded in it. Maybe the framework forces the regex to match the whole string, but if that's the case, why is the second one anchored?
Another difference is that the first regex uses \w throughout, while the second uses [0-9a-zA-Z] in many places. In most regex flavors, \w matches the underscore in addition to letters and digits, but in some (including .NET) it also matches letters and digits from every writing system known to Unicode.
There are many other differences, but that's academic; neither of those regexes is very good. See here for a good discussion of the topic, and a much better regex.
Getting back to the original question, I don't see a performance problem with either of those regexes. Aside from the nested-quantifiers anti-pattern cited in that BCL blog entry, you should also watch out for situations where two or more adjacent parts of the regex can match the same set of characters--for example,
([A-Za-z]+|\w+)#
There's nothing like that in either of the regexes you posted. Parts that are controlled by quantifiers are always broken up by other parts that aren't quantified. Both regexes will experience some avoidable backtracking, but there are many better reasons than performance to reject them.
EDIT: So the second regex is subject to catastrophic backtracking; I should have tested it thoroughly before shooting my mouth off. Taking a closer look at that regex, I don't see why you need the outer asterisk in the first part:
[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*
All that bit does is make sure the first and last characters are alphanumeric while allowing some additional characters in between. This version does the same thing, but it fails much more quickly when no match is possible:
[0-9a-zA-Z][-.\w]*[0-9a-zA-Z]
That would probably suffice to eliminate the backtracking problem, but you could also make the part after the "#" more efficient by using an atomic group:
(?>(?:[0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+)[a-zA-Z]{2,9}
In other words, if you've matched all you can of substrings that look like domain components with trailing dots, and the next part doesn't look like a TLD, don't bother backtracking. The first character you would have to give up is the final dot, and you know [a-zA-Z]{2,9} won't match that.
We use this RegEx which has been tested in-house against 1.5 million addresses. It correctly identifies better than 98% of ours, but there are some formats that I'm aware of that it would error on.
^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$
We also make sure that there are no EOL characters in the data since an EOL can fake out this RegEx. Our Function:
Public Function IsValidEmail(ByVal strEmail As String) As Boolean
' Check An eMail Address To Ensure That It Is Valid
Const cValidEmail = "^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$" ' 98% Of All Valid eMail Addresses
IsValidEmail = False
' Take Care Of Blanks, Nulls & EOLs
strEmail = Replace(Replace(Trim$(strEmail & " "), vbCr, ""), vbLf, "")
' Blank eMail Is Invalid
If strEmail = "" Then Exit Function
' RegEx Test The eMail Address
Dim regEx As New System.Text.RegularExpressions.Regex(cValidEmail)
IsValidEmail = regEx.IsMatch(strEmail)
End Function
I am a newbie, but I tried the following and it seemed to have limited the ".xxx" to only two occurrences or less, after the symbol '#'.
^([a-zA-Z0-9]+[a-zA-Z0-9._%-]*#(?:[a-zA-Z0-9-])+(\.+[a-zA-Z]{2,4}){1,2})$
Note: I had to substitute single '\' with double '\\' as I am using this reg expr in R.
These don't check for all allowable email addresses according to the email address RFC.
I let MS to do the work for me:
Public Function IsValidEmail(ByVal emailString As String) As Boolean
Dim retval As Boolean = True
Try
Dim address As New System.Net.Mail.MailAddress(emailString)
Catch ex As Exception
retval = False
End Try
Return retval
End Function
For server side validation, I found Phil Haack's solution to be one of the better ones. His attempt was to stick to the RFC:
string pattern = #"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|"
+ #"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)"
+ #"#[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
return regex.IsMatch(emailAddress);
Details:
http://blog.degree.no/2013/01/email-validation-finally-a-net-regular-expression-that-works/
Just to contribute, I am using this regex.
^([a-zA-Z0-9]+[a-zA-Z0-9._%-]*#(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4})$
The thing about it is the specifications are changing with each domain extension that is introduced.
You sit here mod your regex, test, test, test, and more testing. You finally get what you "think" is accurate then the specification changes... You update your regex to account for what the new requirements are..
Then someone enters aa#aa.aa and you've done all that work for what? It walks through your fancy regex.. bummer!
You may as well just check for a single #, and a "." and move on. I assure you, you will not get someones email if they do not want to give it up. You'll get garbage or their hotmail account they never check and couldn't care less about.
I've seen in many cases this goes horribly wrong and a client calls up because their own email address is rejected because of a poorly crafted regex check. Which as mentioned shouldn't have even been attempted.
TextBox :-
<asp:TextBox ID="txtemail" runat="server" CssClass="form-control pantxt" Placeholder="Enter Email Address"></asp:TextBox>
Required Filed validator:
<asp:RequiredFieldValidator ID="RequiredFieldValidator9" runat="server" ControlToValidate="txtemail" ErrorMessage="Required"></asp:RequiredFieldValidator>
Regular Expression for email validation :
<asp:RegularExpressionValidator ID="validateemail" runat="server" ControlToValidate="txtemail" ValidationExpression="\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*" ErrorMessage="Invalid Email"></asp:RegularExpressionValidator>
Use this regular expression for email validation in asp.net

Reimplement ASP.NET Membership and User Password Hashing in Ruby

I have a large database of users (~200,000) that I'm transferring from a ASP.NET application to a Ruby on Rails application. I don't really want to ask every user to reset their password and so I'm trying to re-implement the C# password hashing function in Ruby.
The old function is this:
public string EncodePassword(string pass, string saltBase64)
{
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(saltBase64);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inArray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inArray);
}
An example hashed password and salt is (and the password used was "password"):
Hashed password: "weEWx4rhyPtd3kec7usysxf7kpk="
Salt: "1ptFxHq7ALe7yXIQDdzQ9Q=="
Password: "password"
Now with the following Ruby code:
require "base64"
require "digest/sha1"
password = "password"
salt = "1ptFxHq7ALe7yXIQDdzQ9Q=="
concat = salt+password
sha1 = Digest::SHA1.digest(concat)
encoded = Base64.encode64(sha1)
puts encoded
I'm not getting the correct password hash (I'm getting "+BsdIOBN/Vh2U7qWG4e+O13h3iQ=" instead of "weEWx4rhyPtd3kec7usysxf7kpk="). Can anyone see what the problem might be?
Many thanks
Arfon
Just a quick update, a colleague of mine has solved this:
require "base64"
require "digest"
require "jcode"
def encode_password(password, salt)
bytes = ""
password.each_char { |c| bytes += c + "\x00" }
salty = Base64.decode64(salt)
concat = salty+bytes
sha1 = Digest::SHA1.digest(concat)
encoded = Base64.encode64(sha1).strip()
puts encoded
end
I have been tasked with migrating an existing .NET app to Ruby on Rails. I am using the code below to mimic the .NET password hashing. I am very new to Ruby, and don't know .NET at all. The code may not be as clean as it could, but it's a start.
To test, save this as a Ruby script and run with:
ruby script plain_text_password salt_in_base64
e.g.
ruby dotNetHash.rb password123 LU7hUk4MXAvlq6DksvP9SQ==
require "base64"
require "digest"
# Encode password as double-width characters
password_as_text = ARGV.first
double_width_password = []
double_width_password = password_as_text.encode("UTF-16LE").bytes.to_a
# Unencode the salt
salt = Base64.decode64(ARGV[1])
# Concatenate salt+pass
salt_pass_array = []
salt_pass_array = salt.bytes.to_a + double_width_password
# Repack array as string and hash it. Then encode.
salt_pass_str = salt_pass_array.pack('C*')
sha1_saltpass = Digest::SHA1.digest(salt_pass_str)
enc_sha1_saltpass = Base64.encode64(sha1_saltpass).strip()
puts "Encoded SHA1 saltpass is " + enc_sha1_saltpass
You are pretty close. Unfortunately Ruby has no built-in unicode support at the moment, and your hashing function relies on it. There are workarounds. Look around the site on how to do unicode in Ruby.
BTW, I think you forgot to base64 decode the salt, it looks like the ASP.net function does that.
You need to unencode the salt to convert it back to it's byte representation and then concatenate that with the password to get the hashed password value. You're using the encoding salt string directly (which is a different salt) and thus it is hashing to something different.
require "base64"
require "digest/sha1"
password = "password"
salt = Base64.decode64("1ptFxHq7ALe7yXIQDdzQ9Q==")
concat = salt+password
sha1 = Digest::SHA1.digest(concat)
encoded = Base64.encode64(sha1)
puts encoded

Resources