CryptoAPI, base CSPs and private key security.

Note: many of the points raised here refer to older versions of MSIE such as 4.0. With MSIE 5.0 and later setting private key security is now much easier, it is still however possible to set low security so much of this is still relevant.

I have from time to time issued warnings about private key security issues with CryptoAPI and the base CSPs (which almost everyone uses). In this document I will describe the issues and discuss the solutions. CryptoAPI is used by many Microsoft products, including MSIE and Outlook 98. This is not a new issue: it has been mentioned (by me and others) elsewhere.

CryptExportKey function.

It should be emphasized that this problem only applies to the base CSPs which are installed by default by CryptoAPI, if you use an alternative CSP then it may well not be vulnerable. Check your CSP documentation if in doubt.

The problem such as it is is all related to the CryptExportKey function. This standard CryptoAPI function typically allows a private key to be read belonging to the current user (well unless it is unexportable: see below). The problem is that under typical circumstances this can be done silently and without the users consent.

This means that any program run as the same user could in theory at least silently read and steal a users private keys. It could then send them to an appropriate place where the attacker could pick them up.

A compromised private key is much worse than for example a trashed hard disk.

The value of a private key cannot be overemphasized. It may well be the most valuable piece of information on a person's computer. With it an attacker can impersonate the victim. This could be a minor irritation in that they can read their private encrypted mail. If the key is used to sign documents or authentication then this can be more serious (some digital signature legislation renders the victim subject to unlimited liability). If the key is used for signing software then that user's name (or company's name) can be used to sign bogus software. The consequences of a CA private key being stolen would be catastrophic. In this case an attacker could issue their own certificates with totally false details, revoke existing certificates and similar chaos. All existing certificates would need to be revoked and reissued and any information signed with existing certificates would be suspect.

Diagnosing the problem.

The easiest way to see if you are vulnerable is to try to export a certificate and private key with MSIE or Outlook 98. This can be done by selecting View, Internet Options, Content, Personal in MSIE. Now select each key in turn and export it. After you fill in the dialog box asking you for an export password and filename a warning box should appear typically saying "Exporting your private exchange key!" and giving details of the program and asking you to confirm the operation. It may also ask for a password.

If this box does not appear then your private keys are vulnerable (they could be not exportable but this is still a problem).

I had thought at this point of supplying a program that printed out part of a private key when run to demonstrate the problem. However since I've already stressed the dangers and vulnerabilities I doubt anyone would want to run it.


The obvious solution.

The simplest solution to this is to never run any untrusted software. Make sure it is always from a reliable source and never run any untrusted software downloaded from the Internet.

To most people this is not acceptable. Nevertheless this is frequently quoted as a realistic option. This just like saying: the easiest way to avoid a security hole is to not run any software that exploits it.

Private Key protection.

The most usable solution is already present in the base CSPs. However some outdated documentation (saying that the feature was ignored), its lack of use by some CAs and questionable default behaviour has meant that its use is not as widespread as it should be.

When a private key is created (typically by a CA enrollment script) a flag can be set that allows a user to specify a security level for the private key container. There are three settings.

  1.  Low Security. No Security would be a better name. This is the default if the flag is not set: it allows keys to be read and exported silently and without the user's consent.
  2. Medium Security. Whenever an attempt is made to use or read a private key a warning dialog box appears giving details of the program and intended operation. The user can then confirm or deny access.
  3. High Security. The same warning dialog box appears but in addition asks for a password.
Some CAs were not initially aware that this flag did anything useful (after all the documentation suggested it was ignored) and did not set it, others do not set it by default and require you to explicitly ask for "extra security". Why a CA should even be allowed to decide what security a user has is debatable. If the dialog box asking for the security level always appeared then this problem would never have occurred.

Unfortunately there are some drawbacks. Due to a bug in MSIE 4.0 when you export a private key it warns you sixteen times. This is a recognized problem but several months later it still has not been fixed (the latest version of Outlook 98 I've tested also suffers from this problem, except it asks you seventeen times). On the whole though, since exporting a key will not be done very often this is not too serious.

With MSIE 4 when you import a private key and certificate you are not given the choice of security level: you are stuck with low security.

With MSIE 5 and later you can click the checkbox "enable strong private key protection".

Unexportable keys.

There is another flag that can be set (well cleared actually). This makes a key unexportable. This means that the key can never be read by anyone. It can still be used though.

This means that the key cannot be backed up and the registry is corrupted (and you don't have a usable backup) the key is lost forever.

Also even though such a key cannot be exported it can still be used. Therefore it is advisable to still set the protection flags.

With MSIE 4 there was no way to clear the exportable key flag. With MSIE 5 the exportable flag is cleared by default but can be set using a checkbox when a key is imported.

Further Attacks.

The CryptoExportKey attack mentioned here is a very simple attack. The function is fully and freely documented and no specialized skills are needed to exploit it.

Much more sophisticated attacks are possible. If a PC needs to read the value of a private key in order to use it then any program could theoretically steal it at that point. This is potentially much harder to do and the attacker would need to have considerable knowledge about the (undocumented) internals of the CSPs in order to do this.

Technical details.

This section describes how to add the protection flags at a CryptoAPI or Xenroll level. It should only be of interest to those writing applications.

CryptoAPI functions.

Two functions support the protection flag which is called CRYPT_USER_PROTECTED in wincrypt.h. They are CryptGenKey and CryptImportKey (in the latter case this is not documented). The parameter dwflags needs to be or'ed with CRYPT_USER_PROTECTED in each case. The flag CRYPT_EXPORTABLE will also need to be set if the key will ever need to be exported.

When either function is called with the CRYPT_USER_PROTECTED flag a dialog box appears asking the user to choose the security level. Without the flag low security is silently set.


The Xenroll control is used to handle certificate enrollment with MSIE. It is installed as standard in most newer versions of Windows.

The property GenKeyFlags needs to be set (typically with Icontrol.GenKeyFlags = 3) in the enrollment page.

CRYPT_EXPORTABLE has the value 1 and CRYPT_USER_PROTECTED has the value 2. It will thus be typically set to 3. The default value is (unfortunately) 1.

It will be obvious if the flag is set properly: as soon as the private key is created the dialog box will appear.


This ancient enrollment tool does not allow the CRYPT_USER_PROTECTED flag to be set. Its use is discouraged.