Bizarre Return Code from SetUserFileEncryptionKey

115 views Asked by At

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.)

1

There are 1 answers

2
Benjamin On

It appears that 0x80092004 is CRYPT_E_NOT_FOUND and that SetUserFileEncryptionKey only works with certificates that are already part of the user's certificate store. When I import the relevant certificate, the above code returns ERROR_SUCCESS. It seems this function doesn't serve the use case that I hoped it did.