调用advapi.dll使用的PInvoke:CryptDecrypt和CryptEncrypt意外的行为 [英] Calling advapi.dll using Pinvoke: CryptDecrypt and CryptEncrypt unexpected behaviour

查看:577
本文介绍了调用advapi.dll使用的PInvoke:CryptDecrypt和CryptEncrypt意外的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是从<一个跟进href="http://stackoverflow.com/questions/20543777/using-ms-crypto-library-on-server-2012-cryptcreatehash-error-$c$c-87-error-in">this问题。

我打电话从VB.net的WinAPI的加密功能,以确保与传统产品共享所产生的数据的兼容性。

I am calling the WinAPI Cryptography functions from VB.net to ensure compatibility with a legacy product that shares the resulting data.

在code精细运作多年但是我最近碰到一个问题,在Windows Server 2012上运行时服用后看看不同的例子,并回答我的previous问题,我给了$ C C时检修$,包括改变什么previously一个字符串变量为一个字节数组,我相信这是比较正确的功能。它是如何previously合作过的字符串我不知道,但固定原Server 2012的问题后,这是字符串的失败是由于NTE_BAD_LENGTH。

The code functioned fine for many years however I have recently run into a problem when running on Windows Server 2012. After taking a look at various examples and the answer to my previous question I gave the code an overhaul, including changing what was previously a string variable to a Byte array, which I believe is more correct functionality. How it previously ever worked with string I have no idea, but after fixing the original Server 2012 issue it was failing due to NTE_BAD_LENGTH of the string.

现在,该字符串转换为字节数组后,code没有出现在相同的方式,它的确previously加密,也不会解密被CryptEncrypt生成的​​内容。

Now, after converting the string to a byte array, The code does not appear to encrypt in the same way it did previously, nor will it decrypt what is generated by CryptEncrypt.

的code主要大宗如下:

The main bulk of the code follows:

(拨打CryptCreateHash,CryptHashData,CryptDeriveKey precede这一点)

(Calls to CryptCreateHash, CryptHashData, CryptDeriveKey precede this)

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) 

我的方法的原型定义如下:

My method prototypes are defined as:

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
明文数据缓冲区: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? ??? _

An Example output is as follows:
Test Data: Testing123
PlainText Data in buffer: 84, 101, 115, 116, 105, 110, 103, 49, 50, 51,
Encrypted: CW?? ???_
Encrypted Data in buffer: 12, 67, 87, 133, 158, 9, 139, 250, 255, 95,
Decrypted Data in buffer: 12, 67, 87, 133, 158, 9, 139, 250, 255, 95,
Decrypted: CW?? ???_

正如你所看到的,解密后的缓冲保持完全一致。

As you can see, post decryption the buffer stays exactly the same.

我不知道为什么会这样,我唯一的理论是,它是与它的字符集,我编码字节()有,但我尝试了好几种,它使没有什么区别。这也可以应用到密码参数(传递给字节() CryptHashData )。

I have no idea why this is happening, my only theory is that it is something to do with which charset I am encoding the Byte() with, although I have tried several and it makes no difference. This may also apply to the password parameter (Byte() passed to CryptHashData).

任何帮助是极大的AP preciated。

Any help is greatly appreciated.

修改1: 感谢您的帮助,我今天早些时候实现的缓冲区拷贝的问题,我试图找出问题在我的测试应用程序,但最终在复杂的我的变量,是的。

EDIT 1: Thanks for the help, I realised the buffer copy issue earlier today, I was attempting to isolate the problem in my test app but ended up over complicating my variables, yes.

事实上,在VB中的数组进行实例化最高的索引,127创建一个长度128阵列,混淆我知道了。

Indeed, arrays in VB are instantiated with the highest index so 127 creates a length 128 array, confusing I know.

我相信我现在的问题在于编码,当你讨论。 previously的数据被移交到 CryptEncrypt / CryptDecrypt 作为.NET字符串,而不是先被转换为字节(),就像我现在这样。不过,我不知道如何非托管code会处理这个字符串,presumably默认.NET编码或者Windows的编码?

I believe my problem now lies with the encoding, as you discuss. Previously the data was handed into CryptEncrypt/CryptDecrypt as a .NET String instead of first being converted to a byte(), like I am doing now. However I do not know how the unmanaged code would have dealt with this string, presumably the default .NET encoding or maybe the Windows encoding?

我已经试过UTF8和统一code但没有将反映原来的code和encypt的行为/解密previously存储的字符串到其原始值。这也是我想继续使用非托管方法,以反映完全传统行为(使用非托管方法的VB6程序是用于生成加密的数据之前,它是解密在.NET)。

I have tried UTF8 and Unicode however neither will mirror the behaviour of the original code and encypt/decrypt previously stored strings to their original value. This is also the reason I want to continue to use the unmanaged methods, to mirror exactly legacy behaviour (A VB6 app using the unmanaged methods is used to generate the encrypted data before it is decrypted in .NET).

我的code现在进行加密和解密,但是,正如我所说,这些值输出不匹配的原始数据,尽管使用相同的密码技术。感谢您一直以来的帮助。

My Code now encrypts and decrypts, but, as I said, the values output do not match the original data, despite using the same cryptographic techniques. Thank you for your continued help.

推荐答案

作为加密的数据是因为你显示错误的缓冲区您解密的数据显示同样的原因。你解密,以 _encryptedBufferCopy 但复制 _bigBuffer _outBuffer ,以及显示,由于解密的数据。但 _bigBuffer 包含加密数据。

The reason your decrypted data appears the same as the encrypted data is because you're displaying the wrong buffer. You're decrypting to _encryptedBufferCopy but then copying _bigBuffer to _outBuffer, and displaying that as the decrypted data. But _bigBuffer contains the encrypted data.

您有太多的临时缓冲区。此外,这是一个有点不安地看到你分配与数组dataBufferLength - 1 。我试图弄清楚为什么 1 。或者是因为VB的坚持下,该参数是最高的指数,而不是长?

You have too many temporary buffers. Also, it's a little disconcerting to see you allocate an array with dataBufferLength - 1. I'm trying to figure out why the -1. Or is that because of VB's insistence that the parameter is the highest index rather than the length?

您还应该注意的是,最后一个参数 CryptDecrypt 的ByRef 。在函数返回时,也就是应该包含在解密后的明文字节数。您的来电 CryptDecrypt 正在通过 _encryptedBufferCopy.Length ,这似乎像它应该是一个错误。在任何情况下,功能将不能设置解密的长度

You should also note that the last parameter to CryptDecrypt is ByRef. When the function returns, that is supposed to contain the number of bytes in the decrypted plaintext. Your call to CryptDecrypt is passing _encryptedBufferCopy.Length, which seems like it should be an error. In any case, the function won't be able to set the decrypted length.

你将有一个问题是,你使用ASCII编码。 ASCII码是7位编码,这意味着,如果你有codeS任何字符超过127,调用 encoding.GetBytes(textString)将会给你字符,对于那些高位集。

One problem you're going to have is that you're using ASCII encoding. ASCII is a 7-bit encoding, which means that if you have any characters with codes over 127, calling encoding.GetBytes(textString) is going to give you ? characters for those with the high bit set.

可能是你最好的选择是使用 Encoding.UTF8

Probably your best bet is to use Encoding.UTF8.

一旦数据被加密的,你不能可靠地把它变成一个字符串,通过调用 encoding.GetString 。该数据缓冲区可以包含的任意的双字节值,包括哪些内容会被认为可能不会显示控制字符,可能会得出一些奇怪的字符,或谁知道。所以,你必须:

Once the data is encrypted, you can't reliably turn it into a string by calling encoding.GetString. That data buffer can contain any byte value, including what would be considered control characters that might not be displayed, might draw funny characters, or who knows what. So where you have:

''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

这几乎肯定会产生无法显示的字符串。该字符,你现在看到的重新present字节以上127字符值,它的ASCII编码不会跨preT正确。

That's almost certain to create a string that can't be displayed. The ? characters you're seeing here represent bytes with character values above 127, which your ASCII encoding won't interpret correctly.

所有的事情考虑,我不知道为什么你不只是使用托管API中的 System.Security.Cryptography 命名空间这一点。我想你会发现它的容易得多,而且你不必担心把手和32/64位的问题,等等。

All things considered, I'm wondering why you don't just use the managed APIs in the System.Security.Cryptography namespace for this. I think you'll find that it's much easier, and you don't have to worry about handles and 32/64 bit issues, etc.

这篇关于调用advapi.dll使用的PInvoke:CryptDecrypt和CryptEncrypt意外的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆