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/04/18/certificate-has-private-key-but-we-get-the-keyset-does-not-exist-error/
Post name: Certificate has private key but we get "the keyset does not exist" error
Original author: Alejandro Campos Magencio
Posting date: 2008-04-18T04:00:00+00:00


Hi all, welcome back,

The other day we were using CAPICOM in aclient script run in Internet Explorer. We were trying to sign some string with the private key of a certificate we previously installed in the client machine, but we kept getting the error "the keyset does not exist", like if the private key didn't exist. But the certificate had been installed from a PFX file, it appeared in the Personal store and we could see the message "You have a private key that corresponds to this certificate" when double-clicking on the cert. What was going on?

The issue was happening in Windows XP SP2. As we saw in Key Containers: Basics, the keysets (key containers) for the user should be in her profile here C:\Documents and Settings\<user_name>\Application Data\Microsoft\Crypto\RSA\<user_SID>. But we realized this folder was empty, even after re-installing the cert. Weird. Whenwe install a new cert (and no smart cards are involved), a file containing the keys associated with it gets created in there...

Additionally, if we imported the PFX into a smart card, everything worked just fine.

Well, we finally realized what was happening:

When importing a PFX with Windows Certificate Import Wizard we can click "Browse" to select a cert store and "Place all certificates in the following store". We can then check "Show physical stores". In our particular scenario, we had two physical stores under "Personal" store:"3rd Party Smart card SW" first and "Registry" second. "3rd Party Smart card SW" was an store for smart cards related to some third-party smart card CSP.

It turned out that if we chose "Automatically select the certificate store based on the type of certificate" in the wizard instead of browsing for a store, the cert was going to "3rd Party Smart card SW" physical storeautomatically and the key file wasn't being created in the user's profile. For this reason CAPICOM was failing, as it couldn't find that file.

So we solved the issue by re-installing the certand manually browsing for "Registry" physical store in "Personal" store. This way the keyset file got created in Microsoft\Crypto\RSA\<userSID> folder, CAPICOM was able to get the private key and everything worked just fine.

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/04/11/how-to-read-a-registry-key-and-its-values-vbscript/
Post name: How to read a registry key and its values (VBScript)
Original author: Alejandro Campos Magencio
Posting date: 2008-04-11T02:27:00+00:00


Hi all, welcome back,


Today I'll share with you a couple of VBScript samples I developed the other day. They use WMI and its StdRegProv class to read the Windows registry.


This sample will take a registry key and show its subkeys and the values within those subkeys:

' Constants (taken from WinReg.h)
'
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003

Const REG_SZ = 1
Const REG_EXPAND_SZ = 2
Const REG_BINARY = 3
Const REG_DWORD = 4
Const REG_MULTI_SZ = 7

' Chose computer name, registry tree and key path
'
strComputer = "." ' Use . for current machine
hDefKey = HKEY_LOCAL_MACHINE
strKeyPath = "SOFTWARE\Microsoft\Cryptography\Defaults\Provider"

' Connect to registry provider on target machine with current user
'
Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")

' Enum the subkeys of the key path we've chosen
'
oReg.EnumKey hDefKey, strKeyPath, arrSubKeys

For Each strSubkey In arrSubKeys

' Show the subkey
'
wscript.echo strSubkey

' Show its value names and types
'
strSubKeyPath = strKeyPath & "\" & strSubkey
oReg.EnumValues hDefKey, strSubKeyPath, arrValueNames, arrTypes

For i = LBound(arrValueNames) To UBound(arrValueNames)
strValueName = arrValueNames(i)
Select Case arrTypes(i)

' Show a REG_SZ value
'
Case REG_SZ
oReg.GetStringValue hDefKey, strSubKeyPath, strValueName, strValue
wscript.echo " " & strValueName & " (REG_SZ) = " & strValue

' Show a REG_EXPAND_SZ value
'
Case REG_EXPAND_SZ
oReg.GetExpandedStringValue hDefKey, strSubKeyPath, strValueName, strValue
wscript.echo " " & strValueName & " (REG_EXPAND_SZ) = " & strValue

' Show a REG_BINARY value
'
Case REG_BINARY
oReg.GetBinaryValue hDefKey, strSubKeyPath, strValueName, arrBytes
strBytes = ""
For Each uByte in arrBytes
strBytes = strBytes & Hex(uByte) & " "
Next
wscript.echo " " & strValueName & " (REG_BINARY) = " & strBytes

' Show a REG_DWORD value
'
Case REG_DWORD
oReg.GetDWORDValue hDefKey, strSubKeyPath, strValueName, uValue
wscript.echo " " & strValueName & " (REG_DWORD) = " & CStr(uValue)

' Show a REG_MULTI_SZ value
'
Case REG_MULTI_SZ
oReg.GetMultiStringValue hDefKey, strSubKeyPath, strValueName, arrValues
wscript.echo " " & strValueName & " (REG_MULTI_SZ) ="
For Each strValue in arrValues
wscript.echo " " & strValue
Next

End Select
Next

Next


But what if we only need to know if a registry key exists? We could just do the following:

' Constants (taken from WinReg.h)
'
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003

' Chose computer name, registry tree and key path
'
strComputer = "." ' Use . for current machine
hDefKey = HKEY_LOCAL_MACHINE
strKeyPath = "SOFTWARE\Microsoft\Cryptography\Defaults\Provider"

' Connect to registry provider on target machine with current user
'
Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")

' Try to enum the subkeys of the key path we've chosen. We can't if the key doesn't exist
'
If oReg.EnumKey(hDefKey, strKeyPath, arrSubKeys) = 0 Then
wscript.echo "Key exists!"
Else
wscript.echo "Key does not exists!"
End If



I hope this helps.


Cheers,



Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/04/09/win32_process-create-fails-if-user-profile-is-not-loaded/
Post name: Win32_Process.Create fails if user profile is not loaded
Original author: Alejandro Campos Magencio
Posting date: 2008-04-09T04:03:00+00:00


Hi all, welcome back,

The other day I worked on an issue which happened whenusing WMI methodWin32_Process.Create to spawn a process from an ASP.NET application. This methodwas returning an UNKNOWN FAILURE (8) and the new process wasn't created. The application was running as Network Service and WMI was impersonating a special user when launching the new process.

We could also reproduce the issue with a VBScript that we launched as the special user with runas command and its /noprofile option.When we used /profile option instead, the script worked fine, and so did the ASP.NET app! So it seems that this WMImethod is dependant of the user profile being loaded.

After some tests and some debugging, I confirmed this:

Win32_Process.Create always tries to access the registry of the user which will launch the process (the one we are impersonating). This behavior is by design and can't be modified.

If the user profile is loaded, there is no problem as Create can access the registry keys it needs. But if the profile is not loaded, it will try to load the registry hive of the user (NTUSER.DAT file) into the registry. For doing that it uses RegLoadKey API, which in our case is failing with ERROR_PRIVILEGE_NOT_HELD error. This internal error will cause the misleading ERROR_NOT_ENOUGH_MEMORY that we get from Create method.
I verified the privileges this API requires in its documentation: "The calling process must have the SE_RESTORE_NAME and SE_BACKUP_NAME privileges on the computer in which the registry resides."

Summing up, Win32_Process.Create will work if the user we are impersonating has SE_RESTORE_NAME (a.k.a. SeRestorePrivilege a.k.a. "Restore files and directories") and SE_BACKUP_NAME (a.k.a. SeBackupPrivilege a.k.a."Backup files and directories") privileges in the local machine where the WMI code is running. This way it can load the registry hive if the profile for that user is not loaded.

An alternate solution may be to load the profile before calling Create. IIS won’t load it automatically, and WMI neither. We may load it by calling LoadUserProfile API, for instance. But the user calling this API needs high privileges. So if we know the user we are impersonating, we may have an easier way to load the profile: Service Control Manager (SCM) automatically loads the user profile of the user running a Windows service. We may create a dummy service which does nothing but always stay alive and which runs as our special user, so its profile gets loaded. Once the profile of the user gets loaded by SCM, every other app which requires it will have access to it, like a web app or a web service.

I hope this helps.

Cheers,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/04/04/how-to-encrypt-and-decrypt-with-cryptoapi-and-a-user-certificate-windows-mobile/
Post name: How to Encrypt and Decrypt with CryptoAPI and a user Certificate (Windows Mobile)
Original author: Alejandro Campos Magencio
Posting date: 2008-04-04T04:00:00+00:00


Hi all, welcome back,

The other day I had the pleasure to collaborate with my colleague Raffaele Limosanion a CryptoAPI case in Windows Mobile. I don't give technical support to Windows Mobile, and I don't know the specifics on mobile development. He does ?? So we managed to put together a sample which shows how to encrypt and decrypt data with acertificate in a PFX file and works on that platform: Encrypting\Decrypting Data through PFX certificate on Windows Mobile.

CryptoAPI is limitedon Windows Mobile. Some methods are not supported, and we may find differences in the supported methods: number of parameters, flags... We could use the ideas shown in this sample if we want to do the same stuff on Windows NT (XP, Vista, Server 2008, etc.). Just pay attention to the differences I mentioned.MSDN will be our friend here, as always.

The sample shows the usage of the following CryptoAPI methods on encryption:

- CertOpenStore.

- CertFindCertificateInStore

- CryptAcquireContext

- CryptImportPublicKeyInfoEx

- CryptEncrypt

- CertFreeCertificateContext

- CertCloseStore

And on decryption:

- CertOpenStore

- CertFindCertificateInStore

- CryptAcquireCertificatePrivateKey

- CryptGetUserKey

- CryptDecrypt

- CertFreeCertificateContext

- CertCloseStore

I hope this helps.

Cheers,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2008/03/25/how-to-get-ads-providers-list-c/
Post name: How to get ADs Providers list (C#)
Original author: Alejandro Campos Magencio
Posting date: 2008-03-25T12:30:00+00:00


Hi, welcome back,


We may want to get the list of Active Directory Providers ("LDAP:", "WinNT:", "IIS:"...) with .NET the same way we do it with this VBScript:

Set ads = GetObject("ADs:")
For Each provider In ads
Wscript.Echo provider.Name
Next

The information we need is here in the registry: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ADs\Providers. So the following sample code will get the info we need:

using Microsoft.Win32;

...

// Get the HKLM registry key
RegistryKey RegKey = Registry.LocalMachine;

// Open the sub-key which contains all the providers
RegistryKey ProviderKey = RegKey.OpenSubKey(@"Software\Microsoft\ADs\Providers");

// Get the list of the sub-keys
string[] SubKeys = ProviderKey.GetSubKeyNames();

// Create the string array which will hold the provider list
string[] ListOfProviders = new string[SubKeys.Length];

// Now add all providers to the array
for (int Count = 0; Count < SubKeys.Length; Count++)
{
ListOfProviders[Count] = SubKeys[Count] + ":";
}

// Show the list of providers
foreach (string providerName in ListOfProviders)
{
MessageBox.Show(providerName);
}
...


I hope this helps.


Cheers,



Alex (Alejandro Campos Magencio)