|
It seems m_wndColorBar is a variable of type CMFCToolBar not CToolBar. Can you check what is the base class of CMFCToolbar??. It is supposed to be a child of CToolBar. But seems it is not
|
|
|
|
|
I don't understand why I can do CryptCreateHash with CALG_MD5 no problem, but when I use CALG_DES, It fails.
I want just the Basic DES Encryption, using a pair of keys. I suspect that I have the wrong CryptAcquireContext Statement, in which I may be calling the wrong service, but I can't find any reference other than PROV_RS_FULL. I did some research and I'll swear that the DES is a member of the MS_STRONG_PROV.
My Goal is to replicate a function in asp.net to c++
bResult = CryptAcquireContextW( &hProv, 0, MS_STRONG_PROV, PROV_RSA_FULL, 0);
if(!bResult){
bResult=CryptAcquireContext(&hProv, NULL, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET);
return 0;
}
bResult = CryptCreateHash(hProv, CALG_DES, 0, 0, &hHash);
asp.net function:
Dim cryptoProvider As DESCryptoServiceProvider = New DESCryptoServiceProvider()
Dim ms As MemoryStream = New MemoryStream()
Dim cs As CryptoStream = New CryptoStream(ms, cryptoProvider.CreateEncryptor(KEY_64, IV_64), _
CryptoStreamMode.Write)
Dim sw As StreamWriter = New StreamWriter(cs)
|
|
|
|
|
Call GetLastError after using CryptCreateHash and see what the error is (and tell us). See this[^] or this[^] for error codes.
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
> If it doesn't matter, it's antimatter.<
|
|
|
|
|
I should of done that last night, was really tired, this is my 4th time though the program.
The code is 2148073480
2148073480 NTE_BAD_ALGID: Invalid algorithm specified.
|
|
|
|
|
Yeah, it happens... anyways, i think Richard got the right answer.
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
> If it doesn't matter, it's antimatter.<
|
|
|
|
|
I just tried this and received error code 0x80090008 which means "Invalid algorithm specified", so I guess the implementation does not match the documentation.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
My brain finally caught up ...
CALG_DES is an encryption algorithm, not a hash algorithm so cannot be used in a call to CryptCreateHash() .
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
These 3 are qualified to support DES
Microsoft Base Cryptographic Provider v1.0 - DES
Microsoft Strong Cryptographic Provider v2.0 - 3DES
Microsoft Enhanced Cryptographic Provider v1.0 - 3DES
The page below list DES as a member of Microsoft Enhanced Cryptographic Provider
http://msdn.microsoft.com/en-us/library/windows/desktop/aa386986%28v=vs.85%29.aspx[^]
But on running MS_ENHANCED_PROV and calling CALG_DES to CreateHash
error 3435973836
NTE_BAD_UID
I'll do some more reading and get something to match.
|
|
|
|
|
You missed something; read my comments again - carefully.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Hi there,
What Richard is saying is that DES[^] is a block cipher encryption algorithm. The CryptCreateHash function[^] does not encrypt anything... it creates a hash of the data passed into the function.
Some wiki information about hash functions:
Cryptographic hash function[^].
If you want to use the DES algorithm then you will need to use the CryptEncrypt function[^] but there is a small learning curve when working with the Microsoft Cryptography Functions... providers, contexts, keys, key stores...
Acquiring a Cryptographic Context and Generating Keys[^]
If you are looking for a quick copy-and-paste code snippit then you might want to take a look at one of my ancient posts from the Mesozoic era: Re: decrypting question[^]
CPallini still had hair back then.
Best Wishes,
-David Delaune
|
|
|
|
|
I wrote the hash function first, and tried to reuse the parts of code to write the encryption function.
I never did understand why I still had hash stuff, but most of the documentation I could find for symmetrical ???, using key IV pair values pointed me in that direction.
So I need to start all over again with a stronger understanding of how it works.
I will read your links.
Thanks, will let you know what I cook up here on round 5.
|
|
|
|
|
You code sample is what I was looking for. I have some questions if you don't mind.
Can I replace LPCBYTE with BYTE*
I added odbcss.h, but it adds a bunch of sql server stuff that I don't need.
Would Password be my KEY_192 with 24 bytes?
So I can add my IV_192 using CreateKeyParam?
|
|
|
|
|
jkirkerx wrote: You code sample is what I was looking for.
Great.
jkirkerx wrote: Can I replace LPCBYTE with BYTE*
Yes, you could do that. LPCBYTE is a Long Pointer to a Const[^] BYTE. It is good programming practice to pass a variable as constant if you do not want the variable to be modified. You have my permission to be a rogue programmer.
jkirkerx wrote: I added odbcss.h, but it adds a bunch of sql server stuff that I don't need.
Ok.
jkirkerx wrote: Would Password be my KEY_192 with 24 bytes?
So I can add my IV_192 using CreateKeyParam?
I read C/C++ better than I read English even though it is my native language. Show me some code.
Best Wishes,
-David Delaune
|
|
|
|
|
The asp.net version of the program used a key pair. Your example is very similar to what I wrote the 4th time, except you had the Base64 piece that I needed.
So in my 4th version, I had the CryptSetKeyParam to enter the IV for the symmetric key, to complete the pair, but in all honestly, I'm not really sure how it's used, because the asp.net version feed both into a function, and the underlying functionality was hidden.
Side question, How do get the length of a BYTE* for pwlen
BYTE KEY_192[24] = {42, 16, 93, 156, 78, 4, ...
BYTE IV_192[24] = {55, 103, 246, 79, 36, ...
CryptAcquireContext(&hProv, NULL, MS_STRONG_PROV, PROV_RSA_FULL, 0);
CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
CryptHashData(hHash, KEY_192, pwlen would be 24, 0);
CryptDeriveKey(hProv, CALG_3DES, hHash, 0, &hKey);
CryptSetKeyParam(hKey, KP_IV, IV_192, 0);
I like the idea of a definable password, so each copy of my program can have it's own unique key.
hmm.
|
|
|
|
|
jkirkerx wrote: Your example is very similar to what I wrote the 4th time, except you had the Base64 piece that I needed.
Great.
jkirkerx wrote: Side question, How do get the length of a BYTE* for pwlen
A BYTE is an unsigned char and typically for normal NULL terminated strings you would use strlen[^] or _tcslen[^] for obtaining the length of the string excluding the NULL terminator. This would be very common for username/password/key string variables.
However... in your case you are passing a constant array of BYTE so you could use the sizeof operator[^] to get the length in bytes.
CryptHashData(hHash, KEY_192, sizeof(KEY_192), 0);
I do not see anything wrong with the CryptSetKeyParam function you have shown. Are you receiving an error here?
Best Wishes,
-David Delaune
|
|
|
|
|
I went back to what I had before I posted, and changed CALG_DES to CALG_MD5. I don't get an error, , I'm just struggling in making my buffer that passes in/out in CryptEncrypt.
I get a value back that's sort or weird. It works, but I think the size of my buffer is too large.
Rã¾Î‚ò.¢Ó`Ï<oÝV|63¾ˆWãÔZ÷(šœ(è~Ó¥¶ãòõ¬"oÌtðaÏMûÏþƒÝÌXðýýýý««««««««îþîþîþîþîþîþ
I think the end in bold is a leak somewhere, or the buffer being too large
I know I need to CryptEncrypt once to get the buffer size, and do it again for the real thing.
I did a conversion to UTF8, that I think I need to dump. I'm going to dump it, and work on the buffer sizing.
This is what I had before I posted, with the one small change. It's not bad considering I had no clue what to do, but it's all I need, with the base64 at the end of the gig.
WCHAR* CA_Encryption::_encrypt_DES( WCHAR *pzInput )
{
BOOL bResult = FALSE;
WCHAR *szOutput = NULL;
DWORD cbKey64 = sizeof(KEY_64);
HCRYPTPROV hProv;
HCRYPTHASH hHash = NULL;
HCRYPTKEY hKey = NULL;
const int DES_SIZE = 64;
static BYTE hash[DES_SIZE];
DWORD buffer_size = sizeof(hash);
DWORD buffer_length;
int iChar = WideCharToMultiByte(CP_UTF8, 0, pzInput, -1, NULL, 0, NULL, NULL);
char *szInput = new char[iChar];
WideCharToMultiByte(CP_UTF8, 0, pzInput, -1, szInput, iChar, NULL, NULL);
bResult = CryptAcquireContextW( &hProv, 0, MS_STRONG_PROV, PROV_RSA_FULL, 0);
bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
bResult = CryptHashData(hHash, KEY_64, sizeof(KEY_64), 0);
bResult = CryptDeriveKey(hProv, CALG_DES, hHash, 0, &hKey);
bResult = CryptSetKeyParam(hKey, KP_IV, IV_64, 0);
buffer_length = strlen((char *)szInput);
BYTE *szBuffer = new BYTE[64];
memcpy( szBuffer, (BYTE*)szInput, sizeof(szInput) );
delete [] szInput;
bResult = CryptEncrypt(hKey, 0, FALSE, 0, szBuffer, &buffer_size, buffer_length);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return szOutput;
}
This is what I'm trying to copy from asp.net
Public Shared Function Encrypt(ByVal value As String) As String
If value <> "" Then
Dim cryptoProvider As DESCryptoServiceProvider = New DESCryptoServiceProvider()
Dim ms As MemoryStream = New MemoryStream()
Dim cs As CryptoStream = New CryptoStream(ms, cryptoProvider.CreateEncryptor(KEY_64, IV_64), _
CryptoStreamMode.Write)
Dim sw As StreamWriter = New StreamWriter(cs)
sw.Write(value)
sw.Flush()
cs.FlushFinalBlock()
ms.Flush()
Return Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length)
Else
Encrypt = ""
End If
End Function
|
|
|
|
|
jkirkerx wrote: I get a value back that's sort or weird. It works, but I think the size of my buffer is too large.
Rã¾Î‚ò.¢Ó`Ï<oÝV|63¾ˆWãÔZ÷(šœ(è~Ó¥¶ãòõ¬"oÌtðaÏMûÏþƒÝÌXðýýýý««««««««îþîþîþîþîþîþ
I actually recognize those bytes.... the ýýýý are 0xFD and called Guard Bytes. This is how Visual Studio detects when you overwrite a buffer. It checks to see if they are still there.
The next bytes are: «««««««« and are created by HeapAlloc and we call them 0xAB 'No Man's Land' Guard Bytes.
The last bytes are: îþîþîþîþîþîþ are 0xFE and indicate a region of Freed Heap memory.
Think about this for 3 seconds... why do you think you see this wierdness? You see this because 'C' strings are supposed to be NULL terminated. The encrypted memory you just created has no NULL terminator so the Visual Studio debugger shows you the entire contents...
You can fix this by adding this immediately after your CryptEncrypt function call.
szBuffer[buffer_size] = 0;
This will allow Visual Studio to properly display the encrypted bytes as a NULL terminated string.
But by writing a zero there... you just overwrote 1 0xFD guard byte. So don't do it. Heh, I knew you were a rogue software engineer all along.
Btw, congratulations... this code looks like it would actually work correctly. You should probably add more error handling and clean it up a bit but looks OK.
Don't forget to sanitize your buffers with the SecureZeroMemory[^]. Otherwise EvilHacker1337 might be able to retrieve stale memory contents to retrieve sensitive data. (Looks like your key is hard-coded so its not like it would help much anyway)
Best Wishes,
-David Delaune
|
|
|
|
|
I'm trying to get a perfect match on my encryption program, so it matches my asp.net version. It looks like the code you approved actually works correctly, and I'm down to the Base64 part.
Is there a difference between your Base64Encode function and the function on this page?
http://msdn.microsoft.com/en-us/library/dhx0d524.aspx[^]
When looking at CryptBinaryToString, I'm not sure if it implies take a encryptedbinary and convert it to string, or encrypt a binary and convert to string.
|
|
|
|
|
Hey there,
jkirkerx wrote: I'm trying to get a perfect match on my encryption program, so it matches my asp.net version. It looks like the code you approved actually works correctly, and I'm down to the Base64 part.
Excellent.
jkirkerx wrote: Is there a difference between your Base64Encode function and the function on this page?
http://msdn.microsoft.com/en-us/library/dhx0d524.aspx[^]
If you are referring to the code sample in this post: Re: decrypting question[^]
Yeah... I remember helping that guy over a number of weeks and he was complaining about the Microsoft CryptBinaryToString function[^] appending two characters... a carriage return [CR] and line feed [LF] ... so the code sample I provided him removed those characters.
This would probably cause your strings to not match if the ASP version is doing the same... this is the code that removes the CRLF:
TCHAR pByteLF = *(LPWORD)(lpszBase64 + dwResult -1);
TCHAR pByteCR = *(LPWORD)(lpszBase64 + dwResult -2);
if(pByteCR == 0x0D && pByteLF == 0x0A)
{
*(LPWORD)(lpszBase64 + dwResult -2) = 0;
}
It looks like I was planning on making it optional to strip those characters... if you look at the function it contains a BOOL bStripCRLF
You could probably just finish the functionality that I left incomplete...
if(TRUE == bStripCRLF)
{
TCHAR pByteLF = *(LPWORD)(lpszBase64 + dwResult -1);
TCHAR pByteCR = *(LPWORD)(lpszBase64 + dwResult -2);
if(pByteCR == 0x0D && pByteLF == 0x0A)
{
*(LPWORD)(lpszBase64 + dwResult -2) = 0;
}
}
Today is Saturday so I will be coding for the next few hours. If you like... show me a sample with key, initialization vector, and output along with desired output. Don't post the key you intend to use in final production. With this I might be able to see what is causing any discrepancies.
Best Wishes,
-David Delaune
|
|
|
|
|
Sorry for the late reply, I wanted to eliminate as many possibilities as I could.
Well, this requires an expert in encryption, only because I want to match a asp.net result. Otherwise I would just go with it, and be a happy camper. It's beyond my skill level.
The base 64 seems fine.
It's either this MS_ENHANCED_PROV, PROV_RSA_FULL, or the key not being in the right format, or perhaps 5 other possibilities.
This is really important to me, I need it to support another program I wrote.
I have an easy to load sample that is fully documented, with notes from the asp.net side.
|
|
|
|
|
Hi,
Wow, what time zone are you in? The sun was rising out in the Atlantic ocean when you responded.
jkirkerx wrote: It's either this MS_ENHANCED_PROV, PROV_RSA_FULL, or the key not being in the right format, or perhaps 5 other possibilities.
Your not giving enough information... you need to tell me exactly which algorithm is not returning the expected value. The DES, 3DES, MD5 and Base64 algorithms are very standard... the only thing that would make them return an unexpected value... is an initialization vector or hash salt.
If you control both the ASP.NET and C++ source codes... could you step through a debugger in both and determine exactly which algorithm returns a different value?
The CryptSetKeyParam function[^] with either KP_SALT, KP_SALT_EX, KP_IV would potentially cause an encryption/hash algorithm to return an unexpected value. This is exactly what it is suppose to do...
You need to make sure salt and initialization vectors are the same in both your ASP.NET and C++ implementations.
Best Wishes,
-David Delaune
[EDIT]
I just recieved and read your e-mail. I'll take a look the source code you provided and we can correspond through e-mail from here. -Dave
modified 18-Dec-11 11:36am.
|
|
|
|
|
I'm in Huntington Beach CA. I think I'm the only programer in the city.
I was determined to figure it out last night, and went through it with a magnifying glass. Before I crashed, I decided that I should try to write the encryption in vb, so I just converted the one I used to this one that makes more sense to me. The new function produces the same result as the old function to the tee.
Old Function
Public Shared Function Encrypt(ByVal value As String) As String
If value <> "" Then
Dim cryptoProvider As DESCryptoServiceProvider = New DESCryptoServiceProvider()
Dim ms As MemoryStream = New MemoryStream()
Dim cs As CryptoStream = New CryptoStream(ms, cryptoProvider.CreateEncryptor(KEY_64, IV_64), _
CryptoStreamMode.Write)
Dim sw As StreamWriter = New StreamWriter(cs)
sw.Write(value)
sw.Flush()
cs.FlushFinalBlock()
ms.Flush()
Return Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length)
Else
Encrypt = ""
End If
End Function
New Function
Public Shared Function _encrypt_DES(ByVal szInputW As String) As String
Dim szOutput As String = Nothing
Dim szBufferIn() As Byte = Nothing
Dim szBufferOut() As Byte = Nothing
If szInputW <> "" Then
Dim cryptoProvider As DESCryptoServiceProvider = New DESCryptoServiceProvider()
With cryptoProvider
.BlockSize = 64
.FeedbackSize = 8
.Key = KEY_64
.IV = IV_64
.Mode = CipherMode.CBC
.Padding = PaddingMode.PKCS7
End With
Dim symAlg As SymmetricAlgorithm = cryptoProvider
Dim xfrm As ICryptoTransform = Nothing
szBufferIn = UTF8Encoding.UTF8.GetBytes(szInputW)
xfrm = symAlg.CreateEncryptor()
szBufferOut = xfrm.TransformFinalBlock(szBufferIn, 0, szBufferIn.Length)
szOutput = Convert.ToBase64String(szBufferOut, 0, szBufferOut.Length)
Else
szOutput = ""
End If
Return szOutput
End Function
I think this is the part I'm missing in my c++ version, perhaps only the final block of the buffer byte array is being run with base64 to create the final encryption product.
szBufferOut = xfrm.TransformFinalBlock(szBufferIn, 0, szBufferIn.Length)
|
|
|
|
|
Sorry for the late reply, I was watching the New Orleans Saints defeat the Minnesota Vikings.
jkirkerx wrote: .Padding = PaddingMode.PKCS7
This is one of the reasons why you are getting a different result in your C++ code. I believe the C++ code is defaulting to PKCS5. Your .NET code is using PKCS7.
You can confirm this by querying your key parameters in your C++ project before you perform the encryption:
DWORD dwPadding = 0;
DWORD dwLength = sizeof(DWORD);
bResult = CryptGetKeyParam(hKey,KP_PADDING,(BYTE *)&dwPadding,&dwLength,0);
Yes... the wierd casts to BYTE * are necessary... these Microsoft cryptography functions are designed to accept BYTE * even though some calls want DWORD *
The value of dwPadding after this call will be 0x1 which == PKCS5_PADDING confirming that your C++ code defaults to PKCS5.
Everything in here must match your C++ code and vice-versa.
Your VB Code:
Dim cryptoProvider As DESCryptoServiceProvider = New DESCryptoServiceProvider()
With cryptoProvider
.BlockSize = 64
.FeedbackSize = 8
.Key = KEY_64
.IV = IV_64
.Mode = CipherMode.CBC
.Padding = PaddingMode.PKCS7
End With
In your C++ code you need to set these parameters to exactly match your .NET implementation.... here are a few:
Set mode to CBC:
DWORD dwMode = CRYPT_MODE_CBC;
bResult = CryptSetKeyParam(hKey, KP_MODE,(BYTE *)&dwMode, 0);
Set the Padding:
DWORD dwPadding = PKCS5_PADDING;
bResult = CryptSetKeyParam(hKey, KP_PADDING,(BYTE *)&dwPadding, 0);
Set the initialization vector:
BYTE IV_64[8] = {1, 2, 3, 4, 5, 6, 7, 8}; CryptSetKeyParam(hKey, KP_IV, IV_64, 0);
Set the feedback:
DWORD dwFeedback = 8;
bResult = CryptSetKeyParam(hKey, KP_MODE_BITS,(BYTE *)&dwFeedback, 0);
By the way... I am fairly certain that setting the feedback value has no effect when using the CBC mode...
Set the Block length:
DWORD dwBlockLength = 64;
bResult = CryptSetKeyParam(hKey, KP_BLOCKLEN,(BYTE *)&dwBlockLength, 0);
If you get all of the settings to match... the values you get out of the C++ code should perfectly match the values you get from the .NET code.
Btw, I don't think the default Microsoft provider supports PKCS7 padding... I believe the .NET framework uses its own provider. You will probably need to change your .NET padding to something both providers support.
Thats all I can think of for now.
Best Wishes,
-David Delaune
modified 18-Dec-11 17:51pm.
|
|
|
|
|
I give up.
After all that writing, only PKCS5 is available in c++, and only PKCS7 is asp.net
There are comments about 7 in wincrypt.h, but I'm not sure what it means.
bResult = CryptAcquireContextW( &hProv, 0, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
bResult = CryptHashData(hHash, KEY_64, sizeof(KEY_64), 0);
bResult = CryptDeriveKey(hProv, CALG_DES, hHash, 0, &hKey);
DWORD dwBlockLength = 64;
bResult = CryptSetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLength, 0);
DWORD dwFeedback = 8;
bResult = CryptSetKeyParam(hKey, KP_MODE_BITS, (BYTE*)&dwFeedback, 0);
errorCode = GetLastError();
bResult = CryptSetKeyParam(hKey, KP_IV, IV_64, 0);
errorCode = GetLastError();
DWORD dwMode = CRYPT_MODE_CBC;
bResult = CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&dwMode, 0);
errorCode = GetLastError();
DWORD dwSetPadding = PKCS5_PADDING;
bResult = CryptSetKeyParam(hKey, KP_PADDING, (BYTE *)&dwSetPadding, 0);
errorCode = GetLastError();
|
|
|
|
|
Hey Jim,
jkirkerx wrote: I give up.
Don't give up so easily man.
Lets step back and summarize your problem:
1.) You have implemented some encryption in ASP.NET and also VB.NET and you want to duplicate the encryption within C++ using the Microsoft cryptography provider.
2.) You have found that the encrypted messages do not match.
3.) The .NET framework implements its own provider that seems to do things a bit differently than the default Microsoft cryptographic service provider (which is what c++ will utilize).
Have you considered approaching the problem from the other side? Rather than attempt to duplicate the .NET cryptography within your C++ application. You could make native API calls from your .NET application and duplicate the C++ encryption code. The .NET framework is perfectly capable of making P/Invoke native API calls and this would ensure that all 3 code bases were the same.
Best Wishes,
-David Delaune
|
|
|
|
|