EncryptedXml DecryptDocument method error after .Net framework update - c#

I have an old function written in 2013 that decrypt xml that was encrypted by another program.
The code is realy simple
public static void Decrypt(XmlDocument Doc)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml(Doc);
// Decrypt the XML document.
exml.DecryptDocument();
}
It worked like a charm until recently that some of our clients started to upgrade their framework to 4.6.2, so the method DecryptDocument() stopped working. Now it throws an exception "The algorithm group '' is invalid". If I remove .net framework 4.6.2 it works again.
The sample code in this link will reproduce the error, it will encrypt successfully then fail to decrypt.
I'm using A3 certificates, pendrive token. Anyone have faced this problem? there is any work around in .net 4.6.2?
Edit 1:
Stacktrace:
at System.Security.Cryptography.CngAlgorithmGroup..ctor(String algorithmGroup)
at System.Security.Cryptography.CngKey.get_AlgorithmGroup()
at System.Security.Cryptography.RSACng..ctor(CngKey key)
at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)
at System.Security.Cryptography.CngLightup.GetRSAPrivateKey(X509Certificate2 cert)
at System.Security.Cryptography.Xml.EncryptedXml.DecryptEncryptedKey(EncryptedKey encryptedKey)
at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
at Criptografar.Program.Decrypt(XmlDocument Doc) in C:\Users\leoka\Documents\Visual Studio 2017\Projects\ConsoleApp4\Criptografar\Program.cs:line 152
at Criptografar.Program.Main(String[] args) in C:\Users\leoka\Documents\Visual Studio 2017\Projects\ConsoleApp4\Criptografar\Program.cs:line 83

I cannot reproduce the problem myself - I don't have the "pendrive token" which I suspect is the problem - so this is guesswork.
There are two generations of cryptographic APIs in Windows - the "old" one and the "new generation" one, known as CNG.
Now, if you look at the source code for the CngLightup type that appears midway through your stack trace, specifically the DetectRsaCngSupport method, you'll see that .NET framework tries to use the new generation API if possible. My guess is that the "pendrive token" device does not support the new API. You can verify this by forcing the use of the old API. Unfortunately, there does not seem to be a public configuration flag that controls this, so you must resort to reflection-based hacks. For example, you can put something like this at the beginning of your program, so that it runs once, before you try the decrypting operation:
var cngLightupType = typeof(EncryptedXml).Assembly.GetType("System.Security.Cryptography.CngLightup");
var preferRsaCngField = cngLightupType.GetField("s_preferRsaCng", BindingFlags.Static | BindingFlags.NonPublic);
var getRsaPublicKeyField = cngLightupType.GetField("s_getRsaPublicKey", BindingFlags.Static | BindingFlags.NonPublic);
var getRsaPrivateKeyField = cngLightupType.GetField("s_getRsaPrivateKey", BindingFlags.Static | BindingFlags.NonPublic);
preferRsaCngField.SetValue(null, new Lazy<bool>(() => false));
getRsaPublicKeyField.SetValue(null, null);
getRsaPrivateKeyField.SetValue(null, null);
Do note that it is extremely hacky, not thread-safe, error handling is omitted etc. If you verify that the CNG usage is the problem, you can then ask the "pendrive token" supplier to provide drivers that work with CNG. Or you can live with the hack above, rewritten for more safety.

There are some runtime changes in .Net 4.6.2 that affect EncrtyptedXml - see https://msdn.microsoft.com/en-us/library/mt670901(v=vs.110).aspx#Anchor_5

I ran into something very similar today that turned out to be a bug in .NET 4.6.2:
https://github.com/Microsoft/dotnet/issues/341
According to this issue, there are two workarounds:
1) Upgrading the OS to Windows Server 2012R2 or newer, 2) loading the
user profile.

Related

Unable to export RSA private parameters when running as administrator

I'm using NET Core 3.1, and need to export the private RSA parameters (D, P and Q), as they are being used as keying material for an HKDF function (HMAC-based Extract-and-Expand Key Derivation Function) in order to provide deterministic shared secrets.
The code I have is working great - but, bizarrely it throws if it's ran from an elevated admin prompt:
var flags = X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable;
var certs = new X509Certificate2Collection();
certs.Import(#"C:\MyCert.pfx", String.Empty, flags);
var cert = certs.OfType<X509Certificate2>().Where(x => x.HasPrivateKey);
using (var rsa = cert.GetRSAPrivateKey())
{
// This works - *unless* executed from an elevated admin prompt!?
var rsaParms = rsa.ExportParameters(true);
// use the params here...
}
Stack trace:
Unhandled exception. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The requested operation is not supported.
at System.Security.Cryptography.CngKey.Export(CngKeyBlobFormat format)
at System.Security.Cryptography.RSACng.ExportKeyBlob(Boolean includePrivateParameters)
at System.Security.Cryptography.RSACng.ExportParameters(Boolean includePrivateParameters)
If it makes any difference, the certificate in question is self-signed, generated using C#'s System.Security.Cryptography.X509Certificates.CertificateRequest.CreateSelfSigned.
Any idea why this would throw only when executed elevated, or/and how to get it to not throw?
UPDATE
I've done some more digging, and it works as expected if I use a self-signed cert generated using OpenSSL instead of .NET - all the X509 extensions/settings are the same.
I did some debugging, and see that there is a difference when I inspect rsa.Key.
Normal execution, .NET generated certificate:
rsa.Key.ExportPolicy is AllowExport | AllowPlaintextExport
rsa.Key.Provider is Microsoft Enhanced Cryptographic Provider v1.0
Elevated execution, .NET generated certificate:
rsa.Key.ExportPolicy is AllowExport
rsa.Key.Provider is Microsoft Software Key Storage Provider
So, it's using the deprecated CAPI provider when not running elevated, and the "modern" CNG provider when elevated. And we can see that AllowPlaintextExport is missing with the CNG provider, which I assume is the problem.
When using an OpenSSL-generated certificate the provider is always the deprecated CAPI provider, whether elevated or not.
Yet more digging led to this answer, which involves using interop to get the AllowPlaintextExport flag added when using the CNG. Now, this works from an admin prompt when using a .NET-generated certificate... but the call to CryptAcquireCertificatePrivateKey returns false when using an OpenSSL-generated certificate (means "didn't get the private key"), regardless of privileges!
I found another, much simpler answer here, which involves importing the key into an RSACng, and then toggling on the AllowPlaintextExport. However, as expected the call to rsa.ExportParameters(true) still fails with The requested operation is not supported, so I'm unable to import into the RSACng
It really shouldn't be this difficult :(
I can't explain the reason the original code didn't work when running elevated, but I have come up with 2 workarounds, which presumably are ensuring the AllowPlaintextExport policy flag is present.
Workaround 1 - Load PEM Key
In the original code, I load a certificate from a PKCS#12 (.p12/.pfx) file, which contains both the public and private parts. If instead I load a PEM key, it works as expected:
// We need to strip the labels from the beginning and end of the key - working
// with PEM files is much easier in .NET 5, as it handles all this cruft for us
var regex = new Regex(#"^[-]+BEGIN.+[-]+\s(?<base64>[^-]+)[-]+", RegexOptions.Compiled | RegexOptions.ECMAScript | RegexOptions.Multiline);
var keyText = File.ReadAllText(#"C:\app\cert.key");
var keyBase64 = regex.Match(keyText).Groups["base64"].Value;
var keyBytes = keyBase64.FromBase64String();
using var rsaKey = RSA.Create();
rsaKey.ImportRSAPrivateKey(keyBytes, out _);
var rsaParams = rsaKey.ExportParameters(true);
Workaround 2 - Export/Import Key
In this workaround, we export the key to a blob, then import it back again. When I first tried this, it didn't work - for reasons unknown, you have to export it in encrypted form; attempting to export without encryption throws!
Code is based on this internal .NET utility.
public static RSA GetExportableRSAPrivateKey(this X509Certificate2 cert)
{
const CngExportPolicies exportability = CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport;
var rsa = cert.GetRSAPrivateKey();
// Thankfully we don't have to deal with all this shit on Linux
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return rsa;
// We always expect an RSACng on Windows these days, but that could change
if (!(rsa is RSACng rsaCng))
return rsa;
// Is the AllowPlaintextExport policy flag already set?
if ((rsaCng.Key.ExportPolicy & exportability) != CngExportPolicies.AllowExport)
return rsa;
try
{
// Export the original RSA private key to an encrypted blob - note you will get "The requested operation
// is not supported" if trying to export without encryption, so we export with encryption!
var exported = rsa.ExportEncryptedPkcs8PrivateKey(nameof(GetExportableRSAPrivateKey),
new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 2048));
// Load the exported blob into a fresh RSA object, which will have the AllowPlaintextExport policy without
// having to do anything else
RSA copy = RSA.Create();
copy.ImportEncryptedPkcs8PrivateKey(nameof(GetExportableRSAPrivateKey), exported, out _);
return copy;
}
finally
{
rsa.Dispose();
}
}

What's wrong with my CSR inf generation? Giving a Keyspec error

I'm writing a C# program that will create inf files and then run a certreq -new to generate the CSR and output the contents - On older machines ( Windows server 2008, for instance ) I'm getting a generic error
"[NewRequest] KeyAlgorithm = "ECDSA_P256" <=> KeySpec?
Editing the CSR file manually and removing the KeySpec altogether seems to make the CSR go through, but every guide I see seems to mention the KeySpec, so I'm confused whether I'm doing something wrong or what.
string[] csrPairs = {"[NewRequest]", subjectLine, "Exportable = true", "ExportableEncrypted = true", "HashAlgorithm = sha256",
"KeyAlgorithm = " + keyAlgorithm, "KeyLength = " + keysize, "KeySpec = 1", "MachineKeySet = true", "ProviderName = \"Microsoft Software Key Storage Provider\"","RequestType = PKCS10", "SMIME = false", "UseExistingKeySet = false"};
Here's the code I'm using to generate the CSR, seems to work on Windows 10, but not Windows 2008 Server.
Any help would be greatly appreciated - Is there something about Windows Serer 2008 I'm not thinking of? Or something my new computer would have set for .net that the other doesn't? (Target version 3.5)
Side note - Any help understanding keyspec would be great, too. Seems "1" means it can sign and encrypt, "2" can only sign - Not sure the default or what 0 does, or if I'm supposed to do anything else here.
Hmm think I figured some things out.
CNG ( Cryptography API: Next Generation ) providers don't use KeySpec, it's just set to 0 - it seems this setting might mess with some things though, such as creating Code Signing CSR's.
Regular CSP/Legacy providers seem to be able to determine the key you want in some cases, so setting the KeySpec but no the key algorithm works fine - at least from my testing.

SpeechSynthesizer doesn't get all installed voices 3

I have added many voices using "Add language" under region and language. These appear under Text-to-speech in Speech. (I am using Windows 10)
I want to use these in my app with the SpeechSynthesizer class in System.Speech.Synthesis.
When listing the available voices in my application only a handful of those actually available are shown:
static void Main()
{
SpeechSynthesizer speech = new SpeechSynthesizer();
ReadOnlyCollection<InstalledVoice> voices = speech.GetInstalledVoices();
if (File.Exists("available_voices.txt"))
{
File.WriteAllText("available_voices.txt", string.Empty);
}
using (StreamWriter sw = File.AppendText("available_voices.txt"))
{
foreach (InstalledVoice voice in voices)
{
sw.WriteLine(voice.VoiceInfo.Name);
}
}
}
Looking in available_voices.txt only these voices are listed:
Microsoft David Desktop
Microsoft Hazel Desktop
Microsoft Zira Desktop
Microsoft Irina Desktop
But looking under Text-to-speech in the setttings there are many more, like Microsoft George and Microsoft Mark.
The accepted answer here:
SpeechSynthesizer doesn't get all installed voices
suggest changing the platform to x86. I tried this but i am not seeing any change.
This answer:
SpeechSynthesizer doesn't get all installed voices 2
suggest using .NET v4.5 because of a bug in System.Speech.Synthesis. I targeted .NET Framework 4.5 but i can still only retrieve 4 voices.
None of the answers in the questions i linked helped me solve my problem, so i am asking again. Any help is appretiated.
3 years passed after the original question was asked and API seems to contains the same issue, so here is a more "deep dive" answer.
TL;DR; Code example - at the bottom
The issue with the voice list is a weird design of the Microsoft Speech API - there are two sets of voices in Windows registered at different locations in registry - one is at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices, another one - at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices.
The problem is that SpeechSynthesizer's (or more specifically - VoiceSynthesis's) initialization routine is nailed to the first one, meanwhile we usually need a combination of both.
So, there are actually two ways to overcome the behavior.
Option 1 (the one mentioned throughout other answers): manipulate the registry to physically copy the voice definition records from Speech_OneCore registry which make them visible to SpeechSynthesizer. Here you have plenty of options: manual registry manipulation, PowerShell script, code-based etc.
Option 2 (the one I used in my project): use reflection to put additional voices into the internal VoiceSyntesis's _installedVoices field, effectively simulating what Microsoft did in their code.
Good news are that the Speech API source code is open now, so we don't have to fumble in darkness trying to understand what we need to do.
Here's the original code snippet:
using (ObjectTokenCategory category = ObjectTokenCategory.Create(SAPICategories.Voices))
{
if (category != null)
{
// Build a list with all the voicesInfo
foreach (ObjectToken voiceToken in category.FindMatchingTokens(null, null))
{
if (voiceToken != null && voiceToken.Attributes != null)
{
voices.Add(new InstalledVoice(voiceSynthesizer, new VoiceInfo(voiceToken)));
}
}
}
}
We just need to replace SAPICategories.Voices constant with another registry entry path and repeat the whole recipe.
Bad news are that all the needed classes, methods and fields used here are internal so we'll have to extensively use reflection to instantiate classes, call methods and get/set fields.
Please find below the example of my implementation - you call the InjectOneCoreVoices extension method on the synthesizer and it does the job. Note, that it throws exception if something goes wrong so don't forget proper try/catch surroundings.
public static class SpeechApiReflectionHelper
{
private const string PROP_VOICE_SYNTHESIZER = "VoiceSynthesizer";
private const string FIELD_INSTALLED_VOICES = "_installedVoices";
private const string ONE_CORE_VOICES_REGISTRY = #"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices";
private static readonly Type ObjectTokenCategoryType = typeof(SpeechSynthesizer).Assembly
.GetType("System.Speech.Internal.ObjectTokens.ObjectTokenCategory")!;
private static readonly Type VoiceInfoType = typeof(SpeechSynthesizer).Assembly
.GetType("System.Speech.Synthesis.VoiceInfo")!;
private static readonly Type InstalledVoiceType = typeof(SpeechSynthesizer).Assembly
.GetType("System.Speech.Synthesis.InstalledVoice")!;
public static void InjectOneCoreVoices(this SpeechSynthesizer synthesizer)
{
var voiceSynthesizer = GetProperty(synthesizer, PROP_VOICE_SYNTHESIZER);
if (voiceSynthesizer == null) throw new NotSupportedException($"Property not found: {PROP_VOICE_SYNTHESIZER}");
var installedVoices = GetField(voiceSynthesizer, FIELD_INSTALLED_VOICES) as IList;
if (installedVoices == null)
throw new NotSupportedException($"Field not found or null: {FIELD_INSTALLED_VOICES}");
if (ObjectTokenCategoryType
.GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic)?
.Invoke(null, new object?[] {ONE_CORE_VOICES_REGISTRY}) is not IDisposable otc)
throw new NotSupportedException($"Failed to call Create on {ObjectTokenCategoryType} instance");
using (otc)
{
if (ObjectTokenCategoryType
.GetMethod("FindMatchingTokens", BindingFlags.Instance | BindingFlags.NonPublic)?
.Invoke(otc, new object?[] {null, null}) is not IList tokens)
throw new NotSupportedException($"Failed to list matching tokens");
foreach (var token in tokens)
{
if (token == null || GetProperty(token, "Attributes") == null) continue;
var voiceInfo =
typeof(SpeechSynthesizer).Assembly
.CreateInstance(VoiceInfoType.FullName!, true,
BindingFlags.Instance | BindingFlags.NonPublic, null,
new object[] {token}, null, null);
if (voiceInfo == null)
throw new NotSupportedException($"Failed to instantiate {VoiceInfoType}");
var installedVoice =
typeof(SpeechSynthesizer).Assembly
.CreateInstance(InstalledVoiceType.FullName!, true,
BindingFlags.Instance | BindingFlags.NonPublic, null,
new object[] {voiceSynthesizer, voiceInfo}, null, null);
if (installedVoice == null)
throw new NotSupportedException($"Failed to instantiate {InstalledVoiceType}");
installedVoices.Add(installedVoice);
}
}
}
private static object? GetProperty(object target, string propName)
{
return target.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(target);
}
private static object? GetField(object target, string propName)
{
return target.GetType().GetField(propName, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(target);
}
}
After trying about all published solutions, I solved it by editing the registry:
copying Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Speech_OneCore\Voices\Tokens\MSTTS_V110_heIL_Asaf
(where MSTTS_V110_heIL_Asaf is the registry folder of the voice I want to use in .NET, but don't appear in GetInstalledVoices())
to a registry address that looks the same but instead of Speech_OneCore it is just Speech.
technically, to copy the registry folder, i exported the original folder, then edited the .reg file to change Speech OneCore to Speech, and then applied that new .reg file.
I solved it by installing voices from another source and getting Microsoft Speech Platform - Runtime (Version 11)
The available voices can be found on microsofts website (click on the red download button and the voices should be listed)
Sorry if my answer comes so late after the subject was posted but I developed a small tool which allows to patch the installed voices to make them available for the .NET text-to-speech engine.
The tool copies selected items from the "HKLM\SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens" key to "HKLM\SOFTWARE\Microsoft\Speech\Voices\Tokens".
If you're interested : TTSVoicePatcher (it's freeware, FR/EN)
Due to the manipulation of keys in HKLM, the tool requires administrator rights to be launched.
The Microsoft Speech Platform - Runtime Languages (Version 11) on the Microsoft website seem to contain only languages that are already installed. Not the ones that can be found under Speech_OneCore.

C# program connecting to example DBus daemon always gets 'Access is denied: DBus.BusObject'

For our current project we are using DBus (1.6.n).
It is largely accessed from C++ in shared memory mode, and this works really well.
I am now trying to access the same DBus from a C# program.
In order to try things out first, I downloaded the latest version of dbus-sharp I could find, and started the daemon included in the download to see if I could connect to it from my test C# app.
Whenever I make a connection, the daemon console shows that I am communicating with it, but as soon as I try to access any methods on the connection I get the error;
'Access is denied: DBus.BusObject'
Here is the code I have tried;
DBus.Bus dBus = null;
try
{
//input address comes from the UI and ends up as "tcp:host=localhost,port=12345";
//dBus = new Bus(InputAddress.Text + inputAddressExtension.Text);
//string s = dBus.GetId();
//dBus.Close();
//DBus.Bus bus = DBus.Bus.System;
//DBus.Bus bus = Bus.Open(InputAddress.Text + inputAddressExtension.Text);
//DBus.Bus bus = DBus.Bus.Session;
//DBus.Bus bus = DBus.Bus.Starter;
var conn = Connection.Open(InputAddress.Text + inputAddressExtension.Text);
var bus = conn.GetObject<Introspectable>(#"org.freedesktop.DBus.Introspectable", new ObjectPath("/org/freedesktop/DBus/Introspectable"));
bus.Introspect();
}
finally
{
if(dBus != null)
dBus.Close();
}
The commented code produces the same error eventually too.
I have stepped through with the debugger and it always gets to the following code in the TypeImplementer.cs;
public Type GetImplementation (Type declType)
{
Type retT;
lock (getImplLock)
if (map.TryGetValue (declType, out retT))
return retT;
string proxyName = declType.FullName + "Proxy";
Type parentType;
if (declType.IsInterface)
parentType = typeof (BusObject);
else
parentType = declType;
TypeBuilder typeB = modB.DefineType (proxyName, TypeAttributes.Class | TypeAttributes.Public, parentType);
if (declType.IsInterface)
Implement (typeB, declType);
foreach (Type iface in declType.GetInterfaces ())
Implement (typeB, iface);
retT = typeB.CreateType (); <======== Fails here ==========
lock (getImplLock)
map[declType] = retT;
return retT;
}
I have not found any useful examples or documentation about accessing DBus from C#, and there seem to be few recent entries about this anywhere, so maybe no-one else is trying this.
I am running the daemon in the same folder as the test program.
As I am running on windows, the daemon is listening on the tcp setting;
string addr = "tcp:host=localhost,port=12345";
Since this is the example included with the download, I thought it would be really simple to get it going, but alas no luck yet.
Has anyone else been here and know the next piece of the puzzle?
Any ideas would be appreciated.
Having received no comment or response, I will answer the question with the information I have found since asking it.
There appears to be no useful C# interface to DBus. (By useful, I mean one that works!)
The only information or examples I could find are not up to date and no effort appears to be being expended on providing a working interface.
I have decided to interface with DBus by using a C++ implementation written as a Windows service, and my C# program will send messages to DBus via the service. This seems to work ok, so satisfies the business need.
I am disappointed not to be able to get the C# to DBus working, but there are lots of service bus implementations that work on Windows, so in future I will look at implementing those instead of DBus.
If anyone does come up with a workable, documented solution to accessing DBus from C# on Windows, I would still be interested to see it.
I had the same error when I created new test project and add dbus cs source files to it main project assembly. It was when IBusProxy type dynamically created in dynamically created assembly.
asmB = AppDomain.CurrentDomain.DefineDynamicAssembly (new AssemblyName ("NDesk.DBus.Proxies"), canSave ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run);
modB = asmB.DefineDynamicModule ("NDesk.DBus.Proxies");
......
retT = typeB.CreateType ();
I think it was cause current running assembly isnt friendly for created assembly. And just when I add to project compiled NDesk.DBus.dll this error disappeared.

vmware .net api help vmware.vim.dll problems

Vmware's .net api reference is somewhat confusing and hard to follow. I have been able to connect to my vcenter host then get a list of esxi hosts. Then I have been able get all the running modules on the host using HostKernelModuleSystem, and probe the properties on the variable "mod"... but I am not able to figure out how to get license info, I tried creating an object lic below, trying all different kinds of "types" from vmware with the word license in the type. but, it never works it has a problem converting the line with LicenseManagerLicenseInfo lic = .... I always get the following:
"Cannot convert type 'Vmware.Vim.Viewbase' to
'Vmware.Vim.LicenseManagerLicenseInfo'"
but the declaration above it for "mod" works fine.
I have also tried:
HostLicenseConnectInfo
LicenseAssignmentManagerLicenseAssignment
LicenseManager
I am hoping someone who has worked with vmware .net api can shed some light on what i am doing wrong? I am new to C# about 1 year :) but these VMware APIs are somewhat confusing to me.
esxList = client.FindEntityViews(typeof(HostSystem), null, null, null);
foreach (HostSystem host in esxList)
{
HostKernelModuleSystem mod = (HostKernelModuleSystem)client.GetView(host.ConfigManager.KernelModuleSystem, null);
LicenseManagerLicenseInfo lic = (LicenseManagerLicenseInfo)client.GetView(host.ConfigManager.LicenseManager, null);
string name = lic.Name;
}
I'll have to go to work tomorrow to look at this ( don't have ESX and VMWare SDK for .NET at home ) but I've done a bit of this work.
I wrote a generics method that wraps FindEntityViews and takes a filter as an argument. That makes it easy to search for anything. Also I've noticed that searches come back as ManagedObjectReferences and can't be cast to the subclasses. You have to construct them passing the ManagedObjectReference as an argument.
Also I find searching for PowerCLI examples and watching the classes in the immeadiate window very help in navigating this API. It's a fairly decent SDK but they put all of the classes in a single namespace and there's lots of little style inconsistencies ( Device instead of Devices and properties that take strings instead of enums when an enum exists ).
i figured out how to do it :) , by using http://vcenter_hostname/mob I was able to walk through api better. here is what I did, plus instead of of using "host" which was type HostSystem I jused my instance of my vCenter host "client"
VMware.Vim.LicenseManager lic_manager = (VMware.Vim.LicenseManager)client.GetView(client.ServiceContent.LicenseManager, null);
LicenseManagerLicenseInfo[] lic_found = lic_manager.Licenses;
foreach (LicenseManagerLicenseInfo lic in lic_found)
{
string test = lic.Name.ToString();
string test2 = lic.LicenseKey.ToString();
}

Categories