Generally I am not the type to recommend re-implementing functionality present in some API. But with the Windows function CopyFile(Ex) there may be no alternative, because of the less than sub-optimal way Windows copies EFS-encrypted files:
- If the source encryption key can be used on the target (i.e. if the target computer can access the EFS certificate somehow) the file is decrypted on the source computer, transmitted unencrypted over the network and re-encrypted with the same key on the target computer.
- If that fails (e.g. the target computer cannot access the EFS certificate or EFS is disabled on the target), a new key is generated on the target and the file is encrypted with that new key.
- If that fails, too, CopyFile(Ex) fails with ERROR_ENCRYPTION_FAILED unless CopyFileEx has been used and the flag COPY_FILE_ALLOW_DECRYPTED_DESTINATION has been set. Only then is the file stored decrypted on the target system.
Now what is wrong with that algorithm? Pretty much everything:
- The file is transmitted unencrypted over the network.
- When copying encrypted files, either I want them to be encrypted on the target with the same key as in the source, or I do not want it encrypted at all. These two options are missing:
- Do a copy of the raw encrypted data similarly to robocopy with the switch /EFSRAW. This copies the encrypted file without resorting to encryption/decryption. In order to decrypt the file on the target, the private key is required. A side effect of this is that the file is never decrypted and thus transmitted encrypted over the network.
- Store the file unencrypted on the target.
- I never want a file to be encrypted with a new default key (paragraph 2 above). Although generating new self-signed certificates can be turned off per server (see below), this is fundamentally wrong and may lead to new user profiles being created on the target machine.
Do it Yourself
What remains? Copying EFS-encrypted files essentially requires the diligent devolper to write his/her own version of CopyFile(Ex), a task that should not be underestimated. Apparently achieving high performance is one of the many things that are far from trivial. Here are some tips:
- Use the API functions CreateFile, ReadFile and WriteFile to first open the source and create the target and then read/write in chunks.
- Chunk size is relevant. 512 KB seems like a good starting point when reading, 32 MB when writing.
- Use the flag FILE_FLAG_NO_BUFFERING to prevent the operating system’s file system cache to become filled with data that probably is not needed again soon.
- Do not use FILE_FLAG_WRITE_THROUGH, because that disabled the disk’s cache.
- Use asynchronous IO file create an IO queue.
Tips and Helpful Articles on EFS
- Disable EFS on the target computer in order to enforce unencrypted storage of files encrypted in the source. There are several ways to to that:
- fsutil behavior set disableencryption 1 [Vista and newer]. This has the same effect as setting NtfsDisableEncryption to 1 (see below).
- Set the value HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisableEncryption to 1 [DWORD].
- Set the value HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\Efsconfiguration to 1 [DWORD].
- Via Group Policy: Clear Computer Configuration/Windows Settings/Security Settings/Public Key Policies/Encrypting File System/Properties/Allow users to encrypt files using Encrypting File System (EFS)
- Prevent the creation of self-signed certificates:
- Set the value HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\CurrentVersion\EFS\EfsOptions to 0 [DWORD]. An example for an ADM file can be found in the Microsoft KB.
- Via Group Policy: Clear Computer Configuration/Windows Settings/Security Settings/Public Key Policies/Encrypting File System/Properties/Allow EFS to generate self-signed certificate when a Certificate Authority is not available