1 #Region "Microsoft.VisualBasic::d4e24ef5df87a839ca5674d4f8d08eb5, Microsoft.VisualBasic.Core\Extensions\Security\Md5.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module MD5Hash
35     
36     '         FunctionGetFileHashString, (+2 OverloadsGetHashCode, (+3 OverloadsGetMd5Hash, GetMd5Hash2, NewUid
37     '                   SaltValue, StringToByteArray, (+2 OverloadsToLong, VerifyFile, VerifyMd5Hash
38     
39     
40     ' /********************************************************************************/
41
42 #End Region
43
44 Imports System.Runtime.CompilerServices
45 Imports Microsoft.VisualBasic.CommandLine.Reflection
46 Imports Microsoft.VisualBasic.Linq
47 Imports Microsoft.VisualBasic.Scripting.MetaData
48
49 Namespace SecurityString
50
51     <Package("Md5Hash", Publisher:="Microsoft Corp.", Description:="Represents the abstract class from which all implementations of the System.Security.Cryptography.MD5 hash algorithm inherit.")>
52     Public Module MD5Hash
53
54         <ExportAPI("Uid")>
55         Public Function NewUid() As String
56             Dim input As String = Guid.NewGuid.ToString & Now.ToString
57             Return GetMd5Hash(input)
58         End Function
59
60         ReadOnly __hashProvider As New Md5HashProvider
61
62         ''' <summary>
63         ''' Calculate md5 hash value for the input string.
64         ''' </summary>
65         ''' <param name="input"></param>
66         ''' <returns></returns>
67         ''' 
68         <MethodImpl(MethodImplOptions.AggressiveInlining)>
69         <ExportAPI("Md5")>
70         <Extension>
71         Public Function GetMd5Hash(input As StringAs String
72             SyncLock __hashProvider
73                 Return __hashProvider.GetMd5Hash(input)
74             End SyncLock
75         End Function
76
77         ''' <summary>
78         ''' Gets the hashcode of the input string. (<paramref name="input"/> => <see cref="MD5Hash.GetMd5Hash"/> => <see cref="MD5Hash.ToLong(String)"/>)
79         ''' </summary>
80         ''' <param name="input">任意字符串</param>
81         ''' <returns></returns>
82         <ExportAPI("GetHashCode"Info:="Gets the hashcode of the input string.")>
83         Public Function GetHashCode(input As StringAs Long
84             Dim md5 As String = MD5Hash.GetMd5Hash(input)
85             Return ToLong(md5)
86         End Function
87
88         ''' <summary>
89         ''' Gets the hashcode of the input string.
90         ''' </summary>
91         ''' <returns></returns>
92         <ExportAPI("GetHashCode"Info:="Gets the hashcode of the input string.")>
93         <MethodImpl(MethodImplOptions.AggressiveInlining)>
94         Public Function GetHashCode(data As IEnumerable(Of Byte)) As Long
95             Return GetMd5Hash(data.ToArray).ToLong
96         End Function
97
98         ''' <summary>
99         ''' Gets the hashcode of the md5 string.
100         ''' </summary>
101         ''' <param name="md5">计算所得到的MD5哈希值</param>
102         ''' <returns></returns>
103         <ExportAPI("As.Long"Info:="Gets the hashcode of the md5 string.")>
104         <Extension> Public Function ToLong(md5 As StringAs Long
105             Dim bytes = StringToByteArray(md5)
106             Return ToLong(bytes)
107         End Function
108
109         ''' <summary>
110         ''' CityHash algorithm for convert the md5 hash value as a <see cref="Int64"/> value.
111         ''' </summary>
112         ''' <param name="bytes">
113         ''' this input value should compute from <see cref="Md5HashProvider.GetMd5Bytes(Byte())"/>
114         ''' </param>
115         ''' <returns></returns>
116         ''' <remarks>
117         ''' http://stackoverflow.com/questions/9661227/convert-md5-to-long
118         ''' 
119         ''' The very best solution I found (based on my needs... mix of speed and good hash function) is Google's CityHash. 
120         ''' The input can be any byte array including an MD5 result and the output is an unsigned 64-bit long.
121         '''
122         ''' CityHash has a very good but Not perfect hash distribution, And Is very fast.
123         '''
124         ''' I ported CityHash from C++ To C# In half an hour. A Java port should be straightforward too.
125         '''
126         ''' Just XORing the bits doesn't give as good a distribution (though admittedly that will be very fast).
127         '''
128         ''' I'm not familiar enough with Java to tell you exactly how to populate a long from a byte array 
129         ''' (there could be a good helper I'm not familiar with, or I could get some details of arithmetic 
130         ''' in Java wrong). 
131         ''' Essentially, though, you'll want to do something like this:
132         '''
133         ''' Long a = md5[0] * 256 * md5[1] + 256 * 256 * md5[2] + 256 * 256 * 256 * md5[3];
134         ''' Long b = md5[4] * 256 * md5[5] + 256 * 256 * md5[6] + 256 * 256 * 256 * md5[7];
135         ''' Long result = a ^ b;
136         ''' 
137         ''' Note I have made no attempt To deal With endianness. If you just care about a consistent hash value, 
138         ''' though, endianness should Not matter.
139         ''' </remarks>
140         <ExportAPI("As.Long")> <Extension>
141         Public Function ToLong(bytes As Byte()) As Long
142             Dim md5 As Long() = bytes.Select(Function(x) CLng(x)).ToArray
143             Dim a As Long = md5(0) * 256 * md5(1) + 256 * 256 * md5(2) + 256 * 256 * 256 * md5(3)
144             Dim b As Long = md5(4) * 256 * md5(5) + 256 * 256 * md5(6) + 256 * 256 * 256 * md5(7)
145             Dim result As Long = a Xor b
146
147             Return result
148         End Function
149
150         ''' <summary>
151         ''' 由于md5是大小写无关的,故而在这里都会自动的被转换为小写形式,所以调用这个函数的时候不需要在额外的转换了
152         ''' </summary>
153         ''' <param name="hex"></param>
154         ''' <returns></returns>
155         <ExportAPI("As.Bytes")>
156         Public Function StringToByteArray(hex As StringAs Byte()
157             Dim NumberChars As Integer = hex.Length
158             Dim bytes As Byte() = New Byte(NumberChars / 2 - 1) {}
159
160             hex = hex.ToLower  ' MD5是大小写无关的
161
162             For i As Integer = 0 To NumberChars - 2 Step 2
163                 bytes(i / 2) = Convert.ToByte(hex.Substring(i, 2), 16)
164             Next
165             Return bytes
166         End Function
167
168         <MethodImpl(MethodImplOptions.AggressiveInlining)>
169         <ExportAPI("Md5")>
170         Public Function GetMd5Hash(input As Byte()) As String
171             Return New Md5HashProvider().GetMd5Hash(input)
172         End Function
173
174         <Extension>
175         Public Function GetMd5Hash2(Of T)(x As T) As String
176             Dim raw As String = x.ToString & x.GetHashCode
177             Return raw.GetMd5Hash
178         End Function
179
180         <Extension>
181         Public Function GetMd5Hash(Of T As Language.BaseClass)(x As T) As String
182             Dim raw As String = x.__toString & x.GetHashCode
183             Return raw.GetMd5Hash
184         End Function
185
186         ''' <summary>
187         ''' Verify a hash against a string. 
188         ''' </summary>
189         ''' <param name="input"></param>
190         ''' <param name="comparedHash"></param>
191         ''' <returns></returns>
192         ''' <remarks></remarks>
193         ''' 
194         <ExportAPI("Md5.Verify"Info:="Verify a hash against a string.")>
195         Public Function VerifyMd5Hash(input As String, comparedHash As StringAs Boolean
196             If String.IsNullOrEmpty(input) OrElse String.IsNullOrEmpty(comparedHash) Then
197                 Return False
198             End If
199
200             Dim hashOfInput As String = GetMd5Hash(input)  ' Hash the input. 
201             Return String.Equals(hashOfInput, comparedHash, StringComparison.OrdinalIgnoreCase)
202         End Function 'VerifyMd5Hash
203
204         ''' <summary>
205         ''' 校验两个文件的哈希值是否一致
206         ''' </summary>
207         ''' <param name="query"></param>
208         ''' <param name="subject"></param>
209         ''' <returns></returns>
210         ''' <remarks></remarks>
211         ''' 
212         <ExportAPI("File.Equals")>
213         Public Function VerifyFile(query As String, subject As StringAs Boolean
214             Dim md5 As New Md5HashProvider()
215             Dim a1 As Byte() = IO.File.ReadAllBytes(query)
216             Dim a2 As Byte() = IO.File.ReadAllBytes(subject)
217             Dim m1 As String = md5.GetMd5Hash(a1)
218             Dim m2 As String = md5.GetMd5Hash(a2)
219
220             Return String.Equals(m1, m2)
221         End Function
222
223         ''' <summary>
224         ''' Get the md5 hash calculation value for a specific file.(获取文件对象的哈希值,请注意,当文件不存在或者文件的长度为零的时候,会返回空字符串)
225         ''' </summary>
226         ''' <param name="PathUri">The file path of the target file to be calculated.</param>
227         ''' <returns></returns>
228         ''' <remarks></remarks>
229         ''' 
230         <ExportAPI("File.Md5"Info:="Get the md5 hash calculation value for a specific file.")>
231         <Extension>
232         Public Function GetFileHashString(<Parameter("Path.Uri""The file path of the target file to be calculated.")> PathUri As StringAs String
233             If Not PathUri.FileExists OrElse FileIO.FileSystem.GetFileInfo(PathUri).Length = 0 Then
234                 Return ""
235             End If
236
237             Dim bufs As Byte() = IO.File.ReadAllBytes(PathUri)
238             Return GetMd5Hash(bufs)
239         End Function
240
241         ''' <summary>
242         ''' SHA256 8 bits salt value for the private key.
243         ''' </summary>
244         ''' <param name="value"></param>
245         ''' <returns></returns>
246         <ExportAPI("SaltValue"Info:="SHA256 8 bits salt value for the private key.")>
247         Public Function SaltValue(value As StringAs String
248             Dim hash As String = GetMd5Hash(value)
249             Dim chars() = {
250                 hash(0),
251                 hash(1),
252                 hash(3),
253                 hash(5),
254                 hash(15),
255                 hash(23),
256                 hash(28),
257                 hash(31)
258             }
259             Return chars.CharString
260         End Function
261     End Module
262 End Namespace