Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/12/23/rsacryptoserviceprovider-fails-if-we-delete-a-user-from-the-machine/
Post name: RSACryptoServiceProvider fails if we delete a user from the machine
Original author: Alejandro Campos Magencio
Posting date: 2008-12-23T07:09:00+00:00


Hi all,

The other day a customer of mine had an interesting issue: a specific user runs their application for the first time without problem on a machine. Any other user can run the app without problems on the same machine, too. If that specific user is deleted from the machine, any other user will get the following exception when the application tries to create an RSACryptoServiceProvider object:

System.Security.Cryptography.CryptographicException: CryptoAPI cryptographic service provider (CSP) for this implementation could not be acquired.

Crypto keys associated to the RSACryptoServiceProvider object are being created in the machine store (instead of the user's profile) thanks to CspProviderFlags.UseMachineKeyStore flag being passed to RSACryptoServiceProvider.

When checking my post Key Containers: Basics, they found the issue:

My post explains how to read the container name of key files stored in our system. When looking for the problematic key file associated to the container name used by their RSACryptoServiceProvider object, they got an Access Violation error when accessing a specificfile. Then they tried to see the permissions on that file, but they got an error message because the original owner of the file was not present anymore on the machine. So my customer set himself as the owner of the file and set proper permissions on it. They verified that it was the key file they were looking for andthanks to thenew permissions onthe file, all other users can access those keys withRSACryptoServiceProvider and without errors.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/12/23/cryptsignhash-fails-with-nte_bad_keyset/
Post name: CryptSignHash fails with NTE_BAD_KEYSET
Original author: Alejandro Campos Magencio
Posting date: 2008-12-23T06:39:00+00:00


Hi all,


The other day I worked on the following issue:a customer of mine had developed an ActiveX which they usedto sign some data in Internet Explorer. Their signing code was based on CryptoAPI and worked just fine with some certs, but failed with others with a NTE_BAD_KEYSET error.


So we took some traces with my CryptoAPI Tracer script to see which API was returning the error exactly.


This is the API that failed with NTE_BAD_KEYSET error:

CryptSignHashA (0x1f2c)

IN
hHash
0x3346208

dwKeySpec
AT_KEYEXCHANGE

sDescription
NULL

dwFlags
0

pbSignature
NULL

dwSigLen
46500472

OUT
dwSigLen
46500472

RESULT
CryptSignHashA (0x1f2c) FAILED
LastErrorValue: (HRESULT) 0x80090016 (2148073494) - Keyset does not exist
LastStatusValue: (NTSTATUS) 0xc0000034 - Object Name not found


My post CryptAcquireContext fails with NTE_BAD_KEYSET refers to CryptAcquireContext API, but the causes of the error should be the same: keys don't exist or we have no permissions to access them.


We can see above that we are trying tosign with AT_KEYEXCHANGE keys.


CryptSignHash Function
"
dwKeySpec [in]
Identifies the private key to use from the provider's container. It can be AT_KEYEXCHANGE or AT_SIGNATURE.
The signature algorithm used is specified when the key pair is originally created.
"


Summing up, we may have two public/private key pairs: AT_KEYEXCHANGEand AT_SIGNATURE. When we generate the keys, we specify which ones we are creating.


CryptGenKey Function
"
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.
Value Meaning
AT_KEYEXCHANGE Key exchange
AT_SIGNATURE Digital signature

"


When we enroll a cert, keys of a given type get created and associated to it. We can use certutil utility on a cert (i.e. "certutil -dump -v cert.pfx") to check if its KeySpec is AT_KEYEXCHANGE or AT_SIGNATURE. In this particular case, working certs have AT_KEYEXCHANGE KeySpec, and failing certs have AT_SIGNATURE.


So we are trying to sign with AT_KEYEXCHANGE keys but the certificates only have AT_SIGNATURE keys.



When we get the CSP of our certificate via API, we can find out which kind of keys are associated to it:


CryptAcquireCertificatePrivateKey
"
pdwKeySpec [out]
The address of a DWORD variable that receives additional information about the key. This can be one of the following values.
Value Meaning
AT_KEYEXCHANGE The key pair is a key exchange pair.
AT_SIGNATURE The key pair is a signature pair.

"


The following sample shows how to use CryptAcquireCertificatePrivateKeyto get the CSP of a certificate and its Keyspec, and sign with it:


How to sign and verify with CryptoAPI and a user certificate


Note: in this sample I only sign with AT_SIGNATURE. We should modify the sample to get the right KeySpec with CryptAcquireCertificatePrivateKey and pass it to CryptSignHash so it uses the right keys to sign.


I hope this helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/12/11/how-to-sign-exe-files-with-an-authenticode-certificate-part-2/
Post name: How to sign EXE files with an Authenticode certificate (part 2)
Original author: Alejandro Campos Magencio
Posting date: 2008-12-11T04:59:00+00:00


Hi all, welcome back,


The other day a customer of mine was having an issue with SignTool.exe when signing an EXE file. The EXE file was getting corrupted/unusable after signing it.


When troubleshooting this issue, I had the chance to play a bit morewith SignTool and check what it does behind the scenes.


Note: I already talked a bit about signing EXEs in post How to sign EXE files with an Authenticode certificate (VB.NET). This new post will add more details and samples.


SignTool.exe uses CAPICOM.SignedCode class and its Sign method to do the signing.


The following VBScript shows how we may use CAPICOM to do the signing programmatically:

Option Explicit

Dim szCertName, szExeToSign
szCertName = "My cert Subject"
szExeToSign = "MyApplication.exe"

Const CAPICOM_CURRENT_USER_STORE = 2
Const CAPICOM_STORE_OPEN_READ_ONLY = 0
Const CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME = 1
Const CAPICOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_DESCRIPTION = 2

' Get certificate for signing
'
Dim objStore
Set objStore = WScript.CreateObject("CAPICOM.Store")
objStore.Open CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY
Dim objSigningCert
Set objSigningCert = objStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, szCertName).Item(1)

' Create a signer for the code
'
Dim objSigner
Set objSigner = WScript.CreateObject("CAPICOM.Signer")
objSigner.Certificate = objSigningCert

' Sign the file
'
Dim objSignedCode
Set objSignedCode = WScript.CreateObject("CAPICOM.SignedCode")
objSignedCode.FileName = szExeToSign
objSignedCode.Sign objSigner
objSignedCode.TimeStamp "http://timestamp.globalsign.com/scripts/timstamp.dll"

WScript.Echo "Done!"


Note thatthis sample also shows how to time stamp a signature programmatically.


This sample was also reproducing my customer's issue. SoI checked what CAPICOM does behind the scenes to further troubleshoot the issue.


CAPICOM.SignedCode.Sign uses CryptUIWizDigitalSign API to do the signing.


The following VB.NET sample uses CryptUIWizDigitalSign through P/Invoke to do the signing programmatically:

<SAMPLE file="Crypto.vb">

Imports System.Runtime.InteropServices
Imports System.Security.Cryptography
Imports System.ComponentModel
Imports System.Windows.Forms

Public Class Crypto

' #define CRYPTUI_WIZ_NO_UI 1
Public Const CRYPTUI_WIZ_NO_UI As Int32 = 1

' #define CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE 0x01
Public Const CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE As Int32 = 1

' #define CRYPTUI_WIZ_DIGITAL_SIGN_CERT 0x01
Public Const CRYPTUI_WIZ_DIGITAL_SIGN_CERT As Int32 = 1

' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_INFO {
' DWORD dwSize;
' DWORD dwSubjectChoice;
' union {
' LPCWSTR pwszFileName;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO pSignBlobInfo;
' };
' DWORD dwSigningCertChoice;
' union {
' PCCERT_CONTEXT pSigningCertContext;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_STORE_INFO pSigningCertStore;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pSigningCertPvkInfo;
' };
' LPCWSTR pwszTimestampURL;
' DWORD dwAdditionalCertChoice;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO pSignExtInfo;
' } CRYPTUI_WIZ_DIGITAL_SIGN_INFO;
<StructLayout(LayoutKind.Sequential)> _
Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_INFO
Public dwSize As Int32
Public dwSubjectChoice As Int32
<MarshalAs(UnmanagedType.LPWStr)> Public pwszFileName As String
Public dwSigningCertChoice As Int32
Public pSigningCertContext As IntPtr
Public pwszTimestampURL As String
Public dwAdditionalCertChoice As Int32
Public pSignExtInfo As IntPtr
End Structure

' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT {
' DWORD dwSize;
' DWORD cbBlob;
' BYTE* pbBlob;
' } CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT;
<StructLayout(LayoutKind.Sequential)> _
Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
Public dwSize As Int32
Public cbBlob As Int32
Public pbBlob As IntPtr
End Structure

' BOOL WINAPI CryptUIWizDigitalSign(
' DWORD dwFlags,
' HWND hwndParent,
' LPCWSTR pwszWizardTitle,
' PCCRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo,
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT* ppSignContext
' );
<DllImport("Cryptui.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Public Shared Function CryptUIWizDigitalSign( _
ByVal dwFlags As Int32, _
ByVal hwndParent As IntPtr, _
ByVal pwszWizardTitle As String, _
ByRef pDigitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO, _
ByRef ppSignContext As IntPtr _
) As Boolean
End Function

' BOOL WINAPI CryptUIWizFreeDigitalSignContext(
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT pSignContext
' );
<DllImport("Cryptui.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function CryptUIWizFreeDigitalSignContext( _
ByVal pSignContext As IntPtr _
) As Boolean
End Function

End Class

</SAMPLE>

<SAMPLE file="Module1.vb">

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports SignExe.Crypto
Imports System.Security.Cryptography.X509Certificates
Imports System.IO

Module Module1

Sub Main()

' Parameters
Dim certPath As String = "MyCert.pfx"
Dim exePath As String = "MyApplication.exe"
Dim sigPath As String = "signature.sig"

' Variables
'
Dim cert As X509Certificate2
Dim digitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO
Dim pSignContext As IntPtr
Dim pSigningCertContext As IntPtr
Dim signContext As CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
Dim fileOut As FileStream
Dim binWriter As BinaryWriter
Dim blob() As Byte

Try
' Get certificate context
'
cert = New X509Certificate2(certPath, "")
pSigningCertContext = cert.Handle

' Prepare signing info: exe and cert
'
digitalSignInfo = New CRYPTUI_WIZ_DIGITAL_SIGN_INFO
digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo)
digitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE
digitalSignInfo.pwszFileName = exePath
digitalSignInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT
digitalSignInfo.pSigningCertContext = pSigningCertContext
digitalSignInfo.pwszTimestampURL = vbNullString
digitalSignInfo.dwAdditionalCertChoice = 0
digitalSignInfo.pSignExtInfo = IntPtr.Zero

' Sign exe
'
If (Not CryptUIWizDigitalSign( _
CRYPTUI_WIZ_NO_UI, _
IntPtr.Zero, _
vbNullString, _
digitalSignInfo, _
pSignContext _
)) Then
Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizDigitalSign")
End If

' Get the blob with the signature
'
signContext = Marshal.PtrToStructure(pSignContext, GetType(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT))
blob = New Byte(signContext.cbBlob) {}
Marshal.Copy(signContext.pbBlob, blob, 0, signContext.cbBlob)

' Store the signature in a new file
'
fileOut = File.Open(sigPath, FileMode.Create)
binWriter = New BinaryWriter(fileOut)
binWriter.Write(blob)
binWriter.Close()
fileOut.Close()

' Free blob
'
If (Not CryptUIWizFreeDigitalSignContext(pSignContext)) Then
Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizFreeDigitalSignContext")
End If

' We are done
Console.WriteLine("Done!!!")

Catch ex As Win32Exception
' Any expected errors?
'
Console.WriteLine(ex.Message + " error#" + ex.NativeErrorCode.ToString)
Catch ex As Exception
' Any unexpected errors?
'
Console.WriteLine(ex.Message)
End Try

' We are done
'
Console.WriteLine("<< Press any key to continue >>")
Console.ReadKey()

End Sub

End Module

</SAMPLE>


This sample was also reproducing the issue. So I checked what CryptUIWizDigitalSign does behind the scenes.


CryptUIWizDigitalSign API uses SignerSignEx API to do the signing.


The following VC++ sample uses SignerSignEx API to do the signing programmatically:

#include "windows.h"
#include "Wincrypt.h"
#include "stdio.h"
#include "conio.h"

// STRUCTS

typedef struct _SIGNER_FILE_INFO {
DWORD cbSize;
LPCWSTR pwszFileName;
HANDLE hFile;
} SIGNER_FILE_INFO, *PSIGNER_FILE_INFO;

typedef struct _SIGNER_BLOB_INFO {
DWORD cbSize;
GUID *pGuidSubject;
DWORD cbBlob;
BYTE *pbBlob;
LPCWSTR pwszDisplayName;
} SIGNER_BLOB_INFO, *PSIGNER_BLOB_INFO;

typedef struct _SIGNER_SUBJECT_INFO {
DWORD cbSize;
DWORD *pdwIndex;
DWORD dwSubjectChoice;
union {
SIGNER_FILE_INFO *pSignerFileInfo;
SIGNER_BLOB_INFO *pSignerBlobInfo;
} ;
} SIGNER_SUBJECT_INFO, *PSIGNER_SUBJECT_INFO;

typedef struct _SIGNER_CERT_STORE_INFO {
DWORD cbSize;
PCCERT_CONTEXT pSigningCert;
DWORD dwCertPolicy;
HCERTSTORE hCertStore;
} SIGNER_CERT_STORE_INFO, *PSIGNER_CERT_STORE_INFO;

typedef struct _SIGNER_SPC_CHAIN_INFO {
DWORD cbSize;
LPCWSTR pwszSpcFile;
DWORD dwCertPolicy;
HCERTSTORE hCertStore;
} SIGNER_SPC_CHAIN_INFO, *PSIGNER_SPC_CHAIN_INFO;

typedef struct _SIGNER_CERT {
DWORD cbSize;
DWORD dwCertChoice;
union {
LPCWSTR pwszSpcFile;
SIGNER_CERT_STORE_INFO *pCertStoreInfo;
SIGNER_SPC_CHAIN_INFO *pSpcChainInfo;
} ;
HWND hwnd;
} SIGNER_CERT, *PSIGNER_CERT;

typedef struct _SIGNER_ATTR_AUTHCODE {
DWORD cbSize;
BOOL fCommercial;
BOOL fIndividual;
LPCWSTR pwszName;
LPCWSTR pwszInfo;
} SIGNER_ATTR_AUTHCODE, *PSIGNER_ATTR_AUTHCODE;

typedef struct _SIGNER_SIGNATURE_INFO {
DWORD cbSize;
ALG_ID algidHash;
DWORD dwAttrChoice;
union {
SIGNER_ATTR_AUTHCODE *pAttrAuthcode;
} ;
PCRYPT_ATTRIBUTES psAuthenticated;
PCRYPT_ATTRIBUTES psUnauthenticated;
} SIGNER_SIGNATURE_INFO, *PSIGNER_SIGNATURE_INFO;

typedef struct _SIGNER_PROVIDER_INFO {
DWORD cbSize;
LPCWSTR pwszProviderName;
DWORD dwProviderType;
DWORD dwKeySpec;
DWORD dwPvkChoice;
union {
LPWSTR pwszPvkFileName;
LPWSTR pwszKeyContainer;
} ;
} SIGNER_PROVIDER_INFO, *PSIGNER_PROVIDER_INFO;

typedef struct _SIGNER_CONTEXT {
DWORD cbSize;
DWORD cbBlob;
BYTE *pbBlob;
} SIGNER_CONTEXT, *PSIGNER_CONTEXT;

// EXPORTS

typedef HRESULT (WINAPI* SignerFreeSignerContextType)(
__in SIGNER_CONTEXT *pSignerContext
);

typedef HRESULT (WINAPI *SignerSignExType)(
__in DWORD dwFlags,
__in SIGNER_SUBJECT_INFO *pSubjectInfo,
__in SIGNER_CERT *pSignerCert,
__in SIGNER_SIGNATURE_INFO *pSignatureInfo,
__in_opt SIGNER_PROVIDER_INFO *pProviderInfo,
__in_opt LPCWSTR pwszHttpTimeStamp,
__in_opt PCRYPT_ATTRIBUTES psRequest,
__in_opt LPVOID pSipData,
__out SIGNER_CONTEXT **ppSignerContext
);

// MAIN

void main()
{
// PARAMETERS

// File to sign
LPCWSTR pwszFileName = L"C:\\TEST\\MyApplication.exe";

// Signing Cert Subject
LPCWSTR pwszCertSubject = L"My cert Subject";

// VARIABLES
HRESULT hResult = S_OK;
BOOL bResult = TRUE;
HMODULE hMssign32 = NULL;
SignerSignExType pfSignerSignEx = NULL;
SignerFreeSignerContextType pfSignerFreeSignerContext = NULL;
HANDLE hFile = NULL;
HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwIndex = 0;
SIGNER_FILE_INFO signerFileInfo;
SIGNER_SUBJECT_INFO signerSubjectInfo;
SIGNER_CERT_STORE_INFO signerCertStoreInfo;
SIGNER_CERT signerCert;
SIGNER_SIGNATURE_INFO signerSignatureInfo;
SIGNER_CONTEXT * pSignerContext = NULL;

// MAIN

// Attach a debugger now!
printf("<< Press any key to continue>>\n");
_getch();

// Load library containing SignerSignEx and SignerFreeSignerContext
printf("LoadLibrary...");
hMssign32 = LoadLibrary(L"Mssign32.dll");
if (!hMssign32)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Get SignerSignEx function
printf("GetProcAddress(SignerSignEx)...");
pfSignerSignEx = (SignerSignExType) GetProcAddress(hMssign32, "SignerSignEx");
if (!pfSignerSignEx)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Get SignerFreeSignerContext function
printf("GetProcAddress(SignerFreeSignerContext)...");
pfSignerFreeSignerContext = (SignerFreeSignerContextType) GetProcAddress(hMssign32, "SignerFreeSignerContext");
if (!pfSignerFreeSignerContext)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Open file to sign
printf("CreateFile...");
hFile = CreateFile(
pwszFileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hFile)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Open MY cert store
printf("CertOpenStore...");
hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"
);
if (!hCertStore)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Find signing cert in MY cert store
printf("CertFindCertificateInStore...");
pCertContext = CertFindCertificateInStore(
hCertStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
(void *)pwszCertSubject,
NULL
);
if (!pCertContext)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");

// Prepare SIGNER_FILE_INFO struct
signerFileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
signerFileInfo.pwszFileName = pwszFileName;
signerFileInfo.hFile = hFile;

// Prepare SIGNER_SUBJECT_INFO struct
signerSubjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);
dwIndex = 0;
signerSubjectInfo.pdwIndex = &dwIndex;
signerSubjectInfo.dwSubjectChoice = 1; // SIGNER_SUBJECT_FILE
signerSubjectInfo.pSignerFileInfo = &signerFileInfo;

// Prepare SIGNER_CERT_STORE_INFO struct
signerCertStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);
signerCertStoreInfo.pSigningCert = pCertContext;
signerCertStoreInfo.dwCertPolicy = 2; // SIGNER_CERT_POLICY_CHAIN
signerCertStoreInfo.hCertStore = NULL;

// Prepare SIGNER_CERT struct
signerCert.cbSize = sizeof(SIGNER_CERT);
signerCert.dwCertChoice = 2; // SIGNER_CERT_STORE
signerCert.pCertStoreInfo = &signerCertStoreInfo;
signerCert.hwnd = NULL;

// Prepare SIGNER_SIGNATURE_INFO struct
signerSignatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);
signerSignatureInfo.algidHash = CALG_SHA1;
signerSignatureInfo.dwAttrChoice = 0; // SIGNER_NO_ATTR
signerSignatureInfo.pAttrAuthcode = NULL;
signerSignatureInfo.psAuthenticated = NULL;
signerSignatureInfo.psUnauthenticated = NULL;

// Sign file with cert
printf("SignerSignEx...");
hResult = pfSignerSignEx(
0,
&signerSubjectInfo,
&signerCert,
&signerSignatureInfo,
NULL,
NULL,
NULL,
NULL,
&pSignerContext
);
if (S_OK != hResult)
{
printf("Error #%d\n", hResult); goto cleanup;
}
printf("Done!\n");

printf("\nSUCCESS!!!\n");

// Clean up
cleanup:

if (pSignerContext)
{
hResult = pfSignerFreeSignerContext(pSignerContext);
}

if (pCertContext)
{
bResult = CertFreeCertificateContext(pCertContext);
}

if (hCertStore)
{
bResult = CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
}

if (hFile)
{
bResult = CloseHandle(hFile);
}

if (hMssign32)
{
bResult = FreeLibrary(hMssign32);
}

// Exit
printf("<< Press any key to exit >>\n");
_getch();
return;
}


This sample was also reproducing the issue.



Finally we saw that the issue was not in SignTool.exe / CAPICOM.SignCode.Sign / CryptUIWizDigitalSign / SignerSignEx, but in the EXE itself!


I run Visual Studio's Dumpbin.exe with its "/HEADER" parameter to list the problematic EXE's PE header information before and after signing it (the Microsoft Portable Executable and Common Object File Format Specification gives detailed information about PE header information).


Before signing, I could see the following Optional Header Value:

1400 [ C40] RVA [size] of Certificates Directory

After signing, I could see the following value:

1400 [ 1650] RVA [size] of Certificates Directory


Certificates Directory points to the location of the code signing signature in the binary. So an EXE which has not been signed should contain the following values:

0 [ 0] RVA [size] of Certificates Directory

Which was not our case. So SignerSignEx was not corrupting the EXE when signing it. It was already corrupted before that, even if it was successfully running before signing!


Just for testing, I opened the problematic EXE with a binary editor before signing it. I looked for "00 14 00 00 04 0c 00 00" bytes (correspondent to "1400 [ C40] RVA [size] of Certificates Directory" values), changed them to "00 00 00 00 00 00 00 00" and signed the EXE. It worked! Now the EXE is not corrupted and I can see the Digital Signature in Explorer, the certificate I used to sign, launch the EXE and run it as expected.


Manually modifying the PE header is not supported by Microsoft. If you face a similar issue, you should work with the team that developed the EXE and focus on why the EXE got generated with an invalid PE header in the first place.


I looked on the Internet and found that there are some third-party tools which may help us to see and modify the PE header if needed for testing purposes, for example PEInfo 0.9 BETA which I haven't tried so I can't neither recommend nor discourage its use.


Ihope this helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/11/26/how-to-change-windows-theme-programmatically-in-xp/
Post name: How to change Windows Theme programmatically in XP
Original author: Alejandro Campos Magencio
Posting date: 2008-11-26T10:30:00+00:00


Hi all,


You may know already that there is no i.e. COM object or .NET class we can use to change the Windows Theme programmatically on Windows XP. You may also know the following VBScript which can be used to do this change without user intervention:

Set OSHApp = CreateObject("Shell.Application")
Set oShell = CreateObject("Wscript.Shell")

' Set the Path to the Theme file
'
Theme = "\\SERVER\SHAREDFOLDER\FILE.THEME"
Theme = """" + Theme + """"
' Open the Theme in Display Properties
'
oSHApp.ControlPanelItem cstr("desk.cpl desk,@Themes /Action:OpenTheme /file:" & Theme)

' Loop and wait until Display Properties is loaded.
'
While OShell.AppActivate ("Display Properties") = FALSE
Wscript.Sleep 1000
Wend

' Loop and send the Enter key to Display Properties until Theme is applied
'
While OShell.AppActivate ("Display Properties") = TRUE
oShell.AppActivate "Display Properties"
Wscript.Sleep 200
oShell.sendkeys "{ENTER}"
Wend


If you try this script,it will sometimes fail. In some situations, the Display Properties dialog won't be closed automatically. You may "play" with the Sleep times and solve the issue in several machines, but it won't necessarily work in all of them.


The issue here is that "oShell.sendkeys "{ENTER}"" is not getting to the right window and there is any guarantee that it will do it.


Fortunately there is an alternate solution to this script and its SendKeys: send a BM_CLICK window message to the button we need to press in the required Theme window. This way we can warrantee that we close the right window by pressing the right button.


You will find a VB.NET sample below which gets all windows in the desktop (visible and invisible), the way Spy++ does it, in a tree. Then the sample also shows how if we select one of those windows in the tree manually (i.e. the print dialog of notepad), we caninteract with it programmatically(i.e. changing the editbox with the number of copies to print and pressing the Print button).


It should be easy to adapt this sample to our needs so it first launches the Theme console to change the theme the way the script above does it (i.e. Running Windows XP Control Panel Applets from Visual Basic.NET 2005), and then it looks for the window we need to close, locates its OK button and sends a BM_CLICK message to it in order to close the window.


Here is the sample:

Imports System.Runtime.InteropServices

Public Class Form1
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()

'Add any initialization after the InitializeComponent() call

' Get all windows and show them in the tree
'
GetWindows()
TreeView1.Nodes.Item(0).Expand()
End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents Button2 As System.Windows.Forms.Button
Private Sub InitializeComponent()
Me.TreeView1 = New System.Windows.Forms.TreeView
Me.Button1 = New System.Windows.Forms.Button
Me.Button2 = New System.Windows.Forms.Button
Me.SuspendLayout()
'
'TreeView1
'
Me.TreeView1.HideSelection = False
Me.TreeView1.ImageIndex = -1
Me.TreeView1.Location = New System.Drawing.Point(8, 48)
Me.TreeView1.Name = "TreeView1"
Me.TreeView1.SelectedImageIndex = -1
Me.TreeView1.Size = New System.Drawing.Size(640, 504)
Me.TreeView1.TabIndex = 0
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(8, 8)
Me.Button1.Name = "Button1"
Me.Button1.Size = New System.Drawing.Size(160, 32)
Me.Button1.TabIndex = 1
Me.Button1.Text = "Refresh"
'
'Button2
'
Me.Button2.Location = New System.Drawing.Point(176, 8)
Me.Button2.Name = "Button2"
Me.Button2.Size = New System.Drawing.Size(472, 32)
Me.Button2.TabIndex = 2
Me.Button2.Text = "Set ""Number of copies"" and click ""Print"" in selected notepad's Print dialog"
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(656, 558)
Me.Controls.Add(Me.Button2)
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.TreeView1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)

End Sub

#End Region

#Region "AlejaCMa"

' Constants
'
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2

Private Const WM_SETTEXT = &HC
Private Const BM_CLICK = &HF5

' API declarations
'
Private Declare Function GetDesktopWindow Lib "user32" () As IntPtr

Private Declare Function GetWindow Lib "user32" ( _
ByVal hWnd As IntPtr, _
ByVal uCmd As Int32) _
As IntPtr

Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As IntPtr, _
ByVal lpString As String, _
ByVal nMaxCount As Int32) _
As Int32

Public Declare Function GetWindowThreadProcessId Lib "user32" Alias "GetWindowThreadProcessId" ( _
ByVal hwnd As IntPtr, _
ByRef lpdwProcessId As Int32) _
As Int32

Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" ( _
ByVal hWnd As IntPtr, _
ByVal lpClassName As String, _
ByVal nMaxCount As Int32) _
As Int32

Private Declare Function IsWindowVisible Lib "user32" ( _
ByVal hWnd As IntPtr) _
As Int32

Public Declare Auto Function SendMessage Lib "user32" Alias "SendMessageA" ( _
ByVal hWnd As IntPtr, _
ByVal wMsg As Int32, _
ByVal wParam As Int32, _
ByVal lParam As String) _
As Int32

' Get windows, and show them in the tree
'
Private Sub GetWindows()
' Variables
'
Dim hWnd As IntPtr
Dim node As TreeNode
Dim windowText As String
Dim className As String
Dim c As Int32
Dim nodeText As String

' Get desktop window
'
hWnd = GetDesktopWindow()

' Get window text and class name
'
windowText = Space(255)
c = GetWindowText(hWnd, windowText, 255)
windowText = Microsoft.VisualBasic.Left(windowText, c)

className = Space(255)
c = GetClassName(hWnd, className, 255)
className = Microsoft.VisualBasic.Left(className, c)

' Add window to the tree
'
nodeText = String.Format("{0:X8} ""{1}"" {2} (Desktop)", hWnd.ToInt32, windowText, className)
node = TreeView1.Nodes.Add(nodeText)
node.ForeColor = Color.Black

' Search children by recursion
'
GetWindows(hWnd, node)
End Sub

Private Sub GetWindows(ByVal hParentWnd As IntPtr, ByVal parentNode As TreeNode)
' Variables
'
Dim hWnd As IntPtr
Dim node As TreeNode
Dim windowText As String
Dim className As String
Dim c As Int32
Dim nodeText As String

' Get first child window
'
hWnd = GetWindow(hParentWnd, GW_CHILD)

Do Until hWnd.Equals(IntPtr.Zero)
' Get the window text and class name
'
windowText = Space(255)
c = GetWindowText(hWnd, windowText, 255)
windowText = Microsoft.VisualBasic.Left(windowText, c)

className = Space(255)
c = GetClassName(hWnd, className, 255)
className = Microsoft.VisualBasic.Left(className, c)

' Add window to the tree
'
nodeText = String.Format("{0:X8} ""{1}"" {2}", hWnd.ToInt32, windowText, className)
node = parentNode.Nodes.Add(nodeText)
If (IsWindowVisible(hWnd)) Then
' Visible windows are shown in black
'
node.ForeColor = Color.Black
Else
' Invisible windows are shown in red
'
node.ForeColor = Color.Red
End If

' Search children by recursion
'
GetWindows(hWnd, node)

' Get next child window
'
hWnd = GetWindow(hWnd, GW_HWNDNEXT)
Loop
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' Get all windows and show them in the tree
'
TreeView1.Nodes.Clear()
GetWindows()
TreeView1.Nodes.Item(0).Expand()
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
' Variables
'
Dim node As TreeNode
Dim windowInfo As String
Dim hWnd As IntPtr
Dim pid As Int32
Dim processName As String

' Get the selected window in the tree
'
node = TreeView1.SelectedNode

' Get PID (Process ID) for the selected window
'
hWnd = New IntPtr(CInt("&H" + Microsoft.VisualBasic.Left(node.Text, 8)))
GetWindowThreadProcessId(hWnd, pid)

' Get process name from PID
'
processName = Process.GetProcessById(pid).ProcessName

' Check if the selected window in the tree is notepad's Print dialog:
' Window Handle = xxxxxxxx (variable)
' Window Text = Print
' Class Name = #32770
' Process Name = "notepad"
'
windowInfo = Microsoft.VisualBasic.Right(node.Text, node.Text.Length - 9)
If (windowInfo = """Print"" #32770" And processName = "notepad") Then
' The selected window is notepad's Print dialog

' Change the number of copies in "Number of &copies" edit box
'
' xxxxxxxx "Print" #32770 <-- Selected node
' - xxxxxxxx "General" #32770
' - xxxxxxxx "" #32770
' xxxxxxxx "" Edit <-- We have to write here
'
hWnd = New IntPtr(CInt("&H" + Microsoft.VisualBasic.Left(node.Nodes(0).Nodes(13).Nodes(10).Text, 8)))
SendMessage(hWnd, WM_SETTEXT, 0, "2")

' Press the print button
'
' xxxxxxxx "Print" #32770 <-- Selected node
' xxxxxxxx "&Print" Button <-- We have to press here
'
hWnd = New IntPtr(CInt("&H" + Microsoft.VisualBasic.Left(node.Nodes(1).Text, 8)))
SendMessage(hWnd, BM_CLICK, 0, Nothing)
Else
' The selected window is not notepad's Print dialog
'
MessageBox.Show("The selected window is not a notepad's Print dialog")
End If
End Sub
#End Region

End Class


I hope this helps.


Regards,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/11/11/how-to-change-windows-change-password-dialog-to-reflect-our-custom-password-policy/
Post name: How to change Windows Change Password dialog to reflect our custom password policy
Original author: Alejandro Campos Magencio
Posting date: 2008-11-11T04:10:00+00:00


Hi all,

We getmany requests from customers who have created a Password Filter and now would like to change the Change Password dialog of their Windows XP in order to reflect their new password policy. Sowhen users try to change their password and it doesn't meet the new complexity requirements, they can tell exactly why.

Since there is no way of returning meaningful error messages from password filters, the only way to get the policy to the user is through the Change Password dialog. The proper way of doing this would be creating a GINA hook (basically a GINA stub that passes through all calls to MSGINA.dll except those it is interested in) and displaying your own Password Change dialog with policy information (by hooking WlxMessageBox method). This is our only recommended and supported approach.

Related info:

Customizing GINA

810756 White Paper: The Essentials of Replacing Msgina.dll

Please note that GINA DLLs are ignored in WindowsVista and later.

Also note that there are third-party apps which change the password complexity policy and modify the Change Password dialog without modifying the GINA (i.e. Password Policy Enforcer, which I haven't tried so I can't neither recommend nor discurage its use). Please, take into consideration that Microsoft won't help you find out how they made it, so don't bother creating a case with us so we can reverse engineer those apps.Thank you.

I hope this helps.

Regards,

Alex (Alejandro Campos Magencio)