Skip to content

Instantly share code, notes, and snippets.

@jesteves
Last active July 26, 2021 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jesteves/148a9b5b6322789f9e44 to your computer and use it in GitHub Desktop.
Save jesteves/148a9b5b6322789f9e44 to your computer and use it in GitHub Desktop.
MD5Crypt in VB.net - Implements unix crypt (shadow passwords)
''''
'
' MD5Crypt.vb
'
' -- Julián Esteves / 2016-02-13
'
' This is a direct translation from C# of Martín Fernández's MD5Crypt.cs and
' main.cs.
'
''''
'
' Disclaimer:
' Being a Perl/Javascript programmer, it's possible that the following
' lines of code result horrible to the eyes of a VB.Net expert. If you
' are one of those, please consider it just as a starting point and
' hack as needed (and send me your comments if you can...). For my
' pourposes (which was getting an equivalent of Perl's Crypt::PasswdMD5
' in VB) this code is good enough. Also, not being a Windows user,
' I tested the code only on OS X, using Mono.
'
''''
'
' Thanks goes to:
' Martín Fernández - for sharing that C# code
' http://www.codeproject.com/Articles/20767/Unix-md-crypt
' Luis E. Muñoz - for Crypt::PasswdMD5 (mantained by Ron Savage since 2013)
' http://search.cpan.org/~rsavage/Crypt-PasswdMD5-1.40/
'
''''
'
' Keywords:
' md5crypt, unix crypt, shadow password, VB.net, C#
'
'''''''''
'
' All remaining commentaries are retained verbatim from C# code
'
'
' //
' // Oct-07 - Martin Fernández translation for all the linux fans and detractors...
' //
' /*
' #########################################################
' # md5crypt.py
' #
' # 0423.2000 by michal wallace http://www.sabren.com/
' # based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
' # based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
' #
' # MANY THANKS TO
' #
' # Carey Evans - http://home.clear.net.nz/pages/c.evans/
' # Dennis Marti - http://users.starpower.net/marti1/
' #
' # For the patches that got this thing working!
' #
' #########################################################
' md5crypt.py - Provides interoperable MD5-based crypt() function
'
' SYNOPSIS
'
' import md5crypt.py
'
' cryptedpassword = md5crypt.md5crypt(password, salt);
'
' DESCRIPTION
'
' unix_md5_crypt() provides a crypt()-compatible interface to the
' rather new MD5-based crypt() function found in modern operating systems.
' It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
' contains the following license in it:
'
' "THE BEER-WARE LICENSE" (Revision 42):
' <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
' can do whatever you want with this stuff. If we meet some day, and you think
' this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
'
' apache_md5_crypt() provides a function compatible with Apache's
' .htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>.
' */
Imports System
Imports System.Text
'Imports System.Collections.Generic
Imports System.Security.Cryptography
Namespace Unix_MD5Crypt
Public Class MD5Crypt
' Password hash magic
Private Shared magic As String = "$1$"
' Characters for base64 encoding
Private Shared itoa64 As String = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
''' <summary>
''' A function to concatenate bytes[]
''' </summary>
''' <param name="array1"></param>
''' <param name="array2"></param>
''' <returns>New adition array</returns>
Private Shared Function Concat(array1 As Byte(), array2 As Byte()) As Byte()
Dim concat As Byte() = New Byte((array1.Length + array2.Length) - 1) {}
System.Buffer.BlockCopy(array1, 0, concat, 0, array1.Length)
System.Buffer.BlockCopy(array2, 0, concat, array1.Length, array2.Length)
Return concat
End Function
''' <summary>
''' Another function to concatenate bytes[]
''' </summary>
''' <param name="array1"></param>
''' <param name="array2"></param>
''' <returns>New adition array</returns>
Private Shared Function PartialConcat(array1 As Byte(), array2 As Byte(), max As Integer) As Byte()
Dim concat As Byte() = New Byte( (array1.Length + max) - 1 ) {}
System.Buffer.BlockCopy(array1, 0, concat, 0, array1.Length)
System.Buffer.BlockCopy(array2, 0, concat, array1.Length, max)
Return concat
End Function
''' <summary>
''' Base64-Encode integer value
''' </summary>
''' <param name="value"> The value to encode</param>
''' <param name="length"> Desired length of the result</param>
''' <returns>@return Base64 encoded value</returns>
Private Shared Function to64(value As Integer, length As Integer) As String
Dim result As StringBuilder
result = New StringBuilder()
length = length - 1
While length >= 0
result.Append(itoa64.Substring(value And &H3f, 1))
value >>= 6
length = length - 1
End While
Return (result.ToString())
End Function
''' <summary>
''' Unix-like Crypt-MD5 function
''' </summary>
''' <param name="password">The user password</param>
''' <param name="salt">The salt or the pepper of the password</param>
''' <returns>a human readable string</returns>
Public Shared Function crypt(password As String, salt As String) As String
Dim saltEnd As Integer
Dim len As Integer
Dim value As Integer
Dim i As Integer
' /*
' MessageDigest ctx;
' MessageDigest ctx1;
' */
' sResult = BitConverter.ToString(hashvalue1);
Dim final As Byte()
Dim passwordBytes As Byte()
Dim saltBytes As Byte()
Dim ctx As Byte()
Dim result As StringBuilder
Dim x_hash_alg As HashAlgorithm = HashAlgorithm.Create("MD5")
' Skip magic if it exists
If salt.StartsWith(magic) Then
salt = salt.Substring(magic.Length)
End If
' Remove password hash if present
saltEnd = salt.LastIndexOf("$")
If saltEnd <> -1 Then
salt = salt.Substring(0, saltEnd)
End If
' Shorten salt to 8 characters if it is longer
If salt.Length > 8 Then
salt = salt.Substring(0, 8)
End If
ctx = Encoding.ASCII.GetBytes((password + magic + salt))
final = x_hash_alg.ComputeHash(Encoding.ASCII.GetBytes((password + salt + password)))
' Add as many characters of ctx1 to ctx
Dim hashM As Byte() ' = new byte[15];
For len = password.Length To 1 Step -16
If len > 16 Then
ctx = Concat(ctx, final)
Else
ctx = PartialConcat(ctx, final, len)
'System.Buffer.BlockCopy(final, 0, hash16, ctx.Length, len);
'System.Buffer.BlockCopy(ctx, 0, hash16, 0, ctx.Length);
End If
Next
'ctx = hashM;
' Then something really weird...
passwordBytes = Encoding.ASCII.GetBytes(password)
i = password.Length
While i > 0
If (i And 1) = 1 Then
ctx = Concat(ctx, New Byte() {0})
Else
ctx = Concat(ctx, New Byte() {passwordBytes(0)})
End If
i >>= 1
End While
final = x_hash_alg.ComputeHash(ctx)
Dim ctx1 As Byte()
' Do additional mutations
saltBytes = Encoding.ASCII.GetBytes(salt)'.getBytes();
For i = 0 To 999
ctx1 = New Byte() {}
If (i And 1) = 1 Then
ctx1 = Concat(ctx1, passwordBytes)
Else
ctx1 = Concat(ctx1, final)
End If
If i Mod 3 <> 0 Then
ctx1 = Concat(ctx1, saltBytes)
End If
If i Mod 7 <> 0 Then
ctx1 = Concat(ctx1, passwordBytes)
End If
If (i And 1) <> 0 Then
ctx1 = Concat(ctx1, final)
Else
ctx1 = Concat(ctx1, passwordBytes)
End If
final = x_hash_alg.ComputeHash(ctx1)
Next
result = New StringBuilder()
' Add the password hash to the result string
value = ((final(0) And &Hff) << 16) Or ((final(6) And &Hff) << 8) Or (final(12) And &Hff)
result.Append(to64(value, 4))
value = ((final(1) And &Hff) << 16) Or ((final(7) And &Hff) << 8) Or (final(13) And &Hff)
result.Append(to64(value, 4))
value = ((final(2) And &Hff) << 16) Or ((final(8) And &Hff) << 8) Or (final(14) And &Hff)
result.Append(to64(value, 4))
value = ((final(3) And &Hff) << 16) Or ((final(9) And &Hff) << 8) Or (final(15) And &Hff)
result.Append(to64(value, 4))
value = ((final(4) And &Hff) << 16) Or ((final(10) And &Hff) << 8) Or (final(5) And &Hff)
result.Append(to64(value, 4))
value = final(11) And &Hff
result.Append(to64(value, 2))
' Return result string
Return magic + salt + "$" + result.ToString()
End Function
End Class
Public Class Main
Public Shared Sub Main()
Console.Write("Password: ")
Dim pass As String = Console.In.ReadLine()
Console.Write("Salt (possibly an already MD5Cryted password): ")
Dim salt As String = Console.In.ReadLine()
Console.WriteLine()
Console.Write("MD5Crypted password: ")
Dim crypted As String = MD5Crypt.crypt( pass, salt )
Console.WriteLine( crypted )
' verification mode
If crypted = salt Then
Console.WriteLine( "===> MD5Crypted matches salt!!" )
End If
End Sub
End Class
End Namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment