I'd like to read an EFS certificate (say from a pfx file) and use it temporarily to read/write some files. (I'd like it to not persist in any store once the program exits.) It looks like SetUserFileEncryptionKey
might provide this functionality, but I get a bizarre return code (0x80092004
) when I try it. Here's my code:
var x509Cert = new X509Certificate2(@"C:\Users\Public\Downloads\key.pfx", "<mypass>");
var certContext = Marshal.PtrToStructure<CertContext>(x509Cert.Handle);
var blob = new EfsCertificateBlob
{
dwCertEncodingType = certContext.dwCertEncodingType,
cbData = certContext.cbCertEncoded,
pbData = certContext.pbCertEncoded,
};
var pCertBlob = Marshal.AllocHGlobal(Marshal.SizeOf(blob));
Marshal.StructureToPtr(blob, pCertBlob, false);
var id = WindowsIdentity.GetCurrent();
var curStringSid = id.User?.Value;
Console.WriteLine(curStringSid);
ConvertStringSidToSid(curStringSid, out var sidPtr);
var certStruct = new EncryptionCertificate
{
cbTotalLength = (uint) Marshal.SizeOf(typeof(EncryptionCertificate)),
pUserSid = sidPtr,
pCertBlob = pCertBlob,
};
var res = SetUserFileEncryptionKey(certStruct);
Console.WriteLine($"Result: 0x{res:X}"); // Result: 0x80092004
Here too is my interop code:
[StructLayout(LayoutKind.Sequential)]
public class CertContext
{
public uint dwCertEncodingType;
public IntPtr pbCertEncoded;
public uint cbCertEncoded;
public IntPtr pCertInfo;
public IntPtr hCertStore;
}
[StructLayout(LayoutKind.Sequential)]
public class EfsCertificateBlob
{
public uint dwCertEncodingType;
public uint cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential)]
public class EncryptionCertificate
{
public uint cbTotalLength;
public IntPtr pUserSid;
public IntPtr pCertBlob;
}
[DllImport("Advapi32.dll")]
public static extern uint SetUserFileEncryptionKey(EncryptionCertificate pEncryptionCertificate);
Does SetUserFileEncryptionKey
do what I hope it does? And what am I doing wrong here?
(My use case is in working with sensitive data that I don't want the user to later be able to read or redistribute. So I'd like those files to be inaccessible as soon as the process terminates.)
It appears that
0x80092004
isCRYPT_E_NOT_FOUND
and thatSetUserFileEncryptionKey
only works with certificates that are already part of the user's certificate store. When I import the relevant certificate, the above code returnsERROR_SUCCESS
. It seems this function doesn't serve the use case that I hoped it did.