#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>


int main(int argc, char **argv)
{
 HCRYPTPROV prov1, prov2, prov3;

 HCRYPTKEY hKey1, hKey2, hKey3, hKeyCrypt, hKeyDecrypt;
 unsigned char  Data[30];
 BYTE  DataPom[1024];
 CRYPT_DATA_BLOB  blob;
 long  Alg;
 unsigned long Velikost2 = 1024,Velikost = 12;
 DWORD cbServerBlob, pSize;
 //BYTE rgbServerBlob[2048];
 DWORD dwNJ=0;
 // Specify 11 bytes of salt.
    BYTE rgbSalt[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
    CRYPT_DATA_BLOB sSaltData;
    sSaltData.pbData = rgbSalt;
    sSaltData.cbData = sizeof(rgbSalt);


 Alg = CALG_RC4;

 strcpy(Data, "HELLO WORLD!");

 if (!CryptAcquireContext(&prov1, "MyDSS_DH_Container", MS_ENH_DSS_DH_PROV, PROV_DSS_DH, 0))
 {
	 printf("container is not defined \n");
  if (!CryptAcquireContext(&prov1, "MyDSS_DH_Container", MS_ENH_DSS_DH_PROV, PROV_DSS_DH, CRYPT_NEWKEYSET))
  {
   printf("Error 1: %x(%d)\n", GetLastError(), GetLastError());
  }
  else
  {
	  printf("trying to generate new key \n");
	if (!CryptGenKey(prov1, CALG_DH_SF, (512 << 16) | CRYPT_EXPORTABLE | CRYPT_NO_SALT  , &hKey1))
	 {
	  printf("Error 3: %x(%d)\n", GetLastError(), GetLastError());
	 }
  }
 }

 //printf("prov1=%d  hKey1=%d \n", prov1, hKey1);

 if ( 1==1) 
 {
	 printf("aquiring key handle for existing key \n");

	if (!CryptGetUserKey( prov1, CALG_DH_SF, &hKey1))
	{
		printf("Error 3: %x(%d)\n", GetLastError(), GetLastError());
	}
}

 printf("prov1=%d  hKey1=%d \n", prov1, hKey1);


 
 if (!CryptAcquireContext(&prov2, "DHPregen", MS_ENH_DSS_DH_PROV, PROV_DSS_DH, 0))
 {
  if (!CryptAcquireContext(&prov2, "DHPregen", MS_ENH_DSS_DH_PROV, PROV_DSS_DH, CRYPT_NEWKEYSET))
  {
   printf("Error 1: %x(%d)\n", GetLastError(), GetLastError());
  }
 }



 if (!CryptGenKey(prov2, CALG_DH_EPHEM, (512 << 16) | CRYPT_PREGEN | CRYPT_EXPORTABLE | CRYPT_NO_SALT  , &hKey2))
 {
  printf("Error 6: %x(%d)\n", GetLastError(), GetLastError());
 }

printf("prov2=%d  hKey1=%d \n", prov2, hKey2);



Velikost2 = 0;

if (!CryptSetKeyParam(hKey1, KP_X, NULL, 0))
 {
  printf("Error 13: %x(%d)\n", GetLastError(), GetLastError());
 }

 if (!CryptGetKeyParam(hKey1, KP_G, 0,  &Velikost2, 0))
 {
  printf("Error 9: %x(%d)\n", GetLastError(), GetLastError());
 }
 printf("BufSize=%d \n", Velikost2);
 for (dwNJ=0;dwNJ<Velikost2;dwNJ++){
	 (BYTE) DataPom[dwNJ] = 0x0;
}

 if (!CryptGetKeyParam(hKey1, KP_G, DataPom, &Velikost2, 0))
 {
  printf("Error 9: %x(%d)\n", GetLastError(), GetLastError());
 }

 pSize = Velikost2;

 blob.cbData = Velikost2;
 blob.pbData = DataPom;

 if (!CryptSetKeyParam(hKey2, KP_G, (BYTE * ) &blob, 0))
 {
  printf("Error 10: %x(%d)\n", GetLastError(), GetLastError());
 }


Velikost2 = 0;
if (!CryptGetKeyParam(hKey1, KP_P, 0, &Velikost2, 0))
 {
  printf("Error 11: %x(%d)\n", GetLastError(), GetLastError());
 }

 if (!CryptGetKeyParam(hKey1, KP_P, DataPom, &Velikost2, 0))
 {
  printf("Error 11: %x(%d)\n", GetLastError(), GetLastError());
 }  
 
 printf("\nPrime=");
 for (dwNJ=0;dwNJ<Velikost2;dwNJ++){
		printf( "%02x", (BYTE) DataPom[dwNJ]);
	}
 printf("\n");
 blob.cbData = Velikost2;
 blob.pbData =  DataPom;

 if (!CryptSetKeyParam(hKey2, KP_P, (BYTE * ) &blob, 0))
 {
  printf("Error 12: %x(%d)\n", GetLastError(), GetLastError());
 }


 if (!CryptSetKeyParam(hKey2, KP_X, NULL, 0))
 {
  printf("Error 13: %x(%d)\n", GetLastError(), GetLastError());
 }

 Velikost2 = 0;

 if (!CryptExportKey(hKey1,  0, PUBLICKEYBLOB, 0, 0, &Velikost2))
 {
  printf("Error 7: %x(%d)\n", GetLastError(), GetLastError());
 }
 printf("ExpKey Buf=%d \n", Velikost2);
 if (!CryptExportKey(hKey1, 0, PUBLICKEYBLOB, 0, (BYTE * )  &DataPom, &Velikost2))
 {
  printf("Error 7: %x(%d)\n", GetLastError(), GetLastError());
 }
 
 // CryptImportKey(prov2, rgbServerBlob, cbServerBlob, hKey2, 0, &hKeyCrypt)

 if (!CryptImportKey(prov2, (BYTE * ) DataPom, Velikost2, hKey2, 0, &hKeyCrypt))
 {
  printf("Error 8: %x(%d);%d\n", GetLastError(), GetLastError(), Velikost2);
 }
 Velikost2 = 0;
 if (!CryptExportKey(hKey2, 0, PUBLICKEYBLOB, 0, 0, &Velikost2))
 {
  printf("Error 7: %x(%d)\n", GetLastError(), GetLastError());
 }
	printf("ExpKey Buf=%d \n", Velikost2);
	if (!CryptExportKey(hKey2, 0, PUBLICKEYBLOB, 0, (BYTE * )  &DataPom, &Velikost2))
 {
  printf("Error 7: %x(%d)\n", GetLastError(), GetLastError());
 }

 if (!CryptSetKeyParam(hKeyCrypt, KP_ALGID,(BYTE *) &Alg, (128 << 16) | CRYPT_NO_SALT |CRYPT_EXPORTABLE))
 {
  printf("Error 5: %x(%d)\n", GetLastError(), GetLastError());
 }



 if (!CryptEncrypt(hKeyCrypt, 0, TRUE, 0, (BYTE *) Data,&Velikost, 30))
 {
  printf("Error 4: %x(%d)\n", GetLastError(), GetLastError());
 }

 printf("%d;   %s \n", Velikost, Data); 


 if (!CryptImportKey(prov1, (BYTE * ) DataPom, Velikost2, hKey1, 0, &hKeyDecrypt))
 {
  printf("Error 8: %x(%d);%d\n", GetLastError(), GetLastError(), Velikost2);
 }

 /*
 	for (dwNJ=0;dwNJ<Velikost2;dwNJ++){
	printf( "%02x", (BYTE) DataPom[dwNJ]);
}
printf("\n\n");
*/

 if (!CryptSetKeyParam(hKeyDecrypt, KP_ALGID,(BYTE *) &Alg, (128 << 16) | CRYPT_NO_SALT | CRYPT_EXPORTABLE))
 {
  printf("Error 5: %x(%d)\n", GetLastError(), GetLastError());
 }

 Velikost2 = 2048;
// CRYPT_BLOB_VER3, (BYTE *) DataPom
if ( !CryptExportKey( hKey1, 0, PRIVATEKEYBLOB , CRYPT_BLOB_VER3, (BYTE * ) DataPom, &Velikost2))
{
	printf("Error 8a: %x(%d);%d\n", GetLastError(), GetLastError(), Velikost2);
	
}

printf("ExpKey Buf=%d \n", Velikost2);
	for (dwNJ=0;dwNJ<Velikost2;dwNJ++){
	printf( "%02x", (BYTE) DataPom[dwNJ]);
}
printf("\n\n");
 // if (!CryptSetKeyParam(hKeyDecrypt, KP_SALT_EX, (BYTE*)&sSaltData,  0))
 //{
  ///printf("Error 5: %x(%d)\n", GetLastError(), GetLastError());
 //}

 CryptDecrypt(hKeyDecrypt, 0, TRUE, 0, (BYTE *) Data, &Velikost);

 printf("%s \n", Data);


//--------------------------------------------------------------------
//  Declare and initialize local variables to hash the shared secret. 
{
HCRYPTHASH   hHash;
BYTE         *pbHash;
//BYTE         *pbHashSize;
DWORD        dwHashLen = sizeof(DWORD);
DWORD        i;

	if ( !CryptCreateHash( prov2, CALG_MD5, 0, 0, &hHash))
	{
		printf("Error 21: %x(%d)\n", GetLastError(), GetLastError());
	}

	if (!CryptHashSessionKey(hHash, hKeyCrypt,0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}

	if (!CryptGetHashParam(hHash, HP_HASHVAL, 0, &dwHashLen, 0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}

	if(pbHash = (BYTE*)malloc(dwHashLen))
	{
	// It worked. Do nothing.
	}


	if (!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE *) pbHash, &dwHashLen, 0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}
	for (dwNJ=0;dwNJ<dwHashLen;dwNJ++){
		printf( "%02x", (BYTE) pbHash[dwNJ]);
	}
	printf("\n\n");

	free(pbHash);
	if (CryptDestroyHash(hHash))
	{
	// It worked. Do nothing.
	}


		if ( !CryptCreateHash( prov1, CALG_MD5, 0, 0, &hHash))
	{
		printf("Error 21: %x(%d)\n", GetLastError(), GetLastError());
	}

	if (!CryptHashSessionKey(hHash, hKeyDecrypt,0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}

	if (!CryptGetHashParam(hHash, HP_HASHVAL, 0, &dwHashLen, 0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}

	if(pbHash = (BYTE*)malloc(dwHashLen))
	{
	// It worked. Do nothing.
	}


	if (!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE *) pbHash, &dwHashLen, 0))
	{
		printf("Error 22: %x(%d)\n", GetLastError(), GetLastError());
	}
	for (dwNJ=0;dwNJ<dwHashLen;dwNJ++){
		printf( "%02x", (BYTE) pbHash[dwNJ]);
	}
	printf("\n\n");

	free(pbHash);
	if (CryptDestroyHash(hHash))
	{
	// It worked. Do nothing.
	}



}

 if (!CryptReleaseContext(prov1, 0))
  printf("Error 2: %x(%d)\n", GetLastError(), GetLastError());

  if (!CryptReleaseContext(prov2, 0))
  printf("Error 2: %x(%d)\n", GetLastError(), GetLastError());

//   if (!CryptReleaseContext(prov3, 0))
// printf("Error 2: %x(%d)\n", GetLastError(), GetLastError());



 return(1);
}



