0

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

Benjamin
  • 123
  • 1
  • 1
  • 7
  • `SetUserFileEncryptionKey` accepts a pointer to a structure. Try to change the signature of this function to `IntPtr` parameter and pass pointer to a structure. – Crypt32 Oct 13 '20 at 17:43
  • Thanks for the suggestion. I thought that passing a class instance was the same as a ref to a struct (https://learn.microsoft.com/en-us/dotnet/framework/interop/passing-structures), but I'll try that anyway. – Benjamin Oct 13 '20 at 18:24
  • Some APIs are tough and not always work same way through interop. It is a slight change to have a test. If not, then there can be an issue with actual certificate. – Crypt32 Oct 13 '20 at 18:27
  • Looks like with structs and an `IntPtr` I get the same result. When I double-click on the certfile, it seems to be added fine. Anything else that you can think of? – Benjamin Oct 13 '20 at 18:39
  • Last question: what is passed to `pUserSid`? – Crypt32 Oct 13 '20 at 18:43
  • I just added that part in. I get the info from the encrypted file itself, which corresponds to the same user that the key came from. – Benjamin Oct 13 '20 at 18:54

1 Answers1

0

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.

Benjamin
  • 123
  • 1
  • 1
  • 7
  • It seems the document needs some update to include how and when to use `SetUserFileEncryptionKey()`. Generally, use current user key to encrypt the file and decrypt the file. So could you describe your use case a bit more? – Rita Han Oct 15 '20 at 02:19
  • I agree; there's not much info about what that function was intended for. I added a little bit about my use case. – Benjamin Oct 16 '20 at 15:43