Set the PIN without opening the smartcard popup - c#

We used to use this method before.
byte[] pinBuffer = Encoding.ASCII.GetBytes(pinCode);
IntPtr providerHandle = IntPtr.Zero;
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(
ref providerHandle,
"keyContainer",
"My HSM Removable Token Cryptographic Provider",
1,
64));
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(
providerHandle,
32,
pinBuffer,
0U));
SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty(
cert.Handle,
1,
0U,
providerHandle));
It doesn't work anymore.
I have an internal error occurred on CryptAcquireContext.
Is there another way to retrieve the certificate without opening the popup?
Same error with
IntPtr providerHandle = IntPtr.Zero;
var cspParameters = new CspParameters()
{
ParentWindowHandle = providerHandle,
KeyContainerName = "keyContainer",
ProviderName = "My HSM Removable Token Cryptographic Provider",
ProviderType = 1,
Flags = CspProviderFlags.NoPrompt,
};
var rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParameters);
It seems to be the NoPrompt (64) Flags

If you only need to get certificate from smart-card you can use this flag:
// dwFlags definitions for CryptAcquireContext
#define CRYPT_VERIFYCONTEXT 0xF0000000
The CSP will not try to get private key and as a result there were no PIN dialog.

Related

find certificate on smartcard currently on reader

I am using Visual Studio 2013 (C#) to digitally sign document using certificate from smartcard.
I cannot identify certificate currently inserted in the card reader :(
Windows copy certificates from all card inserted in the reader and keep it in the store. I want to use just card cuurently in the reader.
code I using is
public static byte[] Sign(Stream inData, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Find the certificate we'll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
if (cert.HasPrivateKey) {
csp = (RSACryptoServiceProvider)cert.PrivateKey;
if (csp.CspKeyContainerInfo.HardwareDevice)
Console.WriteLine("hardware");
Console.WriteLine(cert.ToString());
}
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
byte[] hash = sha1.ComputeHash(inData);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
But when access cert.PrivateKey user get prompted to insert card in the reader. How to detect and skip this prompt for card or detect that cert HAS corresponding card currently in the reader?
I just want to use certificate from smartcard currently in the reader.
I am afraid it is not possible to detect if the card containing specific X509Certificate2 object is present in the reader by using standard .NET APIs. The best thing (very hackish) I could come up with is this:
public static X509Certificate2 GetDefaultCertificateStoredOnTheCard()
{
// Acquire public key stored in the default container of the currently inserted card
CspParameters cspParameters = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider");
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParameters);
string pubKeyXml = rsaProvider.ToXmlString(false);
// Find the certficate in the CurrentUser\My store that matches the public key
X509Store x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
foreach (X509Certificate2 cert in x509Store.Certificates)
{
if ((cert.PublicKey.Key.ToXmlString(false) == pubKeyXml) && cert.HasPrivateKey)
return cert;
}
return null;
}
However this method is reliable only when the following conditions are met:
You card is accessible via minidriver and Microsoft Base Smart Card Crypto Provider.
There is only one reader connected to your computer with the smartcard present.
There is only one certificate present on the card currently inserted in the reader.
When there are multiple readers with smartcards connected or multiple certificates present on the card you cannot be sure which one will be returned by this method.
Please note there are also other APIs available that can access the smartcard. One example of such API is PKCS#11. It may be an overkill for simple operations but it can give you total control over your card and the objects stored on it. If you are interested and your smartcard comes with the PKCS#11 library you can take a look at my project Pkcs11Interop that brings full power of PKCS#11 API to the .NET environment.
Hope this helps :)
Edited to remove the "single certificate" restriction:
I have slightly modified the code. It now uses unmanaged Crypto API to enumerate the names of all containers managed by Microsoft Base Smart Card Crypto Provider and then searches for the corresponding X509Certificate2 objects in the CurrentUser\My store. Please note that this approach is also very hackish and provided code may not work reliably with all the cards/minidrivers available on the market. It is usually better and easier to let the user pick correct certificate from the built-in certificate selection dialog.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace CSP
{
public static class BaseSmartCardCryptoProvider
{
private const string _providerName = "Microsoft Base Smart Card Crypto Provider";
private static class NativeMethods
{
public const uint PROV_RSA_FULL = 0x00000001;
public const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
public const uint CRYPT_FIRST = 0x00000001;
public const uint CRYPT_NEXT = 0x00000002;
public const uint ERROR_NO_MORE_ITEMS = 0x00000103;
public const uint PP_ENUMCONTAINERS = 0x00000002;
[DllImport("advapi32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true, SetLastError = true)]
public static extern bool CryptAcquireContext(
ref IntPtr phProv,
[MarshalAs(UnmanagedType.LPStr)] string pszContainer,
[MarshalAs(UnmanagedType.LPStr)] string pszProvider,
uint dwProvType,
uint dwFlags);
[DllImport("advapi32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true, SetLastError = true)]
public static extern bool CryptGetProvParam(
IntPtr hProv,
uint dwParam,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder pbData,
ref uint pdwDataLen,
uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptReleaseContext(
IntPtr hProv,
uint dwFlags);
}
public static List<X509Certificate2> GetCertificates()
{
List<X509Certificate2> certs = new List<X509Certificate2>();
X509Store x509Store = null;
try
{
x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
List<string> containers = GetKeyContainers();
foreach (string container in containers)
{
CspParameters cspParameters = new CspParameters((int)NativeMethods.PROV_RSA_FULL, _providerName, container);
cspParameters.Flags = CspProviderFlags.UseExistingKey;
string pubKeyXml = null;
using (RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParameters))
pubKeyXml = rsaProvider.ToXmlString(false);
foreach (X509Certificate2 cert in x509Store.Certificates)
{
if ((cert.PublicKey.Key.ToXmlString(false) == pubKeyXml) && cert.HasPrivateKey)
certs.Add(cert);
}
}
}
finally
{
if (x509Store != null)
{
x509Store.Close();
x509Store = null;
}
}
return certs;
}
private static List<string> GetKeyContainers()
{
List<string> containers = new List<string>();
IntPtr hProv = IntPtr.Zero;
try
{
if (!NativeMethods.CryptAcquireContext(ref hProv, null, _providerName, NativeMethods.PROV_RSA_FULL, NativeMethods.CRYPT_VERIFYCONTEXT))
throw new Win32Exception(Marshal.GetLastWin32Error());
uint pcbData = 0;
uint dwFlags = NativeMethods.CRYPT_FIRST;
if (!NativeMethods.CryptGetProvParam(hProv, NativeMethods.PP_ENUMCONTAINERS, null, ref pcbData, dwFlags))
throw new Win32Exception(Marshal.GetLastWin32Error());
StringBuilder sb = new StringBuilder((int)pcbData + 1);
while (NativeMethods.CryptGetProvParam(hProv, NativeMethods.PP_ENUMCONTAINERS, sb, ref pcbData, dwFlags))
{
containers.Add(sb.ToString());
dwFlags = NativeMethods.CRYPT_NEXT;
}
int err = Marshal.GetLastWin32Error();
if (err != NativeMethods.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(err);
if (hProv != IntPtr.Zero)
{
if (!NativeMethods.CryptReleaseContext(hProv, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
hProv = IntPtr.Zero;
}
}
catch
{
if (hProv != IntPtr.Zero)
{
if (!NativeMethods.CryptReleaseContext(hProv, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
hProv = IntPtr.Zero;
}
throw;
}
return containers;
}
}
}
Just call GetCertificates() method of the provided class to check out whether this code works with your card:
List<X509Certificate2> certs = CSP.BaseSmartCardCryptoProvider.GetCertificates();
I was wondering why you do foreach through all certificates in a store when you know cert subject. My suggestion would be:
public static byte[] Sign(Stream inData, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var foundCerts = my.Certificates.Find(X509FindType.FindBySubjectName, certSubject, true);
if (foundCerts.Count == 0)
throw new Exception("No valid cert was found");
var cert = foundCerts[0];
RSACryptoServiceProvider csp = null;
// let us assume that certSubject is unique
if (cert.HasPrivateKey)
{
csp = (RSACryptoServiceProvider)cert.PrivateKey;
if (csp.CspKeyContainerInfo.HardwareDevice)
Console.WriteLine("hardware");
Console.WriteLine(cert.ToString());
}
else
{
throw new Exception("No private key assigned to this certificate");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
byte[] hash = sha1.ComputeHash(inData);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
If you don't know exact subject or are expecting to find another certificate with this subject this will probably not work for you.

Finding the default application for opening a particular file type on Windows

I'm developing an application targeting .NET Framework 2.0 using C# for which I need to be able to find the default application that is used for opening a particular file type.
I know that, for example, if you just want to open a file using that application you can use something like:
System.Diagnostics.Process.Start( "C:\...\...\myfile.html" );
to open an HTML document in the default browser, or
System.Diagnostics.Process.Start( "C:\...\...\myfile.txt" );
to open a text file in the default text editor.
However, what I want to be able to do is to open files that don't necessarily have a .txt extension (for example), in the default text editor, so I need to be able to find out the default application for opening .txt files, which will allow me to invoke it directly.
I'm guessing there's some Win32 API that I'll need to P/Invoke in order to do this, however a quick look with both Google and MSDN didn't reveal anything of much interest; I did find a very large number of completely irrelevant pages, but nothing like I'm looking for.
You can check under registry section HKEY_CLASSES_ROOT for the extension and action details. Documentation for this is on MSDN. Alternatively, you can use the IQueryAssociations interface.
All current answers are unreliable. The registry is an implementation detail and indeed such code is broken on my Windows 8.1 machine. The proper way to do this is using the Win32 API, specifically AssocQueryString:
using System.Runtime.InteropServices;
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern uint AssocQueryString(
AssocF flags,
AssocStr str,
string pszAssoc,
string pszExtra,
[Out] StringBuilder pszOut,
ref uint pcchOut
);
[Flags]
public enum AssocF
{
None = 0,
Init_NoRemapCLSID = 0x1,
Init_ByExeName = 0x2,
Open_ByExeName = 0x2,
Init_DefaultToStar = 0x4,
Init_DefaultToFolder = 0x8,
NoUserSettings = 0x10,
NoTruncate = 0x20,
Verify = 0x40,
RemapRunDll = 0x80,
NoFixUps = 0x100,
IgnoreBaseClass = 0x200,
Init_IgnoreUnknown = 0x400,
Init_Fixed_ProgId = 0x800,
Is_Protocol = 0x1000,
Init_For_File = 0x2000
}
public enum AssocStr
{
Command = 1,
Executable,
FriendlyDocName,
FriendlyAppName,
NoOpen,
ShellNewValue,
DDECommand,
DDEIfExec,
DDEApplication,
DDETopic,
InfoTip,
QuickTip,
TileInfo,
ContentType,
DefaultIcon,
ShellExtension,
DropTarget,
DelegateExecute,
Supported_Uri_Protocols,
ProgID,
AppID,
AppPublisher,
AppIconReference,
Max
}
Relevant documentation:
AssocQueryString
ASSOCF
ASSOCSTR
Sample usage:
static string AssocQueryString(AssocStr association, string extension)
{
const int S_OK = 0;
const int S_FALSE = 1;
uint length = 0;
uint ret = AssocQueryString(AssocF.None, association, extension, null, null, ref length);
if (ret != S_FALSE)
{
throw new InvalidOperationException("Could not determine associated string");
}
var sb = new StringBuilder((int)length); // (length-1) will probably work too as the marshaller adds null termination
ret = AssocQueryString(AssocF.None, association, extension, null, sb, ref length);
if (ret != S_OK)
{
throw new InvalidOperationException("Could not determine associated string");
}
return sb.ToString();
}
Doh! Of course.
HKEY_CLASSES_ROOT\.txt
includes a reference to
HKEY_CLASSES_ROOT\txtfile
which contains a subkey
HKEY_CLASSES_ROOT\txtfile\shell\open\command
which references Notepad.
Sorted, many thanks!
Bart
Here is a blog post with about this topic. The code samples are in VB.net, but it should be easy to port them to C#.
You can just query the registry. First get the Default entry under HKEY_CLASSES_ROOT\.ext
That will give you the classname. For example .txt has a default of txtfile
Then open up HKEY_CLASSES_ROOT\txtfile\Shell\Open\Command
That will give you the default command used.
A late answer, but there is a good NUGET package that handles file associations: File Association
Link NUGET File Association
Usage is simple, for instance to add all allowed file extensions to a context menu:
private void OnMenuSourceFileOpening(object sender, ...)
{ // open a context menu with the associated files + ".txt" files
if (File.Exists(this.SelectedFileName))
{
string fileExt = Path.GetExtension(this.SelectedFileNames);
string[] allowedExtensions = new string[] { fileExt, ".txt" };
var fileAssociations = allowedExtensions
.Select(ext => new FileAssociationInfo(ext));
var progInfos = fileAssociations
.Select(fileAssoc => new ProgramAssociationInfo (fileAssoc.ProgID));
var toolstripItems = myProgInfos
.Select(proginfo => new ToolStripLabel (proginfo.Description) { Tag = proginfo });
// add also the prog info as Tag, for easy access
// when the toolstrip item is selected
// of course this can also be done in one long linq statement
// fill the context menu:
this.contextMenu1.Items.Clear();
this.contextMenuOpenSourceFile.Items.AddRange (toolstripItems.ToArray());
}
}

Change Named Pipe Access Permissions

I have created a named pipe using System.IO.Pipes. It worked fine until I had to run the program in admin mode. When elevated, the client can no longer connect (client is not running elevated). If I run the client as administrator, it connects fine so it looks like a permissions issue. I've been researching how to fix this and have been unsuccessful (I find dealing with Windows security mind boggling). My goal is to allow any client - whether elevated or not - to be able to connect to the pipe.
The first thing I changed was opening the pipe with access rights:
pipeServer = new NamedPipeServerStream(pipeName,
PipeDirection.InOut,
1,
PipeTransmissionMode.Message,
PipeOptions.Asynchronous,
0x4000,
0x400,
null,
HandleInheritability.Inheritable,
PipeAccessRights.ChangePermissions | PipeAccessRights.AccessSystemSecurity);
Then I cobbled this code together. Everything works until the SetEntriesInAcl call which fails with:
Error: 0x534
"No mapping between account names and security IDs was done."
IntPtr ownerSid = IntPtr.Zero;
IntPtr groupSid = IntPtr.Zero;
IntPtr dacl = IntPtr.Zero, newDacl = IntPtr.Zero;
IntPtr sacl = IntPtr.Zero;
IntPtr securityDescriptor = IntPtr.Zero;
if (SUCCEEDED(GetSecurityInfo(pipeServer.SafePipeHandle.handle.DangerousGetHandle(),
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
out ownerSid,
out groupSid,
out dacl,
out sacl,
out securityDescriptor))) {
EXPLICIT_ACCESS ea = new EXPLICIT_ACCESS();
BuildExplicitAccessWithName(ref ea, "Everyone", GENERIC_ALL, ACCESS_MODE.GRANT_ACCESS, NO_INHERITANCE);
// Next line fails
if (SUCCEEDED(SetEntriesInAcl(1, ref ea, dacl, out newDacl))) {
uint retval = SetSecurityInfo(handle,
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
IntPtr.Zero,
IntPtr.Zero,
newDacl,
IntPtr.Zero);
// Haven't reached this point yet
}
}
The BuildExplicitAccessWithName function does not return a value but seems to succeed ok. This is what it looks like after the call:
I would appreciate any help here.
(All the Win32 functions and data types were found on pinvoke.net. Also, I'm using Windows 10.)
I ended up not having to use any native calls. The PipeSecurity class worked. The trick was I had to pass it to the constructor:
// Creates a PipeSecurity that allows users read/write access
PipeSecurity CreateSystemIOPipeSecurity()
{
PipeSecurity pipeSecurity = new PipeSecurity();
var id = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
// Allow Everyone read and write access to the pipe.
pipeSecurity.SetAccessRule(new PipeAccessRule(id, PipeAccessRights.ReadWrite, AccessControlType.Allow));
return pipeSecurity;
}
Use that function when creating the pipe:
PipeSecurity pipeSecurity = CreateSystemIOPipeSecurity();
pipeServer = new NamedPipeServerStream(pipeName,
PipeDirection.InOut,
1,
PipeTransmissionMode.Message,
PipeOptions.Asynchronous,
0x4000,
0x400,
pipeSecurity,
HandleInheritability.Inheritable);

Signing certificate with root certificate (CA) works but the new signed certificate has no extensions

this is my first question. I have worked on the problem several days and given up, so i hope you can help me.
I wrote an application in C# that can create a certificate with extensions (alternativ domainname, extensions for signing etc.) and import it in the Windows Certificate Store. The most of the functions are Marshaled from Crypt32.dll like CertCreateSelfSignCertificate and everything works.
My next task is to sign a certificate with a self created certificate authority. So i used the CryptSignAndEncodeCertificate to create a signed certificate. It works to created a signed certificate but it has no extensions and no private key.
This is the function that should make the magic.
public static X509Certificate2 SignCertificate(X509Certificate2 certificateToSign, int KeySpecification, X509Certificate2 CACert)
{
IntPtr hCAProv = IntPtr.Zero;
IntPtr hProvAllocPtr = IntPtr.Zero;
IntPtr subordinateCertInfoAllocPtr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// Get CA cert into CERT_CONTEXT
// Get CA cert into CERT_INFO from context.pCertInfo
NativeMethods.CERT_CONTEXT CAContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(CACert.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO CACertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(CAContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
uint pcbData = 0;
// get the context property handle of the CA Cert
if (!NativeMethods.CertGetCertificateContextProperty(CACert.Handle, 2, hProvAllocPtr, ref pcbData))
throw new CryptographicException(Marshal.GetLastWin32Error());
hProvAllocPtr = NativeMethods.LocalAlloc(0, new IntPtr((long)pcbData));
if (!NativeMethods.CertGetCertificateContextProperty(CACert.Handle, 2, hProvAllocPtr, ref pcbData))
throw new CryptographicException(Marshal.GetLastWin32Error());
CRYPT_ALGORITHM_IDENTIFIER signatureAlgo = new CRYPT_ALGORITHM_IDENTIFIER()
{
pszObjId = NativeMethods.OID_RSA_SHA256RSA
};
// get the key handle of the CA Cert
CRYPT_KEY_PROV_INFO pKeyInfo = (CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(hProvAllocPtr, typeof(CRYPT_KEY_PROV_INFO));
// Acquire a context to the provider for crypto
if (!NativeMethods.CryptAcquireContext(ref hCAProv, pKeyInfo.ContainerName, pKeyInfo.ProviderName, pKeyInfo.ProviderType, pKeyInfo.Flags))
throw new Win32Exception(Marshal.GetLastWin32Error());
// Get subordinate cert into CERT_CONTEXT
// Get subordinate cert into CERT_INFO from context.pCertInfo
NativeMethods.CERT_CONTEXT subordinateCertContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(certificateToSign.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO subordinateCertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(subordinateCertContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
IntPtr extensions = CreateExtensionsStructure(certificateToSign.Extensions);
subordinateCertInfo.cExtension = certificateToSign.Extensions == null ? 0 : (uint)certificateToSign.Extensions.Count;
subordinateCertInfo.rgExtension = extensions;
subordinateCertInfo.SignatureAlgorithm = signatureAlgo;
subordinateCertInfo.Issuer = CACertInfo.Subject;
subordinateCertInfoAllocPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.CERT_INFO)));
Marshal.StructureToPtr(subordinateCertInfo, subordinateCertInfoAllocPtr, false);
byte[] pbEncodedCert = null;
UInt32 pbEncodedCertLength = 0;
if (!NativeMethods.CryptSignAndEncodeCertificate(hCAProv,
(uint)KeySpecification,
NativeMethods.X509_ASN_ENCODING,
NativeMethods.X509_CERT_TO_BE_SIGNED,
subordinateCertInfoAllocPtr,
ref signatureAlgo,
IntPtr.Zero,
pbEncodedCert,
ref pbEncodedCertLength))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pbEncodedCert = new byte[pbEncodedCertLength];
if (!NativeMethods.CryptSignAndEncodeCertificate(hCAProv,
(uint)KeySpecification,
NativeMethods.X509_ASN_ENCODING,
NativeMethods.X509_CERT_TO_BE_SIGNED,
subordinateCertInfoAllocPtr,
ref signatureAlgo,
IntPtr.Zero,
pbEncodedCert,
ref pbEncodedCertLength))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
X509Certificate2 signedCertificate = new X509Certificate2(pbEncodedCert, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
return signedCertificate;
}
An other question is why don't i get the extensions of the given certificate from the CERT_INFO
NativeMethods.CERT_CONTEXT subordinateCertContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(certificateToSign.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO subordinateCertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(subordinateCertContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
By the way if i use the subordinateCertInfo directly in CryptSignAndEncodeCertificate as pvStructInfo the new certificate has the extensions but it will not have the connection to the CA. Because the subordinateCertInfo.Issuer was not set.
[DllImport(CRYPT32, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptSignAndEncodeCertificate(IntPtr hCryptProvOrNCryptKey,
uint dwKeySpec,
uint dwCertEncodingType,
ulong lpszStructType,
IntPtr pvStructInfo,
ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
IntPtr pvHashAuxInfo,
byte[] pbEncoded,
ref uint pcbEncoded);
By the way no i don't want to use Bouncy Castle or write the application in C++. So i hope you have a solution without some extrem changes.

How to decrypt the authentication buffer obtained from the secure desktop

I'm trying to request the user to enter credentials on the secure desktop, but I can't find a way to decrypt the resulting authentication buffer:
The credentials are obtained with:
CREDUI_INFO cui = new CREDUI_INFO()
{
cbSize = (uint)Marshal.SizeOf(typeof(CREDUI_INFO)),
pszMessageText = "MyMessage",
pszCaptionText = "MyCaption",
};
bool save = false;
uint authPackage = 0U;
IntPtr authBuffer;
uint authBufferSize;
uint error = CredUIPromptForWindowsCredentials(
ref cui,
0U,
ref authPackage,
IntPtr.Zero,
0U,
out authBuffer,
out authBufferSize,
ref save,
CREDUIWIN_SECURE_PROMPT);
The credentials are unpacked with:
uint userBufferSize = CREDUI_MAX_USERNAME_LENGTH;
uint domainBufferSize = CRED_MAX_STRING_LENGTH;
uint passwordBufferSize = CREDUI_MAX_PASSWORD_LENGTH;
StringBuilder userBuffer = new StringBuilder((int)userBufferSize);
StringBuilder domainBuffer = new String((int)domainBufferSize);
IntPtr passwordBuffer = Marshal.AllocCoTaskMem((int)passwordBufferSize);
bool success = CredUnPackAuthenticationBuffer(
CRED_PACK_PROTECTED_CREDENTIALS,
authBuffer, authBufferSize
userBuffer, ref userBufferSize,
domainBuffer, ref domainBufferSize,
passwordBuffer, ref passwordBufferSize);
If I specify CREDUIWIN_SECURE_PROMPT in CredUIPromptForWindowsCredentials as in the example above, CredUnPackAuthenticationBuffer fails with ERROR_NOT_CAPABLE. If I use CREDUIWIN_GENERIC instead of the secure desktop, it succeeds.
How do I obtain the name, domain and password from such encrypted credentials from the secure desktop? Or how could I use them generally if that would not be possible?

Resources