File.Encrypt() causes IOException The parameter is incorrect

485 views Asked by At

I have a web application that when prompted will check if there is a file with some encryption keys in a particular location. If the file is not present, the keys will be auto-generated and stored within the file. Afterwards, the file is supposed to be encrypted itself.

When I run

File.Encrypt(keyFilePath); 

it runs into an IOException with the message being "The parameter is incorrect".

The operation is running with impersonation of a limited account. When I run under my own elevated credentials, everything works perfectly. I have checked the certificates, created one for the limited account, added the account to all the possible roles (cryptography operators, etc.) and tested. Nothing worked. In the test environment I elevated the account to an unreasonable level and it could perform the encrypt operation. Afterwards I set the account back to the usual level and it could read the file perfectly.

The problem is that I cannot ask for that to be performed in production once this solution is final. My test environment is windows server 2008 data center edition and the solution is being developed on asp .net mvc 5. Please let me know what could be wrong.

1

There are 1 answers

0
Aldeatho On

Here is a solution that might work for you:

If you are able to use the command prompt (batch script [.bat]), or you have written your application in any language that allows you to run external exe's, the answer lies in PSExec.exe & you don't need admin privileges.

I'm not an expert in how Microsoft's EFS is coded or working but it seems the problem is something to do with the certificate or key management programs that are run in the background for each user, only upon user sign in. I say programs because no services are started (other than the ones already running) by signing in to the other user (User X) on the PC.

PSExec is able to perform a proper sign-in of a user entirely in the background, whereas 'run as user' (or anything of the like) seems to be imitating the user's security token (again, I'm no Windows employee) with something akin to the LogonUser function.

Anyways, here's the code that fixes the issue once & for all (keep in mind that User X is the user who encrypted the files in the first place):

Batch Script:

cd /d "C:/PSExec/directory"
PSExec -accepteula -nobanner -d -u "User X" -p "P455W0RD" cipher /c "C:/path/to/any.file"

Once your batch file has run, run your app on behalf of User X. You can make the batch file start at start-up using Windows' built-in task scheduler.

What happened above:

  • Go to the PSExec directory

  • Run PSExec with the following options:

  • (-accepteula) Otherwise PSExec asks you with a prompt to click 'agree' to the software license agreement

  • (-nobanner) Don't display the PSExec copyright & author banner

  • (-d) Don't launch an interactive command prompt

  • (-u & -p) Username & password for the user that originally encrypted the app you want to run & they must also be the user you want to run the app for (User X as you said)

  • (cipher /c [filePath]) Use EFS to check a file on behalf of User X; this is what starts up the certificate & key programs on the user once PSExec has properly signed-in as User X

C# Script (make sure to place using System.Diagnostics; at the top of your C# script file):

//Register a process context for PSExec to initialize a real user logon & relevant EFS programs
ProcessStartInfo execution = new ProcessStartInfo
{
    CreateNoWindow = true,
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    WindowStyle = ProcessWindowStyle.Hidden,
    UseShellExecute = false,
    FileName = "C:/path/to/PSExec.exe",
    Arguments = "-accepteula -nobanner -d -u \"User X\" -p \"P455W0RD\" cipher /c \"C:/path/to/any.file\""
};

//Run user & EFS initialization
Process.Start(execution).WaitForExit();

//Initialize execution of an encrypted program on behalf of a user
execution.FileName = "C:/path/to/encrypted/app.exe";
execution.UserName = "User X";
execution.PasswordInClearText = "P455W0RD";

//Run the specified user's exclusive application
Process.Start(execution);

//Terminate thread
return;

Not the most secure in terms of your passwords, but I'm sure after 7 years of this post being up you'll have your means. Hope this helps someone like me or you who bump into this along the way :).