Securing a computer system entails employing measures that protect the computer's data from viewing or manipulation by unauthorized users. Security measures at the network interface prevent intruders from gaining entry to the computer, and file-system security prevents the computer's authorized users from accessing data they're not supposed to access. However, a computer that is isolated from the Internet behind a firewall and that has stringent file-system security policies in place remains unsecured if no strategy exists to guard the computer's physical security. If unauthorized users have physical access to a computer, they can remove the computer's hard disks and perform offline analysis of the disks' data. When users can view a hard disk's contents on a different computer, file-system security (e.g., the kind NTFS ACLs provide on Windows NT or Windows 2000— Win2K—systems) is of no value. This problem is especially acute for laptop computers because two NTFS file-system drivers that ignore NTFS security—NTFSDOS and an NTFS driver for Linux—let even casual thieves easily view NTFS files.
One way to address the physical security problem is to keep computers in locked rooms, but this solution is obviously not practical for laptop computers, whose main purpose is portability. Thus, to prevent access to file data in situations in which bypassing file-system security is a possibility, data encryption is necessary. Before Win2K, NT users have had to turn to third-party vendors for encryption solutions, but in Win2K a built-in encryption facility for NTFS files exists in the form of Encrypting File System (EFS). By building encryption into the OS, Microsoft can make the encryption and decryption process transparent to both applications and users.
Unfortunately, Microsoft has produced little documentation describing how EFS works. Because many people will undoubtedly rely on EFS to secure their sensitive data, having a solid understanding of what goes on under the hood is important. In this two-part series about EFS, I'll take you beneath the surface and let you know exactly how EFS works with NTFS and Win2K cryptography facilities to help you keep your data safe from prying eyes. This month, I provide an overview of EFS and begin walking you through the process by which EFS encrypts files. Next month, I'll finish the encryption walk-through, describe the decryption process, and introduce the data recovery mechanism EFS has built into the decryption process.
EFS security relies on Win2K cryptography support, which Microsoft introduced in NT 4.0. The first time you encrypt a file, EFS assigns your account one public key and private key pair for use in file encryption. You can deliberately encrypt files via an NT Explorer dialog box, as Screen 1, page 52, shows, or a command-line utility. Win2K automatically encrypts files that reside in directories the OS designates as encrypted directories. When you encrypt a file, EFS generates a random number for the file that EFS calls the file's file encryption key (FEK). EFS uses the FEK to encrypt the file's contents with a stronger variant of the Data Encryption Standard (DES) algorithm—DESX. EFS stores the file's FEK with the file but encrypts the file with your EFS public key using the RSA public key-based encryption algorithm. After EFS completes these steps, the file is secure: Other users can't decrypt your data without the file's decrypted FEK, and they can't decrypt the FEK without your private key.
Why does EFS use a public key/private key algorithm to encrypt FEKs, and DESX to encrypt file data? Because DESX uses the same key to encrypt and decrypt data, it is a symmetric encryption algorithm. Symmetric encryption algorithms are typically very fast, which makes them suitable for encrypting large amounts of data, such as file data. However, symmetric encryption algorithms have a weakness: You can bypass their security if you obtain the key. If multiple users want to share one encrypted file protected only by DESX, each user would require access to the file's FEK. Leaving the FEK unencrypted would obviously be a security problem, but encrypting the FEK once would require all the users to share the same FEK decryption key—another potential security problem.
Keeping the FEK secure is the difficult problem EFS addresses with the public-key-based half of its encryption architecture. Encrypting a file's FEK for individual users who access the file lets multiple users share an encrypted file. EFS can encrypt a file's FEK with each user's public key and can store each user's encrypted FEK with the file. Anyone can access a user's public key, but no one can use a public key to decrypt the data that the public key encrypted. The only way users can decrypt a file is with their private key, which the OS must access and typically stores in a secure location. A user's private key decrypts the user's encrypted copy of a file's FEK. Win2K's first release will store private keys on a computer's hard disk (an arrangement that isn't terribly secure), but subsequent releases of the OS will let you store your private key on a smart card, for example. Public-key-based algorithms are usually slow, but EFS uses these algorithms only to encrypt FEKs. Splitting key management between a publicly available key and a private key makes key management a little easier than symmetric encryption algorithms do, and solves the dilemma of keeping the FEK secure.
Several components work together to make EFS work, as the diagram of EFS architecture in Figure 1 shows. EFS is a device driver that runs in Win2K's kernel mode, in which EFS is tightly connected with the NTFS file-system driver. Whenever NTFS encounters an encrypted file, NTFS executes functions in the EFS driver that the EFS driver registered with NTFS when EFS initialized. The EFS functions encrypt and decrypt file data as applications access encrypted files. Although EFS stores a FEK with a file's data, users' public keys encrypt the FEK. To encrypt or decrypt file data, EFS must decrypt the file's FEK with the aid of cryptography services that reside in user mode.
The Local Security Authority Subsystem (lsass.exe) manages logon sessions but also handles EFS key management chores. For example, when the EFS driver needs to decrypt a FEK to decrypt file data a user wants to access, the EFS driver sends a request to LSASS. EFS sends the request via local procedure call (LPC), a form of interprocess communication in NT and Win2K that the remote procedure call (RPC) API uses for communication between two endpoints on the same computer. (To read more about LPC and RPC, see NT Internals: "Inside NT Networking," March 1999.) The ksecdd.sys driver exports functions for other drivers that need to send LPC messages to LSASS. The Local Security Authority Server (lsasrv.dll) component of LSASS that listens for RPC requests passes requests to decrypt a FEK to the appropriate EFS-related decryption function, which also resides in LSASRV. LSASRV uses functions in Microsoft's CryptoAPI to decrypt the FEK, which the EFS driver sent to LSASS in encrypted form. The CryptoAPI comprises Cryptographic Provider DLLs that make various cryptography services (such as encryption/decryption and hashing) available to applications. For example, the Cryptographic Provider DLLs manage user private and public key storage and retrieval so that LSASRV doesn't need to concern itself with the details of the encryption algorithms or how keys are protected. After LSASRV decrypts a FEK, LSASRV returns the FEK to the EFS driver via an LPC reply message. After EFS receives the decrypted FEK, EFS can use DESX to decrypt the file data for NTFS. Let's look at the details of how EFS integrates with NTFS, and how LSASRV uses the CryptoAPI to manage keys.
NTFS doesn't require the EFS driver's presence to execute, but encrypted files won't be accessible if the EFS driver isn't present. NTFS has a plug-in interface for the EFS driver, so when the EFS driver initializes, it can attach itself to NTFS. The NTFS driver exports several functions for the EFS driver to use, including the NtOfsRegisterCallbacks function. EFS calls NtOfsRegisterCallbacks to notify NTFS both of EFS's presence and of the EFS-related APIs EFS is making available. All of the other functions NTFS exports for the EFS driver begin with the prefix NtOfs, which presumably stands for NT Object File System. One of the original goals of the NT 5.0 project (code-named Cairo) was to develop an object-oriented file system. Although NTFS in Win2K probably hasn't reached the level of object orientation that the Cairo planners had in mind, Microsoft has extended NTFS in several significant ways from its NT 4.0 implementation. One of those ways is NTFS's support for encrypted files via the NtOfs interfaces.
EFS registers the seven EFS APIs that Table 1 lists with NTFS via the NtOfsRegisterCallbacks interface. So that EFS can provide transparent support for encrypted files, at appropriate places throughout NTFS code execution the NTFS driver looks into the table of registered callbacks and invokes the callback that EFS must execute at that point. NTFS supports only one set of registered callbacks; in other words, other device drivers can't use NtOfsRegisterCallbacks because NTFS assumes that only the EFS driver will plug in to the callback interface. You'll understand better how EFS can perform encryption and decryption based on its callback invocation from NTFS as I walk you through the encryption and decryption process.
Encrypting a File for the First Time
The NTFS driver calls only the EFS functions that register when NTFS encounters an encrypted file. A file's attributes record that the file is encrypted in the same way that a file records that it's compressed. NTFS and EFS have specific interfaces for converting a file from nonencrypted to encrypted form, but user-mode components primarily drive the process. Win2K lets you encrypt a file in two ways: by using the Cipher command-line utility or by checking the Encrypted check box in the Advanced properties dialog box for a file in NT Explorer. Both NT Explorer and Cipher rely on the new EncryptFile Win32 API that the Win32 DLL—advapi32.dll (Advanced Win32 APIs DLL)—exports. ADVAPI32 loads another DLL, feclient.dll (File Encryption Client DLL), to obtain APIs that ADVAPI32 can use to invoke EFS interfaces in LSASRV via LPC.
When LSASRV receives an LPC message from FECLIENT to encrypt a file, LSASRV invokes the internal function EfsRpcEncryptFileSrv. EfsRpcEncryptFileSrv's first step is to use Win2K's impersonation facility to impersonate the user that ran the application (either Cipher or NT Explorer) that is encrypting the file. This procedure lets Win2K treat the file operations that LSASRV performs as if the user who wants to encrypt the file is performing them. LSASRV usually runs in the System account, which has different (and more privileged) security capabilities than a typical user account has. If it doesn't impersonate the user, LSASRV might not have permission to access the file in question.
The next step EfsRpcEncryptFileSrv performs is to create a log file into which LSASRV will record the progress of the encryption process. EfsRpcEncryptFileSrv creates the log file on the same drive as the file that EFS will encrypt, and places the log file under the root directory's subdirectory System Volume Information. The log file usually has the name efs0.log, but if other files are undergoing encryption, EfsRpcEncryptFileSrv replaces the number 0 with increasing numbers until LSASRV can create a unique log file for the current encryption.
Win2K's cryptography APIs rely on information that a user's Registry profile stores, so EfsRpcEncryptFileSrv next uses the LoadUserProfile API of userenv.dll (User Environment DLL) to load the profile into the Registry of the user EfsRpcEncryptFileSrv is impersonating. Usually the user profile is already loaded, because winlogon.exe loads a user's profile when a user interactively logs on. However, if you use the Microsoft Windows NT Server 4.0 Resource Kit Su utility or the Win2K RunAs command to log on to a different account, when you try to access encrypted files from that account, the account's profile might not load.
EfsRpcEncryptFileSrv's next step is to call another LSASRV function, EncryptFileSrv, to carry out the rest of the encryption process for the file. EncryptFileSrv begins by querying NTFS about which data streams exist within the file, noting the result for later use. NTFS supports alternate named data streams in addition to the default unnamed stream in which NT typically stores data. Win2K makes heavy use of alternate streams to support compound document storage (i.e., Native Structured Storage), as does Services for Macintosh, which uses alternate streams to implement Macintosh resource forks. EFS must encrypt all of a file's data streams—not just the stream that most applications see. EncryptFileSrv calls the internal function GenerateFEK to generate a FEK for the file.
GenerateFEK uses the CryptoAPI function CryptAcquireContext to initiate a cryptographic session. CryptAcquireContext takes several parameters, including the name of the session's cryptographic provider and the cryptography service that the caller (in this case, GenerateFEK) is interested in. GenerateFEK specifies Microsoft Base Cryptographic Provider 1.0 as the session's cryptographic provider. GenerateFEK also signals that it wants to use the provider's RSA encryption facilities. Base Cryptographic Provider is a built-in provider that is present on all Win2K systems. However, the architecture of the CryptoAPI lets software vendors implement proprietary providers and dynamically add them to Win2K. Thus, GenerateFEK must specify a session's cryptographic provider. RSA is a public-key-based encryption algorithm that has become a de facto worldwide standard. The file rsabase.dll implements RSA for Base Cryptographic Provider 1.0.
After the CryptAcquireContext function returns with a handle to the provider, GenerateFEK calls CryptGenRandom to have the provider generate 16 bytes (128 bits) of random data to serve as the file's FEK. GenerateFEK then calls CryptCloseSession to close the cryptographic provider session, and returns control to EncryptFileSrv.
Constructing Key Rings
At this point, EncryptFileSrv has a FEK and can construct EFS information to store with the file, including an encrypted version of the FEK. Figure 2 illustrates the EFS information's layout. EncryptFileSrv calls another function, ConstructEFS, to construct the EFS information. Before it can do so, ConstructEFS must use the CryptoAPI to get a handle to the user's public key and private key pair. To get this handle, ConstructEFS calls another function, GetCurrentKey, which reads the Registry value HKEY_CURRENT_USER\Software\ Microsoft\Windows NT\ CurrentVersion\ EFS\CurrentKeys\CertificateHash. Every public key/private key pair has a digital certificate that the issuing certificate authority signs, and that users use to obtain their public keys. The digital signature, or hash, uniquely identifies the public key/private key pair. By reading the CertificateHash value, ConstructEFS obtains the current user's public key signature and uses it to access the public key and encrypt FEKs.
When the CertificateHash value doesn't exist, which is the case the first time a user encrypts a file, EFS must determine whether the user has an EFS public key/private key pair, or whether it must create the key pair. First, EFS opens the My system certificate storage area, in which the OS stores EFS key pairs. (Several certificate storage areas can exist on a system, each containing various certificates.) EFS uses the CryptoAPI function CertFindCertificateInStore to look for an EFS key-pair certificate in a provider's My storage area. If CertFindCertificateInStore doesn't find an EFS key-pair certificate, ConstructEFS calls GenerateUserKey to create one. GenerateUserKey calls CryptUIWizCertRequest to create the key pair and return a signed certificate for the pair. The key-pair generation occurs on a domain controller (for a system that is part of a domain) or the local computer (for a computer that is not part of a domain). When EFS locates or creates the user's key-pair certificate, EFS obtains the certificate's hash and stores it in the Registry key HKEY_CURRENT_USER\ Software\Microsoft\Windows NT\CurrentVersion\ EFS\CurrentKeys\CertificateHash. In Win2K beta 3, the provider that creates public key/private key pairs is Base Cryptographic Provider 1.0.
When it has the user's key-pair hash, EFS uses the CertGetCertificateContextProperty CryptoAPI to obtain information about the provider that CACreateLocalAutoEnrollmentObject used to generate the key pair. This information includes the provider's name and the name of the container the provider uses to store key pairs for the user whom LSASRV is impersonating. The container name is meaningful only to the provider, but in the case of Base Cryptographic Provider, the container name is a file path relative to the user's profile directory. An example container name is M:\Documents and Settings\Administrator\Application Data\Microsoft\SystemCertificates\My\Certificates\CD099602FD898D7EFCDC4283C6742D30A15A0062. Win2K hides the Application Data directory by default, and container filenames vary. EFS uses CryptAcquireContext to open a cryptographic session with the provider. In the call to CryptAcquireContext, EFS specifies the provider name, the container name, and that it wants to use RSA encryption services. To obtain the current user's public key, EFS uses the function CryptExportKey, which causes the cryptographic provider to extract the key from the container.
ConstructEFS can now construct the information that EFS stores with the file. The LSASRV function ConstructKeyRing makes use of the user's public key that CryptExportKey obtained to store the EFS information with a file. Microsoft calls the function ConstructKeyRing because, as I mentioned earlier, EFS lets multiple users share encrypted files. EFS stores only one block of information in an encrypted file, and that block contains an entry for each user sharing the file. These entries are called key entries, and EFS stores them in the Data Decryption Field (DDF) portion of the file's EFS data. A collection of multiple key entries is a key ring.
Figure 2 shows a file's EFS information format and key entry format. EFS stores enough information in the first part of a key entry to precisely describe a user's public key. This data includes the cryptographic provider name, the container name in which the key is stored, the user's security ID (SID), and the public key/private key pair certificate hash. The second part of the key entry contains an encrypted version of the FEK. ConstructKeyRing uses the CryptEncrypt CryptoAPI to encrypt the FEK with the RSA algorithm and the user's public key. (When I describe the decryption process next month, I'll explain why EFS stores this information in key entries.)
Next, EFS creates another key ring that contains recovery key entries. EFS stores information about recovery key entries in a file's Data Recovery Field (DRF). The format of DRF entries is identical to the format of DDF entries. The DRF's purpose is to let designated accounts, or Recovery Agents, decrypt a user's file when administrative authority must have access to the user's data. For example, suppose a company employee used a CryptoAPI to store his private key on a smart card, then lost the card. Without Recovery Agents, no one could recover his encrypted data.
You can define Recovery Agents with the Encrypted Recovery Agents security policy (which is a subset of public key policy) of the local computer or domain, as Screen 2 shows. When you use the Recovery Agent Wizard in the domain or computer management Microsoft Management Console (MMC) snap-in, you can add Recovery Agents and specify which public key/private key pairs (designated by their certificates) the agents use for EFS recovery. LSASRV interprets the recovery policy when it initializes and when it receives notification that the recovery policy has changed. EFS uses the cryptographic provider you register for EFS recovery to create a DRF key entry for each Recovery Agent. Currently, the default Recovery Agent provider is the RSA encryption facility of Base Cryptographic Provider 1.0—the same provider GenerateFEK uses for user keys.
In the final step in creating EFS information for a file, ConstructEFS uses the MD5 hash facility of Base Cryptographic Provider 1.0 to calculate a checksum for the DRF and DDF. EFS stores the checksum's result in the EFS information header. EFS references this checksum during decryption to ensure that the contents of a file's EFS information have not become corrupted or been tampered with. Screen 3 shows an encrypted file's $EFS attribute's on-disk contents. Some of the fields I've described are highlighted, including the name of the cryptographic store that stores the key pair the encryption uses (the string beginning 5.d.c.3.b.3.e.b.-.5.), the name of the cryptographic provider (Microsoft Base Cryptographic Provider v1.), and information used for display when applications query the encryption format (L.=.E.F.S.). DiskEdit, the utility viewing the data in Screen 3, is an unsupported tool that Microsoft includes on the Service Pack 4 (SP4) CD-ROM.
To Be Continued
That brings us to the end of this month's column. Next month, I'll conclude my description of the encryption process, and I'll describe the file decryption process and some of the other EFS APIs that LSASRV supports.