none
BCryptVerifySignature returns c000000d (invalid param) RRS feed

  • Question

  • Hello. I'm trying to use Bcrypt APIs to do some digital signature verification, which will eventually end up in KM hence need to only use Bcrypt and none of the other helpful wintrust APIs etc. For now doing it in UM. 

    So I have a signed file with a digital signature X509_ASN_ENCODING, and I'm trying to verify with SHA256 and RSA, as it's dual signed with SHA1 as well.

    The steps I do are, all return values are success except for BCryptVerifySignature

    - Parse the signed PE file, extract out Certificate section, validate fields

    - Extract out Public key (verified this is same value as actual public key)

    - Then: BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
    // I noticed there is also BCRYPT_RSA_SIGN_ALGORITHM

    - Then call: ImportRsaPublicKey(hAlg, (PUBLIC_KEY_VALUES *)pPubKey, &hPublicKey, ppRsaKeyBlob, pWinCert->bCertificate, pWinCert->dwLength);
    // Which internally calls BCryptImportKeyPair(hAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, phPublicKey, (PUCHAR)&rsaBlob, cbRsaBlob, 0);

    // Based of https://docs.microsoft.com/en-gb/windows/win32/medfound/opm-example-code?redirectedfrom=MSDN

    // Where PUBLIC_KEY_VALUES is a struct I created following recommendation from https://docs.microsoft.com/en-gb/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_rsakey_blob 
    {BLOBHEADER blobheader; RSAPUBKEY rsapubkey; BYTE modulus[9999];}

    - Lastly: BCryptVerifySignature(hPublicKey, &pkcs1Info /*BCRYPT_SHA256_ALGORITHM*/, (PUCHAR)&hash, cbHash /*32 B*/, (PUCHAR)&signature, cbSignature/*256 B*/), BCRYPT_PAD_PKCS1);
    // Where hash is the Digest, and signature is the EncryptedDigest. Correct?

    I just don't know why it returns 0xC000000D INVALID_PARAMETER. I looked at it in windbg and it returns that error code from inside bcryptPrimitives!MSCryptRsaVerifySignature. And it looks like it relates to the flags I'm using (though could be my bad assembly reading), I tried NULLing out pPaddingInfo and dwFlags, same error.. Hmm.

    Monday, January 20, 2020 4:37 PM

All replies

  • Yeah, debugging CNG code has always been a huge pain because there is no easy way to debug this stuff. I found that by creating my own hashes and signatures and then validating them, I was able to piece together what needed to be done. I see that there is now a Microsoft-Windows-Crypto-CNG ETW provider. I haven't tried it, but that may provide some clues as to what is going on.

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Monday, January 20, 2020 6:42 PM
    Moderator
  • Thanks Brian. Can you confirm or deny the following:

    For BCryptVerifySignature args.

    - `pbHash` is the unencrypted digest (32 B), i.e. hash of all executable pages/sections as per the authenticode.docx specification

    - `pbSignature` is the encrypted digest (256 B), i.e. public key signed the above unencrypted digest hash

    Do you know of tools which can display the values above visually (digital signature tab, openssl, signtool, etc?), so I can confirm I am getting the correct values? 

    Tuesday, January 21, 2020 10:43 AM
  • 1. For BCryptVerifyHash, pbHash is the unsigned hash

    2. Yes

    Both of those parameters are outputs of BCryptSignHash

    I found that one of the hardest parts was getting the padding and flags correct. It is very difficult to debug more than one problem at a time. Instead of jumping in and trying to get this working on Authenticode, try encrypting and decrypting a simple block of data. Once you can go both directions and you're using CNG properly, then worry about whether you're implementing the Authenticode spec correctly.

    The docs are a lot better now than they were back in 2007 when I authored a class on how to use CNG. Back then, I complained a lot about this to the developers at Microsoft and they said that they would increase the debuggability. Have you tried enabling the ETW provider? What kind of information does it put out?

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Tuesday, January 21, 2020 8:28 PM
    Moderator
  • Hello Brian. I'm thinking the same thing, it is the padding/flags.

    BCryptSignHash() is a nice pre-cursor to BCryptVerifySignature, as it requires less params to find out which cause issues. I doubt the issue is the 32B pbInput, or the sizes. So it leaves 3 params at potential fault, resulting always in STATUS_INVALID_PARAMETER. 

    - hPublicKey - from BCryptImportKeyPair. I don't feel right about the BCRYPT_RSAPUBLIC_BLOB blob I created, the Modulus I use is the same as the one found in OpenSSL (257 B) (with the leading 0x00 stripped off making it 256 B). But what's odd is BCryptImportKeyPair succeeds on both 257 and 256B modulus. The reason I strip it of, I monitored windbg bcrypt!BCryptImportKeyPair and watched sysinternals\sigcheck64.exe verify my file, and I confusingly noticed 2 calls to this API, once without the trailing 0x00 on modulus, and another time with loads of 0x00 like padding before the modulus.

    - paddingInfo - I use BCRYPT_PKCS1_PADDING_INFO, with BCRYPT_SHA256_ALGORITHM

    - dwFlags - I use BCRYPT_PAD_PKCS1

    Do you think the above is correct? I've seen other examples online where they BCRYPT_SHA1_ALGORITHM. But parsing the Certificate struct gives Digest Algorithm SHA256, and Digest Encryption Algorithm RSA. 

    Lastly I don't think this is at fault, but I also use BCryptOpenAlgorithmProvider with BCRYPT_RSA_ALGORITHM and MS_PRIMITIVE_PROVIDER.

    * Also I tried creating a view those ETW sources, along with all crypto related ones (Crypto-BCRYPT, Crypto-CNG, Crypto-DPAPI, Crypto-DSSEnh, Crypto-NCrypt, Crypto-RNG, Crypto-RSAEnh). No output, except Crypt-DPAPI, was printing. Do you have to enable some reg settings to see Bcrypt/CNG?



    Wednesday, January 22, 2020 4:03 PM
  • Following is the solution to one of the labs in my CNG course that I wrote back in 2007. It may help. If you're not familiar with little-endian dumps, they read from right to left with the right-most hex value the offset from the beginning of the buffer and the text reads from left to right, which just makes sense; Microsoft always insists on re-inventing the wheel, and their hex dumps have always been horrible.

     
    //++
    //
    // Copyright © 2007-2020 Sannas Consulting, LLC. All rights reserved.
    //
    // Permission to use, copy, modify, and distribute this software and its
    // documentation in source and binary forms for any purpose and without
    // fee is hereby granted, provided that both the above copyright notice
    // and this permission notice appear in all copies, and that any
    // documentation, advertising materials, and other materials related to
    // such distribution and use acknowledge that the software was developed
    // in part by Sannas Consulting, LLC.  The name of Sannas Consulting,
    // LLC. may not be used to endorse or promote products derived from this
    // software without specific prior written permission.
    //
    // Sannas Consulting, LLC. makes no representations about the suitability
    // of this software for any purpose.  THIS SOFTWARE IS PROVIDED "AS IS"
    // AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
    // LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE.
    //
    // Other copyrights might apply to parts of this software and are so noted
    // when applicable.
    //--
    
    //+
    //
    // FACILITY:	CNG Lab
    //
    // DESCRIPTION:	Hashes a file and signs the hash
    //
    // VERSION:		2.0
    //
    // AUTHOR:		Brian Catlin
    //
    // CREATED:		2007-01-17
    //
    // MODIFICATION HISTORY:
    //
    //	2.0		2009-10-14	Brian Catlin
    //			Specify hash and signing algorithms using #define (now supports 
    //			RSA and DSA)
    //			Fix bug in hash routine where a hash object was being passed
    //			instead of a hash handle
    //
    //	1.0		2007-01-17	Brian Catlin
    //			Original version
    //
    //-
    
    //
    // INCLUDE FILES:
    //
    
    #include <windows.h>
    #include <stdio.h>
    #include <bcrypt.h>
    #include <wchar.h>
    
    //
    // CONSTANTS:
    //
    
    #define STATUS_UNSUCCESSFUL         ((NTSTATUS) 0xC0000001L)
    #define STATUS_SUCCESS				((NTSTATUS) 0x0L)
    #define AT_DEFAULT_DISPLAY_WIDTH	120
    
    static CHAR							nibble_to_hex [] = {'0', '1', '2', '3', '4', '5', '6', '7',
    														 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    
    #define	ASYM_TEST_HASH_ALG			BCRYPT_SHA1_ALGORITHM
    #define	ASYM_TEST_SIGN_ALG			BCRYPT_RSA_ALGORITHM
    #define ASYM_TEST_PUBLIC_BLOB		BCRYPT_RSAPUBLIC_BLOB
    
    //
    // MACROS:
    //
    
    #define NT_SUCCESS(Status)          (((NTSTATUS) (Status)) >= 0)
    
    //
    // DECLARATIONS:
    //
    
    CONSOLE_SCREEN_BUFFER_INFO			AT_sb_info = {0};
    
    //
    // TABLE OF CONTENTS:
    //
    //
    
    
    int 
    main													// Main entry point  
    	(
    	);
    
    NTSTATUS 
    Create_keys												// Create public and private keys
    	(
    	OUT BCRYPT_KEY_HANDLE	*Private_key,				// Handle to private key
    	OUT BCRYPT_KEY_HANDLE	*Public_key					// Handle to public key
    	);
    
    void 
    Dump													// Dump a buffer (in hex) to the console
    	(
    	IN PVOID	Address,								// Address of buffer
    	IN DWORD	Length,									// Length of buffer
    	IN DWORD	Line_width,								// Maximum width of output line
    	IN DWORD	Show_offset,							// Display the offset on each line
    	IN DWORD	Show_ascii								// Display ASCII representation of data
    	);
    
    NTSTATUS 
    Decrypt													// Decrypt a buffer
    	(
    	IN BCRYPT_KEY_HANDLE	Key_handle,					// Key to use
    	IN PUCHAR				Input_buffer,				// Address of input buffer
    	IN DWORD				Input_buffer_length,		// Length of input buffer
    	IN PUCHAR*				Output_buffer,				// Address of output buffer
    	IN DWORD*				Output_buffer_length		// Length of output buffer
    	);
    
    NTSTATUS 
    Encrypt													// Encrypt a buffer
    	(
    	IN BCRYPT_KEY_HANDLE	Key_handle,					// Key to use
    	IN PUCHAR				Input_buffer,				// Address of input buffer
    	IN DWORD				Input_buffer_length,		// Length of input buffer
    	IN PUCHAR*				Output_buffer,				// Address of output buffer
    	IN DWORD*				Output_buffer_length		// Length of output buffer
    	);
    
    NTSTATUS 
    Hash_data												// Hash a buffer
    	(
    	IN PUCHAR	Data,									// Address of data to hash
    	IN DWORD	Length,									// Length of data to hash
    	OUT PVOID	*Hash,									// Address of buffer containing hash
    	OUT DWORD	*Hash_size								// Length of hash
    	);
    
    NTSTATUS 
    Open_and_map_file										// Map a file into the process's address space
    	(
    	IN LPWSTR	File_name,								// File to map
    	OUT HANDLE	*Handle,								// Handle to file
    	OUT HANDLE	*Mapping_handle,						// Section handle
    	OUT PVOID	*Mapped_address,						// Address of mapped file
    	OUT DWORD	*Mapped_size							// Size of mapped file
    	);
    
    NTSTATUS 
    Sign_hash												// Sign a hash
    	(
    	IN PUCHAR	Hash,									// Address of buffer containing hash
    	IN DWORD	Length,									// Length of hash
    	OUT PVOID	*Signed_data,							// Address of buffer containing signature
    	OUT DWORD	*Signed_size,							// Length of signature
    	OUT PVOID	*Key,									// Address of buffer containing public key
    	OUT DWORD	*Key_size								// Length of key
    	);
    
    
    
    int 
    main													// Main entry point  
    	(
    	)
    
    {
    NTSTATUS			status				= STATUS_UNSUCCESSFUL;
    HANDLE				output_hdl			= GetStdHandle (STD_OUTPUT_HANDLE);
    ULONG				display_width;
    WCHAR				separators []		= L" \t";
    PWCHAR				context;
    LPWSTR				command_line;
    LPWSTR				file_name;
    HANDLE				file_handle;
    HANDLE				mapping_handle;
    PVOID				mapped_address;
    DWORD				mapped_size;
    PVOID				hash;
    DWORD				hash_size;
    PVOID				signature;
    DWORD				signature_size;
    PVOID				key;
    DWORD				key_size;
    PUCHAR				cipher_text;
    DWORD				cipher_text_length;
    PUCHAR				plain_text;
    DWORD				plain_text_length;
    BCRYPT_KEY_HANDLE	private_key;
    BCRYPT_KEY_HANDLE	public_key;
    
    
    	//
    	// Get the console dimensions so we can tell Program Options the width, so it can
    	// properly format the help output
    	//
    
    	GetConsoleScreenBufferInfo (output_hdl, &AT_sb_info);
    
    	if (AT_sb_info.dwSize.X!=0)
    		{
    		display_width = AT_sb_info.dwSize.X;
    		}
    	else
    		{
    		display_width = AT_DEFAULT_DISPLAY_WIDTH;
    		}
    
    	//
    	// Get the command line, and point to the second argument
    	// which should be the file name
    	//
    
    	command_line = GetCommandLine ();
    	wcstok_s (command_line, separators, &context);
    	file_name = wcstok_s (NULL, separators, &context);
    
    	//
    	// Open and map the file into the process's address space
    	//
    
    	if (NT_SUCCESS (status = Open_and_map_file (file_name, &file_handle, &mapping_handle, &mapped_address, &mapped_size)))
    		{
    		printf ("File: %S\n", file_name);
    		printf ("Size: %d\n", mapped_size);
    
    		//
    		// Hash the file
    		//
    
    		if (NT_SUCCESS (status = Hash_data ((PUCHAR)mapped_address, mapped_size, &hash, &hash_size)))
    			{
    
    			//
    			// Display the hash
    			//
    
    			printf ("Hash:\n");
    			Dump (hash, hash_size, display_width, TRUE, FALSE);
    
    			//
    			// Sign the hash
    			//
    
    			if (NT_SUCCESS (status = Sign_hash ((PUCHAR)hash, hash_size, &signature, &signature_size, &key, &key_size)))
    				{
    
    				//
    				// Display the signature
    				//
    
    				printf ("\nSignature:\n");
    				Dump (signature, signature_size, display_width, TRUE, FALSE);
    
    				//
    				// Create a public and private key pair
    				//
    
    				if (NT_SUCCESS (status = Create_keys (&private_key, &public_key)))
    					{
    
    					//
    					// Encrypt the data
    					//
    
    					if (NT_SUCCESS (status = Encrypt (public_key, (PUCHAR)signature, signature_size, &cipher_text, &cipher_text_length)))
    						{
    
    						//
    						// Display the encrypted signature
    						//
    
    						printf ("Encrypted signature:\n");
    						Dump (cipher_text, cipher_text_length, display_width, TRUE, FALSE);
    
    						//
    						// Decrypt the signature
    						//
    
    						if (NT_SUCCESS (status = Decrypt (private_key, cipher_text, cipher_text_length, &plain_text, &plain_text_length)))
    							{
    							//
    							// Display the decrypted signature
    							//
    
    							printf ("Decrypted signature:\n");
    							Dump (plain_text, plain_text_length, display_width, TRUE, FALSE);
    
    							//
    							// Compare the original signature with the decrypted version
    							//
    
    							if (!memcmp (signature, plain_text, plain_text_length))
    								{
    								printf ("\nDecrypted signature matches original signature\n");
    								}
    							else
    								{
    								printf ("\n*** Decrypted signature does NOT match original signature ***\n");
    								}
    
    							printf ("\n\n");
    							}
    						else
    							{
    							printf ("Error decrypting signature, status = %08x\n", status);
    							}
    
    						}
    					else
    						{
    						printf ("Error encrypting %d bytes, status = %08x\n", signature_size, status);
    						}
    
    					}
    				else
    					{
    					printf ("Error creating public and private keys, status = %08x\n", status);
    					}
    
    				//
    				// Display the key blob
    				//
    
    				printf ("Key BLOB:\n");
    				Dump (key, key_size, display_width, TRUE, TRUE);
    				}
    			else
    				{
    
    				//
    				// Error signing the hash
    				//
    
    				printf ("Error signing the hash\n");
    				}
    
    			}
    		else
    			{
    
    			//
    			// Error hashing
    			//
    
    			printf ("Error hashing data\n");
    			}
    
    		}
    	else
    		{
    
    		//
    		// Couldn't open the file
    		//
    
    		status = GetLastError ();
    		printf ("Error opening file %S, status = %08x\n", file_name, status);
    		}
    
    	return status;
    }							// End of function main
    
    
    NTSTATUS 
    Create_keys												// Create public and private keys
    	(
    	OUT BCRYPT_KEY_HANDLE	*Private_key,				// Handle to private key
    	OUT BCRYPT_KEY_HANDLE	*Public_key					// Handle to public key
    	)
    
    //+
    //
    // DESCRIPTION:		
    //
    // ASSUMPTIONS:		
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS					status = STATUS_UNSUCCESSFUL;
    BCRYPT_ALG_HANDLE			algorithm_handle;
    BCRYPT_KEY_HANDLE			key_handle;
    BCRYPT_KEY_LENGTHS_STRUCT	key_lengths;
    DWORD						bytes_written;
    BCRYPT_KEY_HANDLE			public_key;
    PUCHAR						public_key_blob;
    ULONG						public_key_blob_size;
    
    
    	//
    	// Get a handle to the encryption algorithm
    	//
    
    	if (NT_SUCCESS (status = BCryptOpenAlgorithmProvider (&algorithm_handle, ASYM_TEST_SIGN_ALG, NULL, 0)))
    		{
    
    		//
    		// Get the key lengths supported by the algorithm
    		//
    
    		if (NT_SUCCESS (status = BCryptGetProperty (algorithm_handle, BCRYPT_KEY_LENGTHS, 
    				(PBYTE)&key_lengths, sizeof (key_lengths), &bytes_written, 0)))
    			{
    
    			//
    			// Generate a key pair.  The size of the key is the minimum supported by the algorithm
    			//
    
    			if (NT_SUCCESS (status = BCryptGenerateKeyPair (algorithm_handle, &key_handle, key_lengths.dwMinLength, 0)))
    				{
    
    				//
    				// Finalize the key pair
    				//
    
    				if (NT_SUCCESS (status = BCryptFinalizeKeyPair (key_handle, 0)))
    					{
    
    					//
    					// Get the size of the public key blob
    					//
    
    					if (NT_SUCCESS (status = BCryptExportKey (key_handle, NULL, 
    							ASYM_TEST_PUBLIC_BLOB, 0, 0, &public_key_blob_size, 0)))
    						{
    
    						//
    						// Allocate the buffer for the public key blob
    						//
    
    						if ((public_key_blob = (PBYTE)HeapAlloc (GetProcessHeap (), 0, public_key_blob_size)) != 0)
    							{
    
    							//
    							// Get the public key blob
    							//
    
    							if (NT_SUCCESS (status = BCryptExportKey (key_handle, NULL, 
    									ASYM_TEST_PUBLIC_BLOB, public_key_blob, public_key_blob_size, &bytes_written, 0)))
    								{
    
    								//
    								// Import the public key blob into a new key pair
    								//
    
    								if (NT_SUCCESS (status = BCryptImportKeyPair (algorithm_handle, 0, ASYM_TEST_PUBLIC_BLOB, 
    										&public_key, public_key_blob, public_key_blob_size, 0)))
    									{
    
    									//
    									// Return keys to caller
    									//
    
    									*Private_key = key_handle;
    									*Public_key = public_key;
    									}
    								else
    									{
    									printf ("Error importing public key, status = %08x\n", status);
    									}
    
    								}
    							else
    								{
    								printf ("Error exporting public key, status = %08x\n", status);
    								}
    
    							}
    						else
    							{
    							printf ("Error allocating %d bytes for public key blob\n", public_key_blob_size);
    							status = STATUS_NO_MEMORY;
    							}
    
    						}
    					else
    						{
    						printf ("Error getting size of public key blob, status = %08x\n", status);
    						}
    
    					}
    				else
    					{
    					printf ("Error finalizing the key pair, status = %08x\n", status);
    					}
    
    				}
    			else
    				{
    				printf ("Error generating the key pair, status = %08x\n", status);
    				}
    
    			}
    		else
    			{
    			printf ("Error getting key length property, status = %08x\n", status);
    			}
    
    		}
    	else
    		{
    		printf ("Error opening a handle to the algorithm %S, status = %08x\n", ASYM_TEST_SIGN_ALG, status);
    		}
    
    
    	return status;
    }							// End of function Create_keys
    
    
    void 
    Dump													// Dump a buffer (in hex) to the console
    	(
    	IN PVOID	Address,								// Address of buffer
    	IN DWORD	Length,									// Length of buffer
    	IN DWORD	Line_width,								// Maximum width of output line
    	IN DWORD	Show_offset,							// Display the offset on each line
    	IN DWORD	Show_ascii								// Display ASCII representation of data
    	)
    
    //+
    //
    // DESCRIPTION:		Dump the buffer to the console, in the proper little-endian format.
    //
    // ASSUMPTIONS:		
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    DWORD		i;
    DWORD		chars_per_byte;
    PUCHAR		buffer = (PUCHAR)Address;
    DWORD		overhead;
    DWORD		bytes_per_line;
    DWORD		num_groups;
    DWORD		offset_index;
    DWORD		ascii_index;
    PUCHAR		line;
    DWORD		bytes_this_line;
    DWORD		j;
    DWORD		cur;
    DWORD		num_lines;
    DWORD		group_size;
    DWORD		first_hex_index;
    DWORD		next_column;
    
    
    	//
    	// Calculate how many bytes can be dumped per line.  A line consists of:
    	// some number of longword groups, separated by spaces, optionally the offset into the buffer,
    	// and optionally the ASCII representation of each of the bytes in the longwords.
    	//
    
    	if (Show_offset)
    		{
    		overhead = 3 + 8;								// 3 spaces + 8 characters for the offset value
    		}
    	else
    		{
    		overhead = 0;									// Without the offset, there isn't any fixed overhead
    		}
    
    	if (Show_ascii)
    		{
    		chars_per_byte = 3;								// 2 hex characters per byte + 1 ASCII character per byte
    		overhead = overhead + 1;						// Space before the ASCII
    		}
    	else
    		{
    		chars_per_byte = 2;								// 2 hex characters per byte
    		}
    
    	group_size = 8 + 1;									// 8 characters per longword + 1 space
    	num_groups = (Line_width - overhead) / ((chars_per_byte * 4) + 1);
    	bytes_per_line = num_groups * 4;					// num_groups * 4 bytes per group
    	first_hex_index = (num_groups * group_size);
    	next_column = first_hex_index;
    
    	if (Show_offset)
    		{
    		offset_index = next_column + 3;					// Calculate where the offset appears on the line
    		next_column = offset_index + 8;					// 8 hex characters in offset
    		}
    	else
    		{
    		offset_index = 0;
    		}
    
    	if (Show_ascii)
    		{
    		ascii_index = next_column + 1;					// Space before ASCII
    		}
    	else
    		{
    		ascii_index = 0;
    		}
    
    	//
    	// Allocate the line buffer
    	//
    
    	if ((line = (PUCHAR) malloc (Line_width)) != 0)
    		{
    
    		//
    		// Walk through the buffer one line at a time
    		//
    
    		num_lines = ((Length + bytes_per_line - 1) / bytes_per_line);
    
    		for (i = 0; i < num_lines; i++)
    			{
    			bytes_this_line = Length - (i * bytes_per_line);
    
    			if (bytes_this_line > bytes_per_line)
    				{
    				bytes_this_line = bytes_per_line;
    				}
    
    			//
    			// Erase the line
    			//
    
    			memset (line, ' ', Line_width);
    
    			//
    			// Write the current offset
    			//
    
    			if (Show_offset)
    				{
    				sprintf_s ((char *)&line [offset_index], 9, "%08X", i * bytes_per_line);
    				line [offset_index + 8] = ' ';		// sprintf_s zero-terminates the buffer; replace the space
    				}
    
    			cur = first_hex_index;
    
    			//
    			// Display the bytes on the current line
    			//
    
    			for (j = 0; j < bytes_this_line; j++)
    				{
    
    				if ((j % 4) == 0)
    					{
    					cur = cur - 1;
    					}
    
    				//
    				// Write the little-endian hex representation of the byte
    				//
    
    				line [cur] = nibble_to_hex [(buffer [(i * bytes_per_line) + j] >> 0) & 0xf];
    				cur = cur - 1;
    				line [cur] = nibble_to_hex [(buffer [(i * bytes_per_line) + j] >> 4) & 0xf];
    				cur = cur - 1;
    
    				//
    				// Write the ASCII representation of the byte
    				//
    
    				if (Show_ascii)
    					{
    
    					if (isprint (buffer [(i * bytes_per_line) + j]))
    						{
    						line [ascii_index + j] = buffer [(i * bytes_per_line) + j];
    						}
    					else
    						{
    						line [ascii_index + j] = '.';
    						}
    
    					}
    
    				}	// End for j
    
    			printf ("%.*s", Line_width, line);
    			}	// End for i
    
    		}
    	else
    		{
    
    		//
    		// Couldn't allocate line buffer
    		//
    
    		printf ("Error allocating %d bytes for line buffer\n", Line_width);
    		}
    
    	free (line);
    }							// End of function dump
    
    
    NTSTATUS 
    Decrypt													// Decrypt a buffer
    	(
    	IN BCRYPT_KEY_HANDLE	Key_handle,					// Key to use
    	IN PUCHAR				Input_buffer,				// Address of input buffer
    	IN DWORD				Input_buffer_length,		// Length of input buffer
    	IN PUCHAR*				Output_buffer,				// Address of output buffer
    	IN DWORD*				Output_buffer_length		// Length of output buffer
    	)
    
    //+
    //
    // DESCRIPTION:		
    //
    // ASSUMPTIONS:		
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS					status = STATUS_UNSUCCESSFUL;
    PUCHAR						output_buffer;
    ULONG						output_buffer_length;
    DWORD						bytes_written;
    
    
    	//
    	// Find out how big the output will be
    	//
    
    	if (NT_SUCCESS (status = BCryptDecrypt (Key_handle, Input_buffer, Input_buffer_length, 0, 0,  0, 0, 0, &output_buffer_length, 0)))
    		{
    
    		//
    		// Allocate a buffer to hold the output
    		//
    
    		if ((output_buffer = (PBYTE)HeapAlloc (GetProcessHeap (), 0, output_buffer_length)) != 0)
    			{
    
    			//
    			// Decrypt the data using the public key
    			//
    
    			if (NT_SUCCESS (status = BCryptDecrypt (Key_handle, Input_buffer, Input_buffer_length, 0,
    					0, 0, output_buffer, output_buffer_length, &bytes_written, 0)))
    				{
    
    				//
    				// Return the cipher text to the caller
    				//
    
    				*Output_buffer = output_buffer;
    				*Output_buffer_length = output_buffer_length;
    				}
    			else
    				{
    
    				//
    				// Error decrypting
    				//
    
    				printf ("Error decrypting, status = %08x\n", status);
    				}
    
    			}
    		else
    			{
    			printf ("Error allocating %d bytes for output buffer\n", output_buffer_length);
    			status = STATUS_NO_MEMORY;
    			}
    
    		}
    	else
    		{
    		printf ("Error getting size of decrypt output buffer, status = %08x\n", status);
    		}
    
    
    
    	return status;
    }							// End of function Encrypt_decrypt
    
    
    NTSTATUS 
    Encrypt													// Encrypt a buffer
    	(
    	IN BCRYPT_KEY_HANDLE	Key_handle,					// Key to use
    	IN PUCHAR				Input_buffer,				// Address of input buffer
    	IN DWORD				Input_buffer_length,		// Length of input buffer
    	IN PUCHAR*				Output_buffer,				// Address of output buffer
    	IN DWORD*				Output_buffer_length		// Length of output buffer
    	)
    
    //+
    //
    // DESCRIPTION:		
    //
    // ASSUMPTIONS:		
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS					status = STATUS_UNSUCCESSFUL;
    PUCHAR						output_buffer;
    ULONG						output_buffer_length;
    DWORD						bytes_written;
    
    
    	//
    	// Find out how big the output will be
    	//
    
    	if (NT_SUCCESS (status = BCryptEncrypt (Key_handle, Input_buffer, Input_buffer_length, 0,
    			0,  0, 0, 0, &output_buffer_length, 0)))
    		{
    
    		//
    		// Allocate a buffer to hold the output
    		//
    
    		if ((output_buffer = (PBYTE)HeapAlloc (GetProcessHeap (), 0, output_buffer_length)) != 0)
    			{
    
    			//
    			// Encrypt the data using the private key
    			//
    
    			if (NT_SUCCESS (status = BCryptEncrypt (Key_handle, Input_buffer, Input_buffer_length, 0,
    					0, 0, output_buffer, output_buffer_length, &bytes_written, 0)))
    				{
    
    				//
    				// Return the cipher text to the caller
    				//
    
    				*Output_buffer = output_buffer;
    				*Output_buffer_length = output_buffer_length;
    				}
    			else
    				{
    
    				//
    				// Error encrypting
    				//
    
    				printf ("Error encrypting, status = %08x\n", status);
    				}
    
    			}
    		else
    			{
    			printf ("Error allocating %d bytes for output buffer\n", output_buffer_length);
    			status = STATUS_NO_MEMORY;
    			}
    
    		}
    	else
    		{
    		printf ("Error getting size of output buffer, status = %08x\n", status);
    		}
    
    	return status;
    }							// End of function Encrypt
    
    
    NTSTATUS 
    Hash_data												// Hash a buffer
    	(
    	IN PUCHAR	Data,									// Address of data to hash
    	IN DWORD	Length,									// Length of data to hash
    	OUT PVOID	*Hash,									// Address of buffer containing hash
    	OUT DWORD	*Hash_size								// Length of hash
    	)
    
    //+
    //
    // DESCRIPTION:		Hash the data starting at the specified address, for the specified length
    //
    // ASSUMPTIONS:		Using SHA1 hash.  Note: DSA will not work with SHA256 (this is specified in FIPS 186-2).
    //					If you want to use SHA256, then use RSA not DSA
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS			status = STATUS_UNSUCCESSFUL;
    BCRYPT_ALG_HANDLE	algorithm_handle;
    DWORD				hash_object_size;
    DWORD				bytes_written;
    PUCHAR				hash_object;
    DWORD				hash_size;
    PBYTE				hash_buffer;
    BCRYPT_HASH_HANDLE	hash_object_handle;
    
    
    	//
    	// Get a handle to the algorithm
    	//
    
    	if (NT_SUCCESS (status = BCryptOpenAlgorithmProvider (&algorithm_handle, ASYM_TEST_HASH_ALG, NULL, 0)))
    		{
    
    		//
    		// Find out how big the buffer needs to be to hold the hash object
    		//
    
    		if (NT_SUCCESS (status = BCryptGetProperty (algorithm_handle,  BCRYPT_OBJECT_LENGTH, (PBYTE)&hash_object_size, 
    				sizeof (hash_object_size), &bytes_written, 0)))
    			{
    
    			//
    			// Allocate space for the hash object on the head
    			//
    
    			if ((hash_object = (PUCHAR)HeapAlloc (GetProcessHeap (), 0, hash_object_size)) != 0)
    				{
    
    				//
    				// Create the hash object
    				//
    
    				if (NT_SUCCESS (status = BCryptCreateHash (algorithm_handle, &hash_object_handle, hash_object, 
    						hash_object_size, NULL, 0, 0)))
    					{
    
    					//
    					// Get the size of the hash
    					//
    
    					if (NT_SUCCESS (status =  BCryptGetProperty (algorithm_handle, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_size,
    							sizeof (hash_size), &bytes_written, 0)))
    						{
    
    						//
    						// Allocate a buffer to hold the hash
    						//
    
    						if ((hash_buffer = (PBYTE)HeapAlloc (GetProcessHeap (), 0, hash_size)) != 0)
    							{
    
    							//
    							// Hash the file
    							//
    
    							if (NT_SUCCESS (status = BCryptHashData (hash_object_handle, Data, Length, 0)))
    								{
    
    								//
    								// Read the hash
    								//
    
    								if (NT_SUCCESS (status = BCryptFinishHash (hash_object_handle, hash_buffer, hash_size, 0)))
    									{
    
    									//
    									// Return the hash
    									//
    
    									*Hash = hash_buffer;
    									*Hash_size = hash_size;
    									status = STATUS_SUCCESS;
    
    									//
    									// Cleanup
    									//
    
    									if (!NT_SUCCESS (status = BCryptDestroyHash (hash_object_handle)))
    										{
    										printf ("Error destroying hash, status = %08x\n", status);
    										}
    
    									if (!NT_SUCCESS (status = HeapFree (GetProcessHeap(), 0, hash_object)))
    										{
    										printf ("Error freeing buffer, status = %08x\n", status);
    										}
    
    									if (!NT_SUCCESS (status = BCryptCloseAlgorithmProvider (algorithm_handle, 0)))
    										{
    										printf ("Error closing algorithm provider, status = %08x\n", status);
    										}
    
    									}
    								else
    									{
    
    									//
    									// Error hashing
    									//
    
    									printf ("Error hashing file, status = %08x\n", status);
    									}
    
    								}
    							else
    								{
    
    								//
    								// Error hashing the file
    								//
    
    								printf ("Error hashing the file, status = %08x\n", status);
    								}
    
    							}
    						else
    							{
    
    							//
    							// Error allocating the buffer
    							//
    
    							status = GetLastError ();
    							printf ("Error allocating %d bytes for the hash\n", status);
    							}
    
    					}
    				else
    					{
    
    					//
    					// Error creating the hash object
    					//
    
    					printf ("Couldn't create the hash object, status = %08x\n", status);
    					}
    
    				}
    			else
    				{
    
    				//
    				// Error allocating heap buffer
    				//
    
    				printf ("Error allocating %d bytes on the heap\n", hash_object_size);
    				}
    
    			}
    		else
    			{
    
    			//
    			// Error get hash object size
    			//
    
    			printf ("Error getting hash object size, status = %08x\n", status);
    			}
    
    		}
    	else
    		{
    
    		//
    		// Error opening a handle to the algorithm
    		//
    
    		printf ("Error opening a handle to the algorithm %ws, status = %08x\n", ASYM_TEST_HASH_ALG, status);
    		}
    
    	}
    
    	return status;
    }							// End of function Hash_data
    
    
    NTSTATUS 
    Open_and_map_file										// Map a file into the process's address space
    	(
    	IN LPWSTR	File_name,								// File to map
    	OUT HANDLE	*Handle,								// Handle to file
    	OUT HANDLE	*Mapping_handle,						// Section handle
    	OUT PVOID	*Mapped_address,						// Address of mapped file
    	OUT DWORD	*Mapped_size							// Size of mapped file
    	)
    
    //+
    //
    // DESCRIPTION:		Open the specified file, and map the file's contents into the process's
    //					address space.
    //
    // ASSUMPTIONS:		File must already exist
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS	status;
    HANDLE		file_handle;
    HANDLE		mapping_handle;
    PVOID		mapped_address;
    
    
    	//
    	// Open a handle to the file
    	//
    
    	if ((file_handle = CreateFile (File_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
    		{
    
    		//
    		// Create a file mapping object
    		//
    
    		if ((mapping_handle = CreateFileMapping (file_handle, NULL, PAGE_READONLY, 0, 0, NULL)) != 0)
    			{
    
    			//
    			// Map the file into the process's address space
    			//
    
    			if ((mapped_address = MapViewOfFile (mapping_handle, FILE_MAP_READ, 0, 0, 0)) != 0)
    				{
    
    				//
    				// Return the information on the mapped file
    				//
    
    				*Handle = file_handle;
    				*Mapping_handle = mapping_handle;
    				*Mapped_address = mapped_address;
    				*Mapped_size = GetFileSize (file_handle, 0);	// Assume file size < 4GB !!!
    				status = STATUS_SUCCESS;
    				}
    			else
    				{
    
    				//
    				// Error mapping the file
    				//
    
    				status = GetLastError ();
    				printf ("Error mapping the file, status = %08x\n", status);
    				}
    
    			}
    		else
    			{
    
    			//
    			// Error creating the mapping object
    			//
    
    			status = GetLastError ();
    			printf ("Couldn't create the file mapping object, status = %08x\n", status);
    			}
    		}
    	else
    		{
    
    		//
    		// Error opening the file
    		//
    
    		status = GetLastError ();
    		printf ("Couldn't open a handle to file %S, status = %08x\n", File_name, status);
    		}
    
    	return status;
    }							// End of function Open_and_map_file
    
    
    NTSTATUS 
    Sign_hash												// Sign a hash
    	(
    	IN PUCHAR	Hash,									// Address of buffer containing hash
    	IN DWORD	Length,									// Length of hash
    	OUT PVOID	*Signed_data,							// Address of buffer containing signature
    	OUT DWORD	*Signed_size,							// Length of signature
    	OUT PVOID	*Key,									// Address of buffer containing the key blob
    	OUT DWORD	*Key_size								// Length of key
    	)
    
    //+
    //
    // DESCRIPTION:		Sign the data starting at the specified address, for the specified length
    //
    // ASSUMPTIONS:		Using DSA signature
    //
    // RETURN VALUES:
    //
    //	STATUS_SUCCESS
    //	Return status from Win32 APIs
    //
    // SIDE EFFECTS:
    //
    //	None.
    //
    //-
    
    {
    NTSTATUS					status = STATUS_UNSUCCESSFUL;
    BCRYPT_ALG_HANDLE			algorithm_handle;
    BCRYPT_KEY_LENGTHS_STRUCT	key_lengths;
    BCRYPT_KEY_HANDLE			key_handle;
    DWORD						signature_length;
    PUCHAR						signature;
    DWORD						bytes_written;
    DWORD						key_size;
    PUCHAR						key;
    BCRYPT_KEY_HANDLE			public_key;
    BCRYPT_PKCS1_PADDING_INFO	padding = {ASYM_TEST_HASH_ALG};
    
    
    	//
    	// Get a handle to the algorithm
    	//
    
    	if (NT_SUCCESS (status = BCryptOpenAlgorithmProvider (&algorithm_handle, ASYM_TEST_SIGN_ALG, NULL, 0)))
    		{
    
    		//
    		// Get the key lengths supported by the algorithm
    		//
    
    		if (NT_SUCCESS (status = BCryptGetProperty (algorithm_handle, BCRYPT_KEY_LENGTHS, 
    				(PBYTE)&key_lengths, sizeof (key_lengths), &bytes_written, 0)))
    			{
    
    			//
    			// Generate a key pair.  The size of the key is the minimum supported by the algorithm
    			//
    
    			if (NT_SUCCESS (status = BCryptGenerateKeyPair (algorithm_handle, &key_handle, key_lengths.dwMinLength, 0)))
    				{
    
    				//
    				// Finalize the key pair
    				//
    
    				if (NT_SUCCESS (status = BCryptFinalizeKeyPair (key_handle, 0)))
    					{
    
    					//
    					// Find out how big the signature will be
    					//
    
    					if (NT_SUCCESS (status = BCryptSignHash (key_handle, 0, Hash, Length, NULL, 0, &signature_length, 0)))
    						{
    
    						//
    						// Allocate a buffer to hold the signature
    						//
    
    						if ((signature = (PBYTE)HeapAlloc (GetProcessHeap (), 0, signature_length)) != 0)
    							{
    
    							//
    							// Sign the data
    							//
    
    							if (NT_SUCCESS (status = BCryptSignHash (key_handle, &padding, Hash, Length, 
    									signature, signature_length, &bytes_written, BCRYPT_PAD_PKCS1)))
    								{
    								signature_length = bytes_written;
    
    								//
    								// Get the size of the public key
    								//
    
    								if (NT_SUCCESS (status = BCryptExportKey (key_handle, NULL, 
    										ASYM_TEST_PUBLIC_BLOB, 0, 0, &key_size, 0)))
    									{
    
    									//
    									// Allocate the buffer for the key
    									//
    
    									if ((key = (PBYTE)HeapAlloc (GetProcessHeap (), 0, key_size)) != 0)
    										{
    
    										//
    										// Get the public key
    										//
    
    										if (NT_SUCCESS (status = BCryptExportKey (key_handle, NULL, 
    												ASYM_TEST_PUBLIC_BLOB, key, key_size, &bytes_written, 0)))
    											{
    											key_size = bytes_written;
    
    											*Signed_data = signature;
    											*Signed_size = signature_length;
    											*Key = key;
    											*Key_size = key_size;
    
    											//
    											// Import the key blob into a new temporary key
    											//
    
    											if (NT_SUCCESS (status = BCryptImportKeyPair (algorithm_handle, NULL, 
    													ASYM_TEST_PUBLIC_BLOB, &public_key, key, key_size, 0)))
    												{
    
    												//
    												// Validate the signature
    												//
    
    												if (!NT_SUCCESS (status = BCryptVerifySignature (public_key, &padding, Hash, Length, 
    														signature, signature_length, BCRYPT_PAD_PKCS1)))
    													{
    													printf ("Error verifying signature, status = %08x\n", status);
    													}
    
    												}
    											else
    												{
    
    												//
    												// Error importing the key blob
    												//
    
    												printf ("Error importing key blob, status = %08x\n", status);
    												}
    
    											//
    											// Cleanup
    											//
    
    											if (!NT_SUCCESS (status = BCryptDestroyKey (key_handle)))
    												{
    												printf ("Error destroying key, status = %08x\n", status);
    												}
    
    											if (!NT_SUCCESS (status = BCryptDestroyKey (public_key)))
    												{
    												printf ("Error destroying temporary key, status = %08x\n", status);
    												}
    
    											if (!NT_SUCCESS (status = BCryptCloseAlgorithmProvider (algorithm_handle, 0)))
    												{
    												printf ("Error closing algorithm provider, status = %08x\n", status);
    												}
    
    											}
    										else
    											{
    
    											//
    											// Error getting public key
    											//
    
    											printf ("Error getting public key, status = %08x\n", status);
    											}
    
    										}
    									else
    										{
    
    										//
    										// Error allocating buffer
    										//
    
    										status = GetLastError ();
    										printf ("Error allocating buffer of size %d, status = %08x\n", key_size, status);
    										}
    
    									}
    								else
    									{
    
    									//
    									// Error exporting key
    									//
    
    									printf ("Error exporting the public key, status = %08x\n", status);
    									}
    
    								}
    							else
    								{
    
    								//
    								// Error signing the data
    								//
    
    								printf ("Error signing the hash, status = %08x\n", status);
    								}
    
    							}
    						else
    							{
    
    							//
    							// Error allocating a buffer
    							//
    
    							status = GetLastError ();
    							printf ("Error allocating a buffer of %d length, status = %08x\n", signature_length, status);
    							}
    
    						}
    					else
    						{
    
    						//
    						// Error querying signature length
    						//
    
    						printf ("Error querying signature length, status = %08x\n", status);
    						}
    
    					}
    				else
    					{
    
    					//
    					// Error finalizing the key pair
    					//
    
    					printf ("Error finalizing the key pair, status = %08x\n", status);
    					}
    
    				}
    			else
    				{
    
    				//
    				// Error generating key pair
    				//
    
    				printf ("Error generating key pair, status = %08x\n", status);
    				}
    
    			}
    		else
    			{
    
    			//
    			// Error from BCryptGetProperty
    			//
    
    			printf ("Error getting key lengths, status = %08x\n", status);
    			}
    
    		}
    	else
    		{
    
    		//
    		// Error opening a handle to the algorithm
    		//
    
    		printf ("Error opening a handle to the algorithm %S, status = %08x\n", ASYM_TEST_SIGN_ALG, status);
    		}
    
    	return status;
    }							// End of function Sign_hash
    
    

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Wednesday, January 22, 2020 10:32 PM
    Moderator