CertSetCertificateContextProperty returning true but no changes in the certificate - com-interop

so I have an issue now with Windows Certificates. I have a list of certificates (identified by SerialNumber/Thumbprint) for which I have to set EnhancedKeyUsage
So what I would like to happen is for a certificate:
From code to "Enable only for following purposes" and only "Client Authentication" to be selected, basically I would like to set a specific certificate as client auth certificate.
So the end-result should be :
Now from what I've read only ( information is very limited) I found that I should use CertSetCertificateContextProperty from Crypt32.dll.
My code looks like this:
public async Task<bool> SetAuthKeyUsageExtension(string certThumbprint)
{
//open certificate store for Read/Write
using X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
//find certificate
var certificate = FindCertificate(certThumbprint, store);
if (certificate != null)
{
//set EKU for Client Auth
SetKeyExtangeUsage(certificate);
return true;
}
return false;
}
private X509Certificate2 FindCertificate(string thumbPrint, X509Store store)
{
X509Certificate2Collection foundX509Certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, false);
if (foundX509Certificates != null || foundX509Certificates.Count > 0)
{
return foundX509Certificates.FirstOrDefault();
}
return null;
}
My native class looks like this:
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern Boolean CertSetCertificateContextProperty(
[In] IntPtr pCertContext,
[In] UInt32 dwPropId,
[In] UInt32 dwFlags,
[In] IntPtr pvData);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPT_OBJID_BLOB
{
public uint cbData;
public IntPtr pbData;
}
private const string OID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2";
private const int CERT_ENHKEY_USAGE_PROP_ID = 9;
public static bool SetKeyExtangeUsage(X509Certificate2 cert)
{
//Create a new Oid collection
OidCollection oids = new OidCollection();
//add to collection
oids.Add(new Oid
{
Value = OID_PKIX_KP_CLIENT_AUTH
});
X509EnhancedKeyUsageExtension eku = new X509EnhancedKeyUsageExtension(oids, true);
//pbData
var pbData = Marshal.AllocHGlobal(eku.RawData.Length);
CRYPT_OBJID_BLOB objID = new CRYPT_OBJID_BLOB();
IntPtr pvData = Marshal.AllocHGlobal(Marshal.SizeOf(objID));
objID.pbData = pbData;
objID.cbData = (uint)eku.RawData.Length;
Marshal.StructureToPtr(objID, pvData, false);
// var result = CertSetCertificateContextProperty(cert.Handle, CERT_ENHKEY_USAGE_PROP_ID, 0, objID.pbData);
var result = CertSetCertificateContextProperty(cert.Handle, CERT_ENHKEY_USAGE_PROP_ID, 0, pvData);
Marshal.FreeHGlobal(objID.pbData);
Marshal.FreeHGlobal(pvData);
return true;
}
This code "works" in terms that it does not break or throw any errors, but when I check the certificate using the UI, no Extended Key Usage are changed, basically it looks like I did nothing. I am sure I am missing something, but I have very few experience with X509Certificate2 and also Interop so I am guessing that somewhere in SetKeyExtangeUsage I am missing something. 5
I used this How to set certificate purposes? as reference, but there is no working code there, only steps, which I think I followed.
Any ideas, what I am missing?
NEDIT: Now running the same code I get this error:
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
EDIT2: Changed pbData to pvData as mentioned.
EDIT3: Changed function
public static bool SetClientAuthEKU(X509Certificate2 cert)
{
OidCollection oids = new OidCollection();
oids.Add(new Oid
{
Value = OID_PKIX_KP_CLIENT_AUTH
});
X509EnhancedKeyUsageExtension eku = new X509EnhancedKeyUsageExtension(oids, true);
CRYPT_OBJID_BLOB objID = new CRYPT_OBJID_BLOB();
//allocate space in memory
IntPtr pbData = Marshal.AllocHGlobal(eku.RawData.Length);
IntPtr pvData = Marshal.AllocHGlobal(Marshal.SizeOf(objID));
//copy eku raw data into pbData
Marshal.Copy(eku.RawData, 0, pbData, eku.RawData.Length);
//set CRYPT_OBJECT value with ekuRaw data and Length
objID.pbData = pbData;
objID.cbData = (uint)eku.RawData.Length;
//copy CRYPT OBJECT into IntPtr
Marshal.StructureToPtr(objID, pvData, false);
var result = CertSetCertificateContextProperty(cert.Handle, CERT_ENHKEY_USAGE_PROP_ID, 0, pvData);
Marshal.FreeHGlobal(objID.pbData);
Marshal.FreeHGlobal(pvData);
return true;
}
EDIT4:

The problem is that you pass actual raw data (without length indicator) to CertSetCertificateContextProperty function, while it must be a pointer to CRYPTOAPI_BLOB. That is, last parameter of CertSetCertificateContextProperty function must be pvData.
The mistake can be easily detected by finding usages of pvData in your code. You write structure into pvData pointer, but the pointer is not used anywhere in the code.
Update:
I just noticed that you don't put anything in pbData. You allocate the buffer, but don't write anything there. You have to copy extension's raw data into pbData buffer.

Related

Xamrin UWP Release causes weird NullReferenceException (System.Security.Cryptography.PasswordDeriveBytes)

public static class CryptoHelper {
// This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be
// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
private const string initVector = "pemgail9uzpgzl88";
// This constant is used to determine the keysize of the encryption algorithm
private static int keysize = 256;
private static int getKeySize()
{
return 256;
}
//Encrypt
//public static byte[] EncryptString( string plainText, string passPhrase ) {
public static byte[] EncryptString(string toEncrypt, string salt)
{
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(toEncrypt);
byte[] keyBytes = new byte[126];
try
{
PasswordDeriveBytes password = new PasswordDeriveBytes(Encoding.UTF8.GetBytes(salt), null);
Debug.WriteLine(CryptoHelper.getKeySize());
Debug.WriteLine(password.ToString());
keyBytes = password.GetBytes(256 / 8);
} catch (Exception e)
{
Debug.WriteLine(e.StackTrace);
}
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
return cipherTextBytes;
}
........
The call to "password.GetBytes(256 / 8);" results in a non catchable NullReferenceException
This happens only when the UWP App is in Release mode; UWP Debug as well as Andorid and IOS are fine.
Also I get a weird Debug Message:
"this._hash" war "null".
or
"this._hash" was "null". (translated)
Here you can see it in action
VS2019 Screenshot
To repuduce this issue the inputs for the function are:
toEncrypt "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiZXhwIjoxNjE3MDAyMTEyfQ.C0CaGgfibM4z55MoANI2CiohUyew09r3_D_TpcQ6n1c8LmQd8BusSyF1SMEIQ3cO5uxE9Tnau0ZAT6D3kN3NcQ"
salt
"9x83m74tzrx9387x4mz98374zt90x8m273z948734z59"
Cause I cant see the detailed cause of this problem there it is basilcy not possible to get a workaround for this.
I was trying to make the same code work. The solution I found was to replace:
PasswordDeriveBytes password = new PasswordDeriveBytes(Encoding.UTF8.GetBytes(salt), null);
with:
Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, Encoding.UTF8.GetBytes("12345678"));
and also add this:
symmetricKey.Padding = PaddingMode.Zeros;

CrmServiceClient cannot be instantiated

We have a situation where the CrmServiceClient class cannot be instantiated, with an 'Object reference not set to an object' error coming from deep within the bowels of the constructor. I've also received a Collection was modified; enumeration operation may not execute error in a few situations.
This does not happen all the time, but we seem to be able to reproduce it when we trigger multiple requests very quickly.
We create the object as follows:
var ctx = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Xrm"].ConnectionString);
The connection string is valid and we have set RequireNewInstance to true
We were originally using the ctx in a using block but we were advised that we shouldn't be disposing of the CrmServiceClient, so we've removed the using block, but this has not resolved the problem.
The stack trace is below - i've only pasted the relevant part. The stack leading up to this point can be any piece of code that attempts to connect to the CRM to retrieve data
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at System.Collections.Generic.List`1.Enumerator.MoveNext()
at Microsoft.Xrm.Tooling.Connector.Utilities.GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, String& onlineRegion, String& organizationName, Boolean& isOnPrem)
at Microsoft.Xrm.Tooling.Connector.CrmConnection.SetOrgnameAndOnlineRegion(Uri serviceUri)
at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(String serviceUri, String userName, String password, String domain, String homeRealmUri, String authType, String requireNewInstance, String clientId, String redirectUri, String tokenCacheStorePath, String loginPrompt, String certStoreName, String certThumbprint, String skipDiscovery)
at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(IDictionary`2 connection)
at Microsoft.Xrm.Tooling.Connector.CrmConnection.Parse(String connectionString)
at Microsoft.Xrm.Tooling.Connector.CrmServiceClient.ConnectToCrmWebService(String crmConnectionString)
at Microsoft.Xrm.Tooling.Connector.CrmServiceClient..ctor(String crmConnectionString)
I believe I've tracked down the issue. I used DotNetPeek to look at the underlying code that was failing. The static method GetOrgnameAndOnlineRegionFromServiceUriwas where the error was occurring.
I tracked it down to a static list (discoSvcs) that was being set to null before the method returns. Other threads that call this method are also trying to do things with this list. It ends up that there is a race condition where one thread could check to see if it isn't null.
The only way I can get around it now is making sure only one CrmServiceClient is being instantiated at any time, by using a lock. This isn't ideal but I am running out of time
Static list definition
namespace Microsoft.Xrm.Tooling.Connector
{
public class Utilities
{
private static CrmOnlineDiscoveryServers discoSvcs;
private static List<string> _autoRetryRetrieveEntityList;
private Utilities()
{
}
Problem Function
The static list variable is checked at the beginning of this function and if it is null, it is populated with some values. It is then used later in the method before being set to null in the finally block.
public static void GetOrgnameAndOnlineRegionFromServiceUri(
Uri serviceUri,
out string onlineRegion,
out string organizationName,
out bool isOnPrem)
{
isOnPrem = false;
onlineRegion = string.Empty;
organizationName = string.Empty;
if (serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS.COM") || serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.DE") || (serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.US") || serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS-INT.COM")))
{
if (Utilities.discoSvcs == null)
Utilities.discoSvcs = new CrmOnlineDiscoveryServers();
try
{
List<string> stringList = new List<string>((IEnumerable<string>) serviceUri.Host.Split(new string[1]
{
"."
}, StringSplitOptions.RemoveEmptyEntries));
organizationName = stringList[0];
stringList.RemoveAt(0);
StringBuilder stringBuilder = new StringBuilder();
foreach (string str in stringList)
{
if (!str.Equals("api"))
stringBuilder.AppendFormat("{0}.", (object) str);
}
string crmKey = stringBuilder.ToString().TrimEnd('.').TrimEnd('/');
stringBuilder.Clear();
if (!string.IsNullOrEmpty(crmKey))
{
CrmOnlineDiscoveryServer onlineDiscoveryServer = Utilities.discoSvcs.OSDPServers.Where<CrmOnlineDiscoveryServer>((Func<CrmOnlineDiscoveryServer, bool>) (w =>
{
if (w.DiscoveryServer != (Uri) null)
return w.DiscoveryServer.Host.Contains(crmKey);
return false;
})).FirstOrDefault<CrmOnlineDiscoveryServer>();
if (onlineDiscoveryServer != null && !string.IsNullOrEmpty(onlineDiscoveryServer.ShortName))
onlineRegion = onlineDiscoveryServer.ShortName;
}
isOnPrem = false;
}
finally
{
Utilities.discoSvcs.Dispose();
Utilities.discoSvcs = (CrmOnlineDiscoveryServers) null;
}
}
else
{
isOnPrem = true;
if (((IEnumerable<string>) serviceUri.Segments).Count<string>() < 2)
return;
organizationName = serviceUri.Segments[1].TrimEnd('/');
}
}

internet properties - read configuration script path

I would like to read the value in the "Use automatic configuration script" Address field.
I need to set the proxy for CefSharp like this
settings.CefCommandLineArgs.Add("proxy-pac-url","proxy address");
I have tried different variations of WebRequest.GetSystemWebProxy and calling function InternetQueryOption in the wininet.dll.
Code from the CefSharp repo on Github
public static class ProxyConfig
{
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetQueryOption(IntPtr hInternet, uint dwOption, IntPtr lpBuffer, ref int lpdwBufferLength);
private const uint InternetOptionProxy = 38;
public static InternetProxyInfo GetProxyInformation()
{
var bufferLength = 0;
InternetQueryOption(IntPtr.Zero, InternetOptionProxy, IntPtr.Zero, ref bufferLength);
var buffer = IntPtr.Zero;
try
{
buffer = Marshal.AllocHGlobal(bufferLength);
if (InternetQueryOption(IntPtr.Zero, InternetOptionProxy, buffer, ref bufferLength))
{
var ipi = (InternetProxyInfo)Marshal.PtrToStructure(buffer, typeof(InternetProxyInfo));
return ipi;
}
{
throw new Win32Exception();
}
}
finally
{
if (buffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(buffer);
}
}
}
}
This code works if there is a proxy in windows settings, easy to test with Fiddler.
I could read the value from the registry, but it feels like a hack
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", false);
var autoConfigUrl = registry?.GetValue("AutoConfigURL");
There must be a "correct" way to do this?
Current test code:
if (settingsViewModel.UseProxy)
{
// https://securelink.be/blog/windows-proxy-settings-explained/
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", false);
var autoConfigUrl = registry?.GetValue("AutoConfigURL").ToString();
var proxyEnable = registry?.GetValue("ProxyEnable").ToString();
if (!string.IsNullOrEmpty(autoConfigUrl) && !string.IsNullOrEmpty(proxyEnable) && proxyEnable == "1")
{
settings.CefCommandLineArgs.Add("proxy-pac-url", autoConfigUrl);
}
else
{
var proxy = ProxyConfig.GetProxyInformation();
switch (proxy.AccessType)
{
case InternetOpenType.Direct:
{
//Don't use a proxy server, always make direct connections.
settings.CefCommandLineArgs.Add("no-proxy-server", "1");
break;
}
case InternetOpenType.Proxy:
{
settings.CefCommandLineArgs.Add("proxy-server", proxy.ProxyAddress.Replace(' ', ';'));
break;
}
case InternetOpenType.PreConfig:
{
settings.CefCommandLineArgs.Add("proxy-auto-detect", "1");
break;
}
}
}
}

Monitor registry key with reactive extensions

I would like to track changes to a registry key, for instance addition/removal of a subkey, addition/removal/edition of a value. How could I create an IObservable sequence that exposes these changes?
One way is to p/invoke the RegNotifyChangeKeyValue, a Win32 function which notifies the caller about changes to the attributes or contents of a specified registry key. This function sets an event whenever it detects a change. Note that it it must be called on a persistent thread, otherwise it will signal whenever the thread exits (even though no change happened). See below for a possible implementation of this with Rx.Net.
using System;
using System.ComponentModel;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32;
public class RegistryMonitoringOperations
{
[Flags]
public enum RegChangeNotifyFilter
{
/// <summary>Notify the caller if a subkey is added or deleted.</summary>
Key = 1,
/// <summary>Notify the caller of changes to the attributes of the key,
/// such as the security descriptor information.</summary>
Attribute = 2,
/// <summary>Notify the caller of changes to a value of the key. This can
/// include adding or deleting a value, or changing an existing value.</summary>
Value = 4,
/// <summary>Notify the caller of changes to the security descriptor
/// of the key.</summary>
Security = 8
}
private const int KeyQueryValue = 0x0001;
private const int KeyNotify = 0x0010;
private const int StandardRightsRead = 0x00020000;
public static IObservable<Unit> CreateKeyValuesChangedObservable(
RegistryHive hive,
string subKey,
RegChangeNotifyFilter filter,
IScheduler registrationScheduler)
{
return Observable.Create<Unit>(
obs =>
{
try
{
var key = OpenKey(hive, subKey);
return new CompositeDisposable(
CreateKeyValuesChangedObservable(key, filter).SubscribeOn(registrationScheduler).Subscribe(obs),
Disposable.Create(() => RegCloseKey(key)));
}
catch (Win32Exception e)
{
obs.OnError(e);
return Disposable.Empty;
}
});
}
private static IDisposable SetCallbackWhenSignalled(WaitHandle waitObject, Action action)
{
var registeredWait = ThreadPool.RegisterWaitForSingleObject(waitObject, (s, t) => action(), null, -1, true);
return Disposable.Create(() => registeredWait.Unregister(null));
}
private static IObservable<Unit> CreateKeyValuesChangedObservable(IntPtr key, RegChangeNotifyFilter filter)
{
return Observable.Create<Unit>(
obs =>
{
var eventNotify = new AutoResetEvent(false);
var result = RegNotifyChangeKeyValue(key, true, filter, eventNotify.SafeWaitHandle.DangerousGetHandle(), true);
if (result != 0)
{
obs.OnError(new Win32Exception(Marshal.GetLastWin32Error()));
}
return new CompositeDisposable(
eventNotify,
SetCallbackWhenSignalled(
eventNotify,
() =>
{
obs.OnNext(Unit.Default);
obs.OnCompleted();
}));
}).Repeat();
}
private static IntPtr OpenKey(RegistryHive hive, string subKey)
{
IntPtr registryKey;
var result = RegOpenKeyEx((int)hive, subKey, 0, StandardRightsRead | KeyQueryValue | KeyNotify, out registryKey);
if (result != 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return registryKey;
}
Here's a typical usage of this function:
RegistryMonitoringOperations.CreateKeyValuesChangedObservable(
RegistryHive.LocalMachine,
"somepath",
RegistryMonitoringOperations.RegChangeNotifyFilter.Value,
DispatcherScheduler.Instance)
As you can see above, one way to avoid dedicating a thread for calling this function is to use the UI thread which is persistent (so in rx terms, using the dispatcher scheduler). RegNotifyChangeKeyValue returns immediatly when in asynchronous mode so it won't block the UI.

How to get output value (customOut) from EnvDTE Commands.Raise method?

I am writing a Visual Studio 2012 add-in that needs to send a command to the built-in T-SQL Editor. The command in question, sqlEditorSqlDatabaseCommand, can be used to either set or get the name of the current database in the editor. Here is some (working) code that sets the database name:
const string guidSqlEditorCommandSet = "b371c497-6d81-4b13-9db8-8e3e6abad0c3";
const int sqlEditorSqlDatabaseCommand = 0x312;
object customIn = "myDatabaseName";
object customOut = null;
m_applicationObject.Commands.Raise(guidSqlEditorCommandSet, sqlEditorSqlDatabaseCommand, ref customIn, ref customOut);
The problem is, I need to get the current database name, which would require use of the customOut parameter, and I can't figure out how to make customOut work.
The implementation of sqlEditorSqlDatabaseCommand is as follows (via Reflector):
protected override int HandleExec(uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
AuxiliaryDocData auxiliaryDocDataForEditor = base.GetAuxiliaryDocDataForEditor();
if (auxiliaryDocDataForEditor != null)
{
QueryExecutor queryExecutor = auxiliaryDocDataForEditor.QueryExecutor;
if (queryExecutor != null)
{
if (pvaIn != IntPtr.Zero)
{
string objectForNativeVariant = (string) Marshal.GetObjectForNativeVariant(pvaIn);
this.SetDatabase(auxiliaryDocDataForEditor, objectForNativeVariant);
}
else if (pvaOut != IntPtr.Zero)
{
object database = string.Empty;
IDbConnection connection = queryExecutor.ConnectionStrategy.Connection;
if (((connection != null) && (connection.State == ConnectionState.Open)) && !string.IsNullOrEmpty(connection.Database))
{
database = connection.Database;
}
else
{
database = string.Empty;
}
Marshal.GetNativeVariantForObject(database, pvaOut);
}
}
}
return 0;
}
According to every marshaling example I've seen, I should be able to pass null for both customIn and customOut, and the database name should be placed in customOut. When I do this, I get an E_INVALIDARG exception from Commands.Raise:
System.ArgumentException: The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))
at EnvDTE.Commands.Raise(String Guid, Int32 ID, Object& CustomIn, Object& CustomOut)
at RDSVisualStudioAddIn.Exec(String commandName, vsCommandExecOption executeOption, Object& varIn, Object& varOut, Boolean& handled) in c:\RDS\RDSVisualStudioAddIn\Connect.cs:line 193
Has anyone used Commands.Raise with the customOut parameter?

Resources