[英]Pinvoking adwapi.dll - cryptDecrypt and cryptEncrypt functions, weird problem
[英]Calling advapi.dll using Pinvoke: CryptDecrypt and CryptEncrypt unexpected behaviour
這是該問題的后續措施。
我正在從VB.net調用WinAPI加密功能,以確保與共享結果數據的舊產品兼容。
該代碼可以正常工作很多年,但是最近我在Windows Server 2012上運行時遇到了問題。在查看了各種示例和上一個問題的答案之后,我對代碼進行了全面檢查,包括更改了以前的字符串變量為字節數組,我認為這是更正確的功能。 我以前不知道它如何與字符串一起工作,但是在解決了原始的Server 2012問題之后,由於字符串的NTE_BAD_LENGTH而失敗。
現在,將字符串轉換為字節數組后,該代碼似乎無法像以前一樣進行加密,也不會解密CryptEncrypt生成的內容。
該代碼的主要內容如下:
(在此之前調用CryptCreateHash,CryptHashData,CryptDeriveKey)
Dim _encoding As System.Text.ASCIIEncoding = New System.Text.ASCIIEncoding()
Dim data As String = "Testing123" ''define test String
Debug.WriteLine("Test Data: " & data)
''convert data to Byte()
Dim _bigBuffer As Byte() = New Byte(127) {} ''Buffer to be encrypted
Dim _dataBuffer As Byte() = _encoding.GetBytes(data) ''Get the text to be encrypted into a byte()
Buffer.BlockCopy(_dataBuffer, 0, _bigBuffer, 0, _dataBuffer.Length) ''Copy the data into the big buffer
Dim _dataBufferLength As UInteger = Convert.ToUInt32(_dataBuffer.Length) ''Get the length
Debug.Write("PlainText Data in buffer: ")
For Each _b As Byte In _dataBuffer
Debug.Write(_b & ", ")
Next
''Encrypt data.
If CryptoApi.CryptEncrypt(_hKey, IntPtr.Zero, True, 0, _bigBuffer, _dataBufferLength, _bigBuffer.Length) = False Then
Dim _error As Integer = Err.LastDllError
Throw New Exception("Error during CryptEncrypt. Error Code: " & _error.ToString)
End If
''print out the encrypted string
Dim _encryptedDataBuffer As Byte() = New Byte(_dataBufferLength - 1) {} ''Buffer, size is size of the output encrypted string
Buffer.BlockCopy(_bigBuffer, 0, _encryptedDataBuffer, 0, _dataBufferLength) ''copy from output buffer to new buffer
Dim _encryptedStringFromBuffer As String = _encoding.GetString(_encryptedDataBuffer) ''Decode buffer back to string
Debug.WriteLine("")
Debug.WriteLine("Encrypted: " & _encryptedStringFromBuffer) ''Write encrypted string from buffer
Debug.Write("Encrypted Data in buffer: ")
For Each _b As Byte In _encryptedDataBuffer
Debug.Write(_b & ", ")
Next
''Copy encrypted strings buffer, ready to decrypted
Dim _encryptedBufferCopy As Byte() = New Byte(127) {} ''new buffer to hold encrypted data
Buffer.BlockCopy(_bigBuffer, 0, _encryptedBufferCopy, 0, _bigBuffer.Length) ''copy the output of encryption
Dim _inputLengthCopy As UInteger = _dataBufferLength ''Copy the length of encrypted data
''Decrypt data.
If CryptoApi.CryptDecrypt(_hKey, IntPtr.Zero, True, 0, _encryptedBufferCopy, _encryptedBufferCopy.Length) = False Then
Dim _error As Integer = Err.LastDllError
Throw New Exception("Error during CryptDecrypt. Error Code: " & _error.ToString)
End If
Dim _outBuffer As Byte() = New Byte(_dataBufferLength - 1) {}
Buffer.BlockCopy(_bigBuffer, 0, _outBuffer, 0, _dataBufferLength)
Dim _stringFromBuffer As String = _encoding.GetString(_outBuffer)
Debug.WriteLine("")
Debug.Write("Decrypted Data in buffer: ")
For Each _b As Byte In _outBuffer
Debug.Write(_b & ", ")
Next
Debug.WriteLine("")
Debug.WriteLine("Decrypted: " & _stringFromBuffer)
我的方法原型定義為:
Friend Declare Function CryptEncrypt Lib "advapi32.dll" _
(ByVal hKey As IntPtr, _
ByVal hHash As IntPtr, _
<MarshalAs(UnmanagedType.Bool)> ByVal final As Boolean, _
ByVal dwFlags As Integer, _
ByVal data As Byte(), _
ByRef pdwDataLen As Integer, _
ByVal dwBufLen As Integer) _
As <MarshalAs(UnmanagedType.Bool)> Boolean
Friend Declare Function CryptDecrypt Lib "advapi32.dll" _
(ByVal hKey As IntPtr, _
ByVal hHash As IntPtr, _
<MarshalAs(UnmanagedType.Bool)> ByVal final As Boolean, _
ByVal dwFlags As Integer, _
ByVal data As Byte(), _
ByRef pdwDataLen As Integer) _
As <MarshalAs(UnmanagedType.Bool)> Boolean
示例輸出如下:
測試數據:Testing123
緩沖區中的PlainText數據:84、101、115、116、105、110、103、49、50、51,
加密:CW? ??? _
緩沖區中的加密數據:12、67、87、133、158、9、139、250、255、95,
緩沖區中的解密數據:12,67,87,133,158,9,139,250,255,95,
解密:CW? ??? _
如您所見,解密后緩沖區保持完全相同。
我不知道為什么會這樣,我唯一的理論是,這與我用來編碼Byte()的字符集有關,盡管我嘗試了幾次,但沒有區別。 這也可能適用於密碼參數(傳遞給CryptHashData
Byte())。
任何幫助是極大的贊賞。
編輯1:感謝您的幫助,我今天早些時候意識到緩沖區復制問題,我試圖在我的測試應用程序中隔離問題,但最終導致變量變復雜,是的。
實際上,VB中的數組是使用最高索引實例化的,所以127創建了一個長度為128的數組,這讓我感到困惑。
正如您所討論的,我相信我的問題現在出在編碼上。 以前,數據是作為.NET字符串傳遞到CryptEncrypt
/ CryptDecrypt
,而不是像我現在所做的那樣首先轉換為byte()。 但是我不知道非托管代碼將如何處理該字符串,大概是默認的.NET編碼還是Windows編碼?
我已經嘗試了UTF8和Unicode,但是它們都不會鏡像原始代碼的行為,也不會將以前存儲的字符串加密/解密為原始值。 這也是我要繼續使用非托管方法以完全反映舊行為的原因(使用非托管方法的VB6應用程序用於在.NET中解密之前生成加密數據)。
我的代碼現在可以加密和解密,但是,正如我所說,盡管使用了相同的加密技術,但輸出的值與原始數據不匹配。 感謝您一直以來的幫助。
解密后的數據看起來與加密后的數據相同的原因是因為您顯示了錯誤的緩沖區。 您正在解密到_encryptedBufferCopy
,然后將_bigBuffer
復制到_outBuffer
,並將其顯示為解密的數據。 但是_bigBuffer
包含加密的數據。
您有太多的臨時緩沖區。 另外,看到您使用dataBufferLength - 1
分配數組有點令人不安。 我試圖弄清楚為什么-1
。 還是因為VB堅持認為參數是最高索引而不是長度?
您還應該注意, CryptDecrypt
的最后一個參數是ByRef
。 當函數返回時,該函數應包含已解密明文中的字節數。 您對CryptDecrypt
調用傳遞了_encryptedBufferCopy.Length
,這似乎應該是一個錯誤。 無論如何,該功能將無法設置解密長度。
您將要遇到的一個問題是您正在使用ASCII編碼。 ASCII是一種7位編碼,這意味着如果您有任何字符的代碼超過127,則調用encoding.GetBytes(textString)
將為您提供?
高位設置的字符。
最好的選擇是使用Encoding.UTF8
。
數據加密后,就無法通過調用encoding.GetString
可靠地將其轉換為字符串。 該數據緩沖區可以包含任何字節值,包括可能不顯示的控制字符,可能繪制有趣的字符或誰知道的字符。 所以你有:
''print out the encrypted string
Dim _encryptedDataBuffer As Byte() = New Byte(_dataBufferLength - 1) {} ''Buffer, size is size of the output encrypted string
Buffer.BlockCopy(_bigBuffer, 0, _encryptedDataBuffer, 0, _dataBufferLength) ''copy from output buffer to new buffer
Dim _encryptedStringFromBuffer As String = _encoding.GetString(_encryptedDataBuffer) ''Decode buffer back to string
幾乎可以肯定會創建一個無法顯示的字符串。 ?
您在此處看到的字符表示字符值大於127的字節,您的ASCII編碼將無法正確解釋這些字符。
考慮到所有問題,我想知道為什么您不僅僅使用System.Security.Cryptography
命名空間中的托管API。 我認為您會發現它容易得多,而且您不必擔心句柄和32/64位問題等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.