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