Windows provides a built-in for normalizing strings, the NormalizeString function. However, it can be a bit tricky to use.
Here is an implementation, based on the C example in the docs provided above:
'Declare the function Public Declare PtrSafe Function NormalizeString Lib "Normaliz.dll" (ByVal NormForm As Byte, ByVal lpSrcString As LongPtr, ByVal cwSrcLength As Long, ByVal lpDstString As LongPtr, ByVal cwDstLength As Long) As Long 'And a relevant error code Const ERROR_INSUFFICIENT_BUFFER = 122 'And a helper enum Public Enum NORM_FORM NormalizationC = &H1 NormalizationD = &H2 NormalizationKC = &H5 NormalizationKD = &H6 End Enum 'Available normalization forms can be found under https://learn.microsoft.com/en-us/windows/win32/api/winnls/ne-winnls-norm_form 'KD normalization is preferred(https://stackoverflow.com/a/16173329/7296893) when hashing characters 'If you already have hashes stored, C normalization is least likely to break them Public Function UnicodeNormalizeString(str As String, Optional norm_form As Byte = NormalizationKD) As String If Len(str) = 0 Then 'Zero-length strings can't be normalized UnicodeNormalizeString = str Exit Function End If Dim outlenestimate As Long 'Get an initial length estimate for the string outlenestimate = NormalizeString(norm_form, StrPtr(str), Len(str), 0, 0) Dim i As Long 'Try 10 times For i = 1 To 10 'Initialize buffer UnicodeNormalizeString = String(outlenestimate, vbNullChar) 'Get either the normalized string, or a new length estimate outlenestimate = NormalizeString(norm_form, StrPtr(str), Len(str), StrPtr(UnicodeNormalizeString), outlenestimate) If outlenestimate > 0 Then 'We got the normalized string 'Truncate off the unused characters UnicodeNormalizeString = Left(UnicodeNormalizeString, outlenestimate) Exit Function Else If Err.LastDllError <> ERROR_INSUFFICIENT_BUFFER Then Exit For 'An unexpected error occurred End If outlenestimate = outlenestimate * -1 'Use the new length estimate, try again End If Next Err.Raise 5000, Description:="Failure to normalize unicode string" End Function
Once you have declared the normalization function, always run your password through it before hashing:
If SomeHashFun(UnicodeNormalizeString(MyPassword)) = SomeHashedPassword Then 'We are in! End If