microsoft-visualbasic-runtime/Extensions/Security/Md5.vb

339 lines
13 KiB
VB.net
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Region "Microsoft.VisualBasic::ced60d52179f290950843cd79cc35712, Microsoft.VisualBasic.Core\Extensions\Security\Md5.vb"
' Author:
'
' asuka (amethyst.asuka@gcmodeller.org)
' xie (genetics@smrucc.org)
' xieguigang (xie.guigang@live.com)
'
' Copyright (c) 2018 GPL3 Licensed
'
'
' GNU GENERAL PUBLIC LICENSE (GPL3)
'
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' /********************************************************************************/
' Summaries:
' Module MD5Hash
'
' Function: Fletcher32, GetFileMd5, (+2 Overloads) GetHashCode, (+2 Overloads) GetMd5Hash, GetMd5Hash2
' NewUid, SaltValue, Sha256ByteString, StringToByteArray, (+2 Overloads) ToLong
' VerifyFile, VerifyMd5Hash
'
'
' /********************************************************************************/
#End Region
Imports System.IO
Imports System.Runtime.CompilerServices
Imports System.Security.Cryptography
Imports System.Text
Imports Microsoft.VisualBasic.CommandLine.Reflection
Imports Microsoft.VisualBasic.Language
Imports Microsoft.VisualBasic.Linq
Imports Microsoft.VisualBasic.Scripting.MetaData
Namespace SecurityString
<Package("Md5Hash", Publisher:="Microsoft Corp.", Description:="Represents the abstract class from which all implementations of the System.Security.Cryptography.MD5 hash algorithm inherit.")>
Public Module MD5Hash
<ExportAPI("Uid")>
Public Function NewUid() As String
Dim input As String = Guid.NewGuid.ToString & Now.ToString
Return GetMd5Hash(input)
End Function
ReadOnly hashProvider As New Md5HashProvider
''' <summary>
''' Calculate md5 hash value for the input string.
''' </summary>
''' <param name="input"></param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("Md5")>
<Extension>
Public Function GetMd5Hash(input As String) As String
SyncLock hashProvider
Return hashProvider.GetMd5Hash(input)
End SyncLock
End Function
''' <summary>
''' Gets the hashcode of the input string. (<paramref name="input"/> => <see cref="MD5Hash.GetMd5Hash"/> => <see cref="MD5Hash.ToLong(String)"/>)
''' </summary>
''' <param name="input">任意字符串</param>
''' <returns></returns>
<ExportAPI("GetHashCode", Info:="Gets the hashcode of the input string.")>
Public Function GetHashCode(input As String) As Long
Dim md5 As String = MD5Hash.GetMd5Hash(input)
Return ToLong(md5)
End Function
''' <summary>
''' Gets the hashcode of the input string.
''' </summary>
''' <returns></returns>
<ExportAPI("GetHashCode", Info:="Gets the hashcode of the input string.")>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Public Function GetHashCode(data As IEnumerable(Of Byte)) As Long
Return GetMd5Hash(data.ToArray).ToLong
End Function
''' <summary>
''' Gets the hashcode of the md5 string.
''' </summary>
''' <param name="md5">计算所得到的MD5哈希值</param>
''' <returns></returns>
<ExportAPI("As.Long", Info:="Gets the hashcode of the md5 string.")>
<Extension> Public Function ToLong(md5 As String) As Long
Dim bytes = StringToByteArray(md5)
Return ToLong(bytes)
End Function
''' <summary>
''' CityHash algorithm for convert the md5 hash value as a <see cref="Int64"/> value.
''' </summary>
''' <param name="bytes">
''' this input value should compute from <see cref="Md5HashProvider.GetMd5Bytes(Byte())"/>
''' </param>
''' <returns></returns>
''' <remarks>
''' http://stackoverflow.com/questions/9661227/convert-md5-to-long
'''
''' The very best solution I found (based on my needs... mix of speed and good hash function) is Google's CityHash.
''' The input can be any byte array including an MD5 result and the output is an unsigned 64-bit long.
'''
''' CityHash has a very good but Not perfect hash distribution, And Is very fast.
'''
''' I ported CityHash from C++ To C# In half an hour. A Java port should be straightforward too.
'''
''' Just XORing the bits doesn't give as good a distribution (though admittedly that will be very fast).
'''
''' I'm not familiar enough with Java to tell you exactly how to populate a long from a byte array
''' (there could be a good helper I'm not familiar with, or I could get some details of arithmetic
''' in Java wrong).
''' Essentially, though, you'll want to do something like this:
'''
''' Long a = md5[0] * 256 * md5[1] + 256 * 256 * md5[2] + 256 * 256 * 256 * md5[3];
''' Long b = md5[4] * 256 * md5[5] + 256 * 256 * md5[6] + 256 * 256 * 256 * md5[7];
''' Long result = a ^ b;
'''
''' Note I have made no attempt To deal With endianness. If you just care about a consistent hash value,
''' though, endianness should Not matter.
''' </remarks>
<ExportAPI("As.Long")> <Extension>
Public Function ToLong(bytes As Byte()) As Long
Dim md5 As Long() = bytes.Select(Function(x) CLng(x)).ToArray
Dim a As Long = md5(0) * 256 * md5(1) + 256 * 256 * md5(2) + 256 * 256 * 256 * md5(3)
Dim b As Long = md5(4) * 256 * md5(5) + 256 * 256 * md5(6) + 256 * 256 * 256 * md5(7)
Dim result As Long = a Xor b
Return result
End Function
''' <summary>
''' 由于md5是大小写无关的故而在这里都会自动的被转换为小写形式所以调用这个函数的时候不需要在额外的转换了
''' </summary>
''' <param name="hex"></param>
''' <returns></returns>
<ExportAPI("As.Bytes")>
Public Function StringToByteArray(hex As String) As Byte()
Dim NumberChars As Integer = hex.Length
Dim bytes As Byte() = New Byte(NumberChars / 2 - 1) {}
hex = hex.ToLower ' MD5是大小写无关的
For i As Integer = 0 To NumberChars - 2 Step 2
bytes(i / 2) = Convert.ToByte(hex.Substring(i, 2), 16)
Next
Return bytes
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("Md5")>
Public Function GetMd5Hash(input As Byte()) As String
Return New Md5HashProvider().GetMd5Hash(input)
End Function
<Extension>
Public Function GetMd5Hash2(Of T)(x As T) As String
Dim raw As String = x.ToString & x.GetHashCode
Return raw.GetMd5Hash
End Function
''' <summary>
''' Verify a hash against a string.
''' </summary>
''' <param name="input"></param>
''' <param name="comparedHash"></param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("Md5.Verify", Info:="Verify a hash against a string.")>
Public Function VerifyMd5Hash(input As String, comparedHash As String) As Boolean
If String.IsNullOrEmpty(input) OrElse String.IsNullOrEmpty(comparedHash) Then
Return False
End If
Dim hashOfInput As String = GetMd5Hash(input) ' Hash the input.
Return String.Equals(hashOfInput, comparedHash, StringComparison.OrdinalIgnoreCase)
End Function 'VerifyMd5Hash
''' <summary>
''' 校验两个文件的哈希值是否一致
''' </summary>
''' <param name="query"></param>
''' <param name="subject"></param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("File.Equals")>
Public Function VerifyFile(query As String, subject As String) As Boolean
Dim md5 As New Md5HashProvider()
Dim a1 As Byte() = IO.File.ReadAllBytes(query)
Dim a2 As Byte() = IO.File.ReadAllBytes(subject)
Dim m1 As String = md5.GetMd5Hash(a1)
Dim m2 As String = md5.GetMd5Hash(a2)
Return String.Equals(m1, m2)
End Function
''' <summary>
''' Get the md5 hash calculation value for a specific file.(获取文件对象的哈希值,请注意,当文件不存在或者文件的长度为零的时候,会返回空字符串)
''' </summary>
''' <param name="PathUri">The file path of the target file to be calculated.</param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("File.Md5", Info:="Get the md5 hash calculation value for a specific file.")>
<Extension>
Public Function GetFileMd5(<Parameter("Path.Uri", "The file path of the target file to be calculated.")> PathUri As String) As String
Dim size& = PathUri.FileLength
If size <= 0 Then
Return ""
ElseIf size < 1024 * 1024 * 5 Then
' small files
Dim bufs As Byte() = File.ReadAllBytes(PathUri)
Return GetMd5Hash(bufs)
Else
' large files
Using stream As New FileStream(PathUri, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 1024 * 2)
Dim sha As New SHA256Managed()
Dim checksum = sha.ComputeHash(stream)
Return BitConverter.ToString(checksum).Replace("-", String.Empty)
End Using
End If
End Function
''' <summary>
''' SHA256 8 bits salt value for the private key.
''' </summary>
''' <param name="value"></param>
''' <returns></returns>
<ExportAPI("SaltValue", Info:="SHA256 8 bits salt value for the private key.")>
Public Function SaltValue(value As String) As String
Dim hash As String = GetMd5Hash(value)
Dim chars() = {
hash(0),
hash(1),
hash(3),
hash(5),
hash(15),
hash(23),
hash(28),
hash(31)
}
Return chars.CharString
End Function
''' <summary>
''' sha256 computed byte array in a readable format.
''' </summary>
''' <param name="array"></param>
''' <returns></returns>
'''
<Extension>
Public Function Sha256ByteString(array() As Byte, Optional delimiter As Char = " "c) As String
Dim sb As New StringBuilder
For i As Integer = 0 To array.Length - 1
sb.Append($"{array(i):X2}")
If i Mod 4 = 3 Then
sb.Append(delimiter)
End If
Next
Return sb.ToString.Trim(delimiter)
End Function
''' <summary>
''' Calculate the Fletcher32 checksum.
''' </summary>
''' <param name="bytes">the bytes data for verify</param>
''' <param name="offset">initial offset</param>
''' <param name="length">the message length (if odd, 0 is appended)</param>
''' <returns></returns>
''' <remarks>
''' https://github.com/AKafakA/CSE5234_Group1/blob/ae055d2bb45be9ccf30bbf2bf9dfac4e6e982b1c/h2/src/main/org/h2/mvstore/DataUtils.java#L798
''' </remarks>
<Extension>
Public Function Fletcher32(bytes As Byte(), offset%, length%) As Integer
Dim s1 As Integer = &HFFFF
Dim s2 As Integer = &HFFFF
Dim i As VBInteger = offset
Dim len As Integer = offset + (length And (Not 1))
Do While i < len
' reduce after 360 words (each word is two bytes)
Dim [end] As Integer = System.Math.Min(CInt(i) + 720, len)
Do While i < [end]
'ORIGINAL LINE: int x = ((bytes[i++] & &Hff) << 8) | (bytes[i++] & &Hff);
Dim x As Integer = ((bytes(++i) And &HFF) << 8) Or (bytes(++i) And &HFF)
s1 += x
s2 += s1
Loop
s1 = (s1 And &HFFFF) + (CInt(CUInt(s1) >> 16))
s2 = (s2 And &HFFFF) + (CInt(CUInt(s2) >> 16))
Loop
If (length And 1) <> 0 Then
' odd length: append 0
Dim x As Integer = (bytes(i) And &HFF) << 8
s1 += x
s2 += s1
End If
s1 = (s1 And &HFFFF) + (CInt(CUInt(s1) >> 16))
s2 = (s2 And &HFFFF) + (CInt(CUInt(s2) >> 16))
Return (s2 << 16) Or s1
End Function
End Module
End Namespace