|
Hi ! I have been trying to use the crypto API to decript an AES_128 crypted file.
the key to decrypt this file is a SHA1 hash of a certain keyword, so I made a code that should work but doesn't ( you know the feeling ^^ )
so please if someone can help me out I would be very grateful.
the function that seems to crash is the last one the cryptdecript function.
The error message I get from my fonction is : "crypt decript failed : incorect data"
here is my full code :
<br />
#include <stdio.h><br />
#include <stdlib.h><br />
#include <windows.h><br />
#include <wincrypt.h><br />
<br />
<br />
#define buf_size 2048<br />
<br />
void ErrorExit(LPTSTR lpszFunction) <br />
{ <br />
<br />
LPVOID lpMsgBuf;<br />
LPVOID lpDisplayBuf;<br />
DWORD dw = GetLastError(); <br />
<br />
FormatMessage(<br />
FORMAT_MESSAGE_ALLOCATE_BUFFER | <br />
FORMAT_MESSAGE_FROM_SYSTEM |<br />
FORMAT_MESSAGE_IGNORE_INSERTS,<br />
NULL,<br />
dw,<br />
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),<br />
(LPTSTR) &lpMsgBuf,<br />
0, NULL );<br />
<br />
<br />
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, <br />
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); <br />
printf("%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf); <br />
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); <br />
<br />
LocalFree(lpMsgBuf);<br />
LocalFree(lpDisplayBuf);<br />
ExitProcess(dw); <br />
<br />
system("PAUSE");<br />
}<br />
<br />
<br />
<br />
<br />
<br />
<br />
int main(int argc, char *argv[])<br />
{<br />
printf("depart");<br />
<br />
char * texte[buf_size+1];<br />
char * texte2[buf_size+1];<br />
FILE* fd;<br />
int i=0,j=0,k=0;<br />
char value;<br />
<br />
char * buffer[buf_size+1]; <br />
<br />
if( (fd=fopen("C:\\Dev-Cpp\\projects\\decriptage\\1009369384.stream","rb")) == NULL )<br />
{<br />
perror("opening failed"); <br />
exit(0);<br />
}<br />
memset(buffer,0,2048);<br />
memset(texte,0,2048);<br />
memset(texte2,0,2048);<br />
<br />
system("PAUSE");<br />
i=0;<br />
while( fread(&texte,1,1*sizeof(char) ,fd)== 1 ){<br />
i++;<br />
strcat(texte2,texte);<br />
} <br />
<br />
<br />
<br />
<br />
printf("::::%d::::\n%s\nfin fichier crypté.\n\n",i,texte2); <br />
i=0;<br />
char * cle="ironboy007@hotmail.fr";<br />
HCRYPTPROV hProv = 0;<br />
HCRYPTHASH hHash = 0;<br />
HCRYPTKEY hKey = 0;<br />
DWORD Len_txt = (DWORD)strlen(texte);<br />
DWORD Len_txt2 = (DWORD)strlen(texte2);<br />
DWORD Len_cle = (DWORD)strlen(cle);<br />
DWORD ret;<br />
i=0;<br />
while(texte[i]!='\0')i++; <br />
while(texte2[j]!='\0')j++;<br />
while(cle[k]!='\0')k++;<br />
printf("texte:%d\n", i);<br />
printf("texte2:%d\n", j); <br />
printf("clé:%d\n", k);<br />
if ( !CryptAcquireContext (&hProv, NULL, NULL, PROV_RSA_AES, 0) )<br />
{<br />
ret = GetLastError();<br />
<br />
if (ret != NTE_BAD_KEYSET)<br />
{<br />
perror("1" );<br />
}<br />
else <br />
{<br />
if ( !CryptAcquireContext (&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET) )<br />
{<br />
perror("2" );<br />
}<br />
}<br />
}<br />
<br />
if ( !CryptCreateHash (hProv, CALG_SHA1, 0, 0, &hHash) )<br />
{ <br />
if (hProv) CryptReleaseContext(hProv, 0);<br />
perror("3" );<br />
}<br />
<br />
if ( !CryptHashData (hHash, cle, Len_cle, 0) )<br />
{<br />
if (hHash) CryptDestroyHash(hHash); <br />
if (hProv) CryptReleaseContext(hProv, 0);<br />
perror("4" );<br />
}<br />
<br />
if ( !CryptDeriveKey (hProv, CALG_AES_128, hHash, 0, &hKey) )<br />
{<br />
if (hHash) CryptDestroyHash(hHash); <br />
if (hProv) CryptReleaseContext(hProv, 0);<br />
perror( "5" );<br />
ErrorExit(TEXT("CryptDeriveKey"));<br />
ret = GetLastError();<br />
printf("%s", ret);<br />
}<br />
<br />
<br />
if ( !CryptDecrypt(hKey, 0, TRUE, 0, texte2, &Len_txt2) ) <br />
{<br />
ErrorExit(TEXT("CryptDecrypt"));<br />
ret = GetLastError();<br />
printf("%d", ret);<br />
ErrorExit(TEXT("CryptDecrypt"));<br />
system("PAUSE");<br />
<br />
ErrorExit(TEXT("CryptDecrypt"));<br />
system("PAUSE");<br />
if (hKey) CryptDestroyKey(hKey);<br />
if (hHash) CryptDestroyHash(hHash); <br />
if (hProv) CryptReleaseContext(hProv, 0); <br />
}<br />
<br />
printf("::::%d::::\n%s\nEnd of decrypted file.\n\n",strlen(texte2),texte2);<br />
<br />
if (hKey) <br />
CryptDestroyKey(hKey);<br />
<br />
if (hHash) <br />
CryptDestroyHash(hHash); <br />
<br />
if (hProv) <br />
CryptReleaseContext(hProv, 0);<br />
<br />
system("PAUSE");<br />
system("PAUSE"); <br />
return 0;<br />
<br />
}<br />
<br />
thanks in advance.
|
|
|
|
|
Hi Jessn,
Thanks a lot for this code, I am trying to use it on a small app I am creating but I was reading some of the comments and you mention that there are some bugs on the code and that you were going to correct them... Is the code diplayed here the fixed one? if not, which are the bugs you noticed so I can be aware?
Thanks!
|
|
|
|
|
Gonzalo Parra wrote: Hi Jessn,
Thanks a lot for this code, I am trying to use it on a small app I am creating but I was reading some of the comments and you mention that there are some bugs on the code and that you were going to correct them... Is the code diplayed here the fixed one? if not, which are the bugs you noticed so I can be aware?
Thanks!
I have pasted some code snippets in the threads with minor corrections to the codebase. These should be the corrections. What so ever please perform a test on your own, because the provided code/sample app is only a prototype.
Regards,
--
Jess Nielsen, b.sc.
Security Analyst
http://jessn.blogspot.com/
|
|
|
|
|
Hi Jess,
Thanks a lot for the fast response...
I was able to incorporate it into my app but but there is something that I would still like to fix and don't know how (sorry, I do not have that much experience with C++):
The Cipher text contains some extra characters at the end as somebody here pointed and you mentioned to null terminate a string to fix it... could you please help me with this? which string do I need to null terminate and how can I do that?
Thanks!
|
|
|
|
|
Gonzalo Parra wrote: Hi Jess,
Thanks a lot for the fast response...
I was able to incorporate it into my app but but there is something that I would still like to fix and don't know how (sorry, I do not have that much experience with C++):
The Cipher text contains some extra characters at the end as somebody here pointed and you mentioned to null terminate a string to fix it... could you please help me with this? which string do I need to null terminate and how can I do that?
Thanks!
When terminating a string you will have to set the last char to '\0'. This means for the string like "the man on the moon" the length will be the number of the characters plus one for the null-terminating character. This is important when allocating the memomry for the string. Always remember to allocate an extra than is reserved for the null-terminating character. Before usage of newly allocated it is normally initialized e.g memset(psz, 0, strlen(psz)). This sets all allocated characters to zero/null even the null-terminating character.
char * pSz = "....";
pSz[strlen(pSz)-1] = '\0'; // the last character will now be null
Hth
--
Jess Nielsen, b.sc.
Security Analyst
http://jessn.blogspot.com/
|
|
|
|
|
|
In your example, the output included non ascii characters. Is it possible to restrict the encrypted output to alphanumeric characters?
|
|
|
|
|
Angus Comber wrote: In your example, the output included non ascii characters. Is it possible to restrict the encrypted output to alphanumeric characters?
Instead of changing the output of the algoritm I will suggest that you perform a base64 encoding of the output. I have made a similar solution where the output should be used as a http get parameter and no data was lost or "obfuscated" doing base64 encoding.
Hth
--
Jess Nielsen, b.sc.
Security Analyst
http://jessn.blogspot.com/
|
|
|
|
|
Thanks for your great code.
However, I found that while encrypting/decrypting some specific combination of characters, it doesn't work right. I am not able to reproduce the problem because with different value of the key, the result are different. Have you experienced this and how may I solve it?
Thanks again.
Simon
|
|
|
|
|
nomicism wrote: Thanks for your great code.
However, I found that while encrypting/decrypting some specific combination of characters, it doesn't work right. I am not able to reproduce the problem because with different value of the key, the result are different. Have you experienced this and how may I solve it?
Thanks again.
Simon
It might be possible to fix it if you choose to use byte data instead of the char pointers. This means that you "just" take a part from the memory and encrypt it. A simple way of doing this is by having a pointer to where the data block starts and a long indicating the length of the data block. Copying of these blocks can be done with memcpy().
Anyhow this requires that you change the current implementation. The implementation is provided as is and it is *just* a prototype, so you are welcome to change it.
Hth
--
Jess Nielsen, b.sc.
Security Analyst
http://jessn.blogspot.com/
|
|
|
|
|
The origin of the key and more... can be found on external article (Year 2001):
PHD Computer Consultants Ltd. Setting the RC4 key in Windows CryptoAPI
Article Copyright © 2001,2003 PHD Computer Consultants Ltd.
http://www.phdcc.com/cryptorc4.htm
This old article is base on Microsoft Q228786:
How to export and import plain text session keys by using CryptoAPI
Article Copyright © ???? Microsoft.
http://support.microsoft.com/?scid=kb%3Ben-us%3B228786&x=24&y=10
...
|
|
|
|
|
Hi Jessn,
I was coding based on your codes, and I run into the problem of imports/exports. This is what I did.
I see that in the absence of key.txt, you generate a session key based on a hard-coded key in key.h. So I mimick that by doing
HCRYPTKEY pkey;
int res = CryptGenKey(m_prov, CALG_RC4, CRYPT_EXPORTABLE, &pkey);
res = CryptExportKey(pkey, 0, PUBLICKEYBLOB , 0, NULL, &size); // to get the size.
It miserably fails, saying that a bad key is provided.
Then I tried
HCRYPTKEY pkey;
int res = CryptGenKey(m_prov, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &pkey);
res = CryptExportKey(pkey, 0, PUBLICKEYBLOB , 0, NULL, &size); // to get the size.
And I get some hundred bytes for the key. Then I replaced the key in key.h with these bytes. It seems to be running perfectly okay on 1 PC. But then when I generate the session key on one pc, export it into a file, import it on another PC, and try to decrypt whatever that's been encrypted, I get a Bad Data error (NTE_BAD_DATA).
This problem doesn't occur when I use your key. What mistakes have I made in generating this key? How did you generate that key?
-- modified at 3:48 Tuesday 18th April, 2006
|
|
|
|
|
Hi Ray
I have noticed a few bugs in my sample code, which I'm going to correct soon. This means when my new laptop has been installed. During the easter my previous laptop just entered its period of retirement. I noticed this the hard way.
What so ever until the few bugs have been corrected and I have been looking into your problem pleace notice that that the keys are generated in the scope of your CSP. This can cause problems when tranferring between two PCs. I will try to help you with this when my home dev.enrivonment is up and running again. I expect it to be running at the end of this week. I have a few high priority meetings and tasks as well, which has to be done first that's why.
When you are using the AT_KEYEXCHANGE instead of the CALG_RC4 you are generating a public/private key pair.
-- snip --
In addition to generating session keys for symmetric algorithms, this function can also generate public/private key pairs. Each CryptoAPI client generally possesses two public/private key pairs. To generate one of these key pairs, set the Algid parameter to one of the following values.
-- snip --
Hth
Jess
--
Jess Nielsen
Security Analyst
C++ Programmer
|
|
|
|
|
Hi Jess,
Thank you very much for your help.
I have given it another shot, and I ran into the same problem where the
session key cannot be imported on the second PC. This time, it is bad key error during import.
These are the related bits of codes (where PKey is the BYTE* for the hard-coded key). May I have some quick suggestions as to where I have made the mistakes?
(for producing the hard-coded key)
if (!CryptAcquireContext(&m_prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{
HCRYPTKEY pkey = 0;
DWORD size = 2048;
DWORD flag = CRYPT_EXPORTABLE;
flag |= 0x00010000 * size;
int res = CryptGenKey(m_prov, CALG_RSA_KEYX, flag, &pkey);
res = CryptExportKey(pkey, 0, PUBLICKEYBLOB, 0, NULL, &size);
BYTE *pk = new BYTE[size+1]; memset(pk, 0, sizeof(pk));
res = CryptExportKey(pkey, 0, PUBLICKEYBLOB, 0, pk, &size);
}
(then for generating the session key)
HCRYPTKEY pkey, key;
if (!CryptAcquireContext(&m_prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{
int res = CryptImportKey(m_prov, PKey, sizeof(PKey), 0, 0, &pkey);
DWORD size = 128;
DWORD flag = (0x00010000 * size) |= CRYPT_EXPORTABLE;
if (CryptGenKey(m_prov, CALG_RC4, flag, &key))
{
int res = CryptExportKey(m_key, pkey, SIMPLEBLOB , 0, NULL, &size);
ret = new BYTE[size+1]; memset(ret, 0, (size+1)*sizeof(BYTE));
res = CryptExportKey(key, pkey, SIMPLEBLOB , 0, ret, &size);
}
}
(then for importing the session key on the second PC)
HCRYPTKEY pkey, key;
if (!CryptAcquireContext(&m_prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{
int res = CryptImportKey(m_prov, PKey, sizeof(PKey), 0, 0, &pkey);
<big> int res = CryptImportKey(m_prov, pbBlob, cbBlob, pkey, CRYPT_EXPORTABLE, &key);</big> if (!res)
{
dwResult = GetLastError();
printf("Error: CryptImportKey() failed: %x %x.\n", res, dwResult);
}
}
|
|
|
|
|
Hi Ray,
Sorry for the late response, I have just been at bit tight up - more than expected. A thing that pops into my eyes is the container chosen. When aquiring the context you give a '0', which means it takes the default container for your CSP. I have had a few bad experiences with this (not particular in this approach), but i.e. when the security component is used from another context than the interactive user.
Do you think it is possible for you to make a small sample application (read: non-production code) and send it to me, I'll then take a look at it. (please find my email at my cv, the url is in my profile)
Hth
--
Jess Nielsen
Security Analyst
C++ Programmer
|
|
|
|
|
Hi Jess.
Thanks for your time and help. At the moment, I am stuck with a few other projects at work while the one with encryption is on hold. So I will need some spare time to prepare it.
Thanks again, and I hope I can get the sample application ready ASAP.
Regards
Ray
|
|
|
|
|
Hi Ray
That's OK, I'll be awaiting a mail from you.
The article at the following URL might have your interest. It is primary about building secure applications.
http://jess.heidrun.dk/cv/BuildingSecureApp.pdf[^]
Regards,
Jess
--
Jess Nielsen
Security Analyst
C++ Programmer
|
|
|
|
|
The publication at the link below might be interesting to some of you guys.
Building Secure Applications[^]
Take a look at it, I think it is worth reading!
--
Jess Nielsen
Systems Developer
C++ Programmer
|
|
|
|
|
Hi,
I am slightly confused, but shouldn't you call the
CryptGenKey function in your code with
CALG_RSA_SIGN and
0x08000000 | CRYPT_EXPORTABLE as the second and third arguments (in case using RSA with 2048 bits key length)?
Quoting the Crypt-API docu on CryptGenKey: "dwFlags
[in] Specifies the type of key generated. The sizes of a session key, RSA signature key, and RSA key exchange keys can be set when the key is generated. The key size, representing the length of the key modulus in bits, is set with the upper 16 bits of this parameter. Thus, if a 2,048-bit RSA signature key is to be generated, the value 0x08000000 is combined with any other dwFlags predefined value with a bitwise-OR operation."
Semmel
|
|
|
|
|
Yes, you have to use the bitwise-OR operation for that flag like "0x08000000 | CRYPT_EXPORTABLE". My point here is that CRYPT_EXPORTABLE must be used to make it possible to export the key. You can choose either to import the key (using CryptImportKey) or generate the key (using CryptGenKey). You have to export (and maybe store it) before you are able to import it.
The CALG_RSA_SIGN flag is quiet confusing, but the fact is that it is not supported by the Microsoft Base Cryptographic Provider as far as I know. If you take a look into the MSDN you don't find that flag as an option to the CryptGenKey function. Unfortunately!
Remember that the sample application is not fully tested nor fully developed. It is only meant as an application to help people getting started.
Hth
--
Jess Nielsen
Systems Developer
C++ Programmer
-- modified at 14:15 Thursday 24th November, 2005
|
|
|
|
|
Thanks for the head start into understanding cryptography. But I ran across a couple problems that maybe you could comment on or help with.
1. after encrypting, the cipherblock is one byte longer than the clear text string and it is not null terminated. so assigning it back to the CString m_cipher end up copying extra characters up until the first zero is stumbled upon. luckily, this doesn't seem to harm the decrypting, but it could lead to a crash or at least longer encrypted data to be stored or transmitted.
2. related to this, I have discovered some input patterns that end up giving a zero in the middle of the encrypted result. so assigning to the CString m_cipher ends up truncating the result. admittedly, my clear text had a tab character ('\t') at the start which your program won't allow to be entered, but maybe that is not a unique case. also, i am trying to be able to be more general with the clear data being encrytped. if you wish to test, the string i tried that failed is \tllibllib\t (where '\t' is the tab character) followed by anything else.
3. upon exiting, the code in the constructor, if (!CryptExportKey(hSessionKey, hKey, SIMPLEBLOB, 0, NULL, &cbBlob)) gives an error. (the error is not displayed, because the MessageBox call doesn't do anything on my system.) also, if it had passed, the next line is a repeat of the same call. since i don't understand what's in the constructor and destructor, could you please explain them?
4. i noticed that if i encrypt two lines that begin with the same sequence, that the encrypted results both start with the same encrypted sequence of the same length as the unencrypted common sequences. furthermore, if the same sequence exists in two unencrypted strings at the same offset from the beginning, there will be common sequences in the excrypted strings at that same offset from the beginning. this seems that it might help breaking the code easier???
|
|
|
|
|
Ad. 1) You are absolutely right, the string should be NULL terminated! If I didn't do it in the program feel free to change it in your version of the program.
Ad. 2) For convenience I used a CString. To make sure that it will be able to take all kinds of input this has to be changed to byte ptr (byte*) instead. Remember to NULL terminate as mentioned above.
Ad. 3) What error code do you get, when it calls the method? The spec outlines about ten return values. You have to do a bit more debugging here before I can help you...
Ad. 4) The current implementation does not use chaining mode. Implementing CBC will solve this issue. This is done by using an initialization vector. When doing this you have to split the clear text into blocks and chain them as illustrated in the article.
For the implementation of this please note the FINAL PARAMETER at the CryptEncrypt() method:
-- snip --
Final
[in] Specifying whether this is the last section in a series being encrypted. Final is set TRUE for the last or only block and FALSE if there are more blocks to be encrypted. For more information, see the Remarks section later in this topic.
-- snip --
Hth
--
Jess Nielsen
Systems Developer
C++ Programmer
-- modified at 12:45 Wednesday 23rd November, 2005
|
|
|
|
|
Jess,
3. The error was #define NTE_BAD_KEY_STATE _HRESULT_TYPEDEF_(0x8009000BL). That still doesn't help me understand. Why would the key be in a bad state? Why would CryptExportKey() need to be called three times in the destructor? Does the key need to be saved to a text file? How does that saved key functionally compare to the key in key.h?
4. I was not referring to chaining. I just noticed that common substring at common locations were encrypted to the same values. I felt it might be less secure, but when I think about it, I doubt it is a problem. All it means is that if you know the key then you can determine what the key is. No worries there.
Thanks,
Bill
|
|
|
|
|
Bill,
3. Calling the CryptExportKey three times is definately a bug. It should only be called twice! 1) Obtaining the size of the key 2) Obtaining the key itself. I am just wondering how the last one got there as well, but I guess it got into the destructor when I have been restructuring the code in the project.
The NTE_BAD_KEY_STATE is typically returned when you don't have permission to export the key. Please note, when I generate the key in the constructor I make it exportable with the CRYPT_EXPORTABLE flag. This has to be done when the key is imported as well.
-- snip --
CRYPT_EXPORTABLE
The key being imported is eventually to be re-exported. If this flag is not used, then calls to CryptExportKey with the key handle fail.
-- snip --
After I have been inspecting the code, I can see that I have been using the zero value when importing the key, that is the reason why you can't export it. Feel free to change it.
4. I know you were not referring to the chaining mode, I just explained how you could change it to avoid the common substrings to be encrypted to the same values. You are right it will make it less secure without the CBC.
Hth
--
Jess Nielsen
Systems Developer
C++ Programmer
|
|
|
|
|
3. Ok. After I change the code to make the imported key exportable, I get a different error, NTE_NO_KEY. That's because a public key is only made in your code if the file key.txt does not exist. If I delete that file and run the program, i do not get any error the first time through. But unfortunately, all my previously encrypted data cannot be decrypted anymore. I guess the key has changed. (No harm though, because it has only been test data, not real data.)
Could you explain why you want to save the key to a file? Is it to avoid generating a new session key each time to save time? Is that secure if the file is found by someone? Is the key in the file the session key itself? Or is it the session key encrypted by the private key? Could you explain where you got the private key in the key.h header? Is it secure to embed that in the program?
Thanks very much...
|
|
|
|
|