简体   繁体   中英

Win10 .NET 3.5 RSACryptoServiceProvider.Dispose throws CryptographicException (ACCESS DENIED or SHARING VIOLATION)

Summary:

The Dispose() method of an RSACryptoServiceProvider object is throwing a CryptographicException for no apparent reason. It appears to be due to some bad programming in the SafeProvHandle class it uses for the underlying provider facility at an unmanaged level, where it tries to work with a temporary file. I suppose my question is mostly "why is this happening?", but of course with the end goal in mind: "how should the programmer best avoid this?"

Context:

An old console application targeting .NET Framework 3.5 for CPU type x86, but being run on Windows 10 x64 Pro (specifically, 10.0.17134 Build 17134). The application does not have any explicit setting in the app config or manifest saying that it supports windows 10. Most things work just fine as they did on Windows XP (!), but this is one of a couple of odd differences I've run into. In fact, I am pretty sure that this used to work just fine (no exception) on Windows 10 a while back, and I'm not certain why this has changed (I know the program used to work, and it would have had to have decrypted one of its settings even as it now tries to but gets this exception). When the same code targets newer frameworks (>= .NET Framework 4), this exception is not thrown, but that is of little value when an application is stuck on 3.5 (as mine is, for reasons unimportant here). This has nothing to do with any other framework -- this happens even in a simple Console application.

The exception:

The exception is thrown when any RSACryptoServiceProvider object is disposed. To isolate it, one can switch from a "using" block to a "try/finally" structure and then use the Clear() method in the finally block (the Dispose() method is made inaccessible, and I am now doing this instead of having a "using" block only so that I can separate out the exact point where the exception is thrown). Normally, the ToString() of the exception gives a string something like this:

System.Security.Cryptography.CryptographicException: The process cannot access the file because it is being used by another process.

   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.SafeProvHandle._FreeCSP(IntPtr pProvCtx)
   at System.Security.Cryptography.SafeProvHandle.ReleaseHandle()
   at System.Runtime.InteropServices.SafeHandle.InternalDispose()
   at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
   at System.Runtime.InteropServices.SafeHandle.Dispose()
   at System.Security.Cryptography.RSACryptoServiceProvider.Dispose(Boolean disposing)
   at System.Security.Cryptography.AsymmetricAlgorithm.System.IDisposable.Dispose()
   at System.Security.Cryptography.AsymmetricAlgorithm.Clear()
   at Baker.Security.SafelyDisposeRCSP(RSACryptoServiceProvider rsa, Boolean encVsDec) in %PROJECT_DIR%\Security.cs:line 95

(I replaced the path to my source with %PROJECT_DIR%).

This one comes right after any successful attempt to Decrypt a string. But after any successful attempt to Encrypt a string, the subsequent call to Clear results in a CryptographicException with the same stack trace but a different Message : Access is denied.

So the exception Message will be either of two forms:

  1. The process cannot access the file because it is being used by another process.
  2. Access is Denied

I have also isolated demonstrative code into a very small project. It shows the same behavior except that at least sometimes both Encrypt and Decrypt result in "Access is Denied". So exactly which one you get when may vary. But I gathered the following details from the original application.

Inappropriateness:

Either way, even my client code in the immediate vicinity around the offending method call is not explicitly trying to do anything with a file, so the exception message is completely unhelpful. In fact, it is misleading, because originally the code was only catching the exception at a much higher level, where my client code was in fact trying to write to or read from an XML settings file, where the user name and password for a database connection are both encrypted -- but that was more a fault of this client code not dealing with the exception at a place where its unrelatedness to the XML settings file would be clear. And who would expect that disposing of an object that does not advertise any use of the file system whatsoever would throw this type of exception? But we can assume that the exceptions are about something internal to how the RSACryptoServiceProvider works.

Low-level cause details:

Since the exception provides no clue as to what file it might be concerned about, the only way I could find out was to simultaneously use "procmon" (from SysInternals ) to track the system calls. As you will see below, it seems that the RSACryptoServiceProvider object is creating a temporary file, perhaps to hold some status information, and then it appears to try to rename (move?) that file to a different temporary file path (both are in AppData under the user's profile). Either that operation or a re-open (create) fails. None of this is described in the documentation so it is difficult to guess what is the actual purpose of this activity. In any case, as shown in the above stack trace, it throws the exception from the Dispose() of the SafeProvHandle (which I assume is just a type-specific derived class extending SafeHandle ), which is one of the two wrapped handle members of an RSACryptoServiceProvider object.

Another oddity here is that whenever I have the code stopped in the debugger where the exception is thrown, the original file has 1318 bytes that are all set to 0, so the file does not actually contain any useful information at all. What is it's purpose? I don't know. Further, I tried doing the rename operation manually and it succeeded without anything unusual happening. So why can't the RSACryptoServiceProvider do it? I don't know, but perhaps doing it from the Windows Explorer involves different system calls.

From procmon, here are some relevant crypto events the process performed leading up to the exception being thrown. I have replaced the path prefix of my home directory with %USERPROFILE% and what I assume is the user SID with %USERSID% to reduce the size of these lines, which were pasted here in tab-separated format but I don't think stackoverflow preserves the tabs:

Time of Day Operation   Path    Result  Detail

4:30:00.1340329 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: S, ShareMode: None, AllocationSize: n/a, OpenResult: Opened
4:30:00.1342753 p.m.    QueryStandardInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS AllocationSize: 4,096, EndOfFile: 1,318, NumberOfLinks: 1, DeletePending: False, Directory: False
4:30:00.1342908 p.m.    WriteFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Offset: 0, Length: 1,318, Priority: Normal
4:30:00.1343567 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:30:00.1346438 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:30:00.1348435 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:30:00.1348624 p.m.    SetDispositionInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   Delete: True
4:30:00.1746923 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:30:00.1749270 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:30:00.1749638 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS CreationTime: 9/19/2018 3:24:30 p.m., LastAccessTime: 1/31/2020 4:29:47 p.m., LastWriteTime: 1/31/2020 4:29:47 p.m., ChangeTime: 1/31/2020 4:29:47 p.m., FileAttributes: D
4:30:00.1749774 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:30:00.1751791 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:30:00.1752765 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:30:00.1753066 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS CreationTime: 1/31/2020 4:30:00 p.m., LastAccessTime: 1/31/2020 4:30:00 p.m., LastWriteTime: 1/31/2020 4:30:00 p.m., ChangeTime: 1/31/2020 4:30:00 p.m., FileAttributes: SA
4:30:00.1754060 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Write Data/Add File, Synchronize, Disposition: Open, Options: , Attributes: n/a, ShareMode: Read, Write, AllocationSize: n/a, OpenResult: Opened
4:30:00.1756804 p.m.    SetRenameInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   ReplaceIfExists: True, FileName: %USERPROFILE%\AppData\Local\Temp\csp2483.tmp
4:30:00.1759340 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:30:00.1760082 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:30:00.1761512 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%    SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:30:00.1761977 p.m.    QueryDirectory  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_* SUCCESS Filter: b501608ed52de158b562a9467bbebaa6_*, 1: b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375
4:30:00.1763073 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, AllocationSize: n/a, OpenResult: Opened
4:30:00.1764712 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%    SUCCESS 
4:30:00.1765216 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:30:00.1766530 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\b501608ed52de158b562a9467bbebaa6_7c965620-2eb3-4093-97cc-3e136a471375  SHARING VIOLATION   Desired Access: Generic Write, Read Attributes, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: S, ShareMode: None, AllocationSize: n/a

A short while later, another exception is thrown upon disposing of another instance of RSACryptoServiceProvider that was used to decrypt. The object-specific file name used has changed to a different identifier, but otherwise the operations and their results follow the same pattern, and the same exception message results.

A little while later, the program goes to encrypt a string. This time, the events are slightly different: the last straw is simply ACCESS DENIED instead of SHARING VIOLATION:

4:49:56.3149713 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: S, ShareMode: None, AllocationSize: n/a, OpenResult: Opened
4:49:56.3151740 p.m.    QueryStandardInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS AllocationSize: 4,096, EndOfFile: 1,318, NumberOfLinks: 1, DeletePending: False, Directory: False
4:49:56.3151919 p.m.    WriteFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Offset: 0, Length: 1,318, Priority: Normal
4:49:56.3152521 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3155032 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3156288 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:49:56.3156477 p.m.    SetDispositionInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   Delete: True
4:49:56.3542999 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3546286 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3546780 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS CreationTime: 9/19/2018 3:24:30 p.m., LastAccessTime: 1/31/2020 4:48:02 p.m., LastWriteTime: 1/31/2020 4:48:02 p.m., ChangeTime: 1/31/2020 4:48:02 p.m., FileAttributes: D
4:49:56.3546974 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:49:56.3549030 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3550494 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:49:56.3550785 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS CreationTime: 1/31/2020 4:49:56 p.m., LastAccessTime: 1/31/2020 4:49:56 p.m., LastWriteTime: 1/31/2020 4:49:56 p.m., ChangeTime: 1/31/2020 4:49:56 p.m., FileAttributes: SA
4:49:56.3551978 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Write Data/Add File, Synchronize, Disposition: Open, Options: , Attributes: n/a, ShareMode: Read, Write, AllocationSize: n/a, OpenResult: Opened
4:49:56.3555954 p.m.    SetRenameInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   ReplaceIfExists: True, FileName: %USERPROFILE%\AppData\Local\Temp\cspA6D4.tmp
4:49:56.3559595 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:49:56.3560327 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3562301 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%    SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3562921 p.m.    QueryDirectory  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_* SUCCESS Filter: e0b0f7743bf0ba1af76a0a9f21e83437_*, 1: e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375
4:49:56.3564453 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, AllocationSize: n/a, OpenResult: Opened
4:49:56.3567585 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%    SUCCESS 
4:49:56.3568211 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3569981 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: S, ShareMode: None, AllocationSize: n/a, OpenResult: Opened
4:49:56.3572681 p.m.    QueryStandardInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS AllocationSize: 4,096, EndOfFile: 1,318, NumberOfLinks: 1, DeletePending: False, Directory: False
4:49:56.3572967 p.m.    WriteFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Offset: 0, Length: 1,318, Priority: Normal
4:49:56.3573971 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3576710 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3578194 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:49:56.3578848 p.m.    SetDispositionInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   Delete: True
4:49:56.3878718 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 
4:49:56.3881894 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3882383 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS CreationTime: 9/19/2018 3:24:30 p.m., LastAccessTime: 1/31/2020 4:48:02 p.m., LastWriteTime: 1/31/2020 4:48:02 p.m., ChangeTime: 1/31/2020 4:48:02 p.m., FileAttributes: D
4:49:56.3882567 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:49:56.3884424 p.m.    CreateFile  %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Desired Access: Read Attributes, Delete, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
4:49:56.3885627 p.m.    QueryAttributeTagFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS Attributes: SA, ReparseTag: 0x0
4:49:56.3885898 p.m.    QueryBasicInformationFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS CreationTime: 1/31/2020 4:49:56 p.m., LastAccessTime: 1/31/2020 4:49:56 p.m., LastWriteTime: 1/31/2020 4:49:56 p.m., ChangeTime: 1/31/2020 4:49:56 p.m., FileAttributes: SA
4:49:56.3887004 p.m.    CreateFile  %USERPROFILE%\AppData\Local\Temp    SUCCESS Desired Access: Write Data/Add File, Synchronize, Disposition: Open, Options: , Attributes: n/a, ShareMode: Read, Write, AllocationSize: n/a, OpenResult: Opened
4:49:56.3889472 p.m.    SetRenameInformationFile    %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  ACCESS DENIED   ReplaceIfExists: True, FileName: %USERPROFILE%\AppData\Local\Temp\csp2011.tmp
4:49:56.3891731 p.m.    CloseFile   %USERPROFILE%\AppData\Local\Temp    SUCCESS 
4:49:56.3892245 p.m.    CloseFile   %USERPROFILE%\AppData\Roaming\Microsoft\Crypto\RSA\%USERSID%\e0b0f7743bf0ba1af76a0a9f21e83437_7c965620-2eb3-4093-97cc-3e136a471375  SUCCESS 

The exception thrown is likewise different:

System.Security.Cryptography.CryptographicException: Access is denied.

   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.SafeProvHandle._FreeCSP(IntPtr pProvCtx)
   at System.Security.Cryptography.SafeProvHandle.ReleaseHandle()
   at System.Runtime.InteropServices.SafeHandle.InternalDispose()
   at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
   at System.Runtime.InteropServices.SafeHandle.Dispose()
   at System.Security.Cryptography.RSACryptoServiceProvider.Dispose(Boolean disposing)
   at System.Security.Cryptography.AsymmetricAlgorithm.System.IDisposable.Dispose()
   at System.Security.Cryptography.AsymmetricAlgorithm.Clear()
   at Baker.Security.SafelyDisposeRCSP(RSACryptoServiceProvider rsa, Boolean encVsDec) in %PROJECT_DIR%\Security.cs:line 95

Notice, however, that it is still arising from disposing of the SafeProvHandle within the RSACryptoProviderService object.

In both cases, the same 2 operations are performed ( SetDispositionInformationFile , SetRenameInformationFile ) with the same failure (ACCESS DENIED), but in the former case (after doing Decrypt), this pair of operations is only done once and does not result in an exception being thrown, whereas in the latter case (after doing Encrypt) the pair of operations is performed twice, and then the exception is thrown. In the former case, an exception is not thrown until after a subsequent failure (SHARING VIOLATION) that occurs upon attempting to CreateFile (when it already exists) for writing without sharing. It is possible, of course, that if the post-Decrypt Dispose had not gotten that SHARING VIOLATION, it might also have eventually thrown an exception for the ACCESS DENIED failure[s] -- but why speculate? As I mentioned before, my simplified demonstrative example code can (at times) only get the ACCESS DENIED failure, but I'm not sure why it should differ.

Conclusion:

Let me suggest that this is some sort of defect in the Crypto code in the Windows 10 implementation of .NET 3.5. Or, is there is some environmental condition that is interfering with its proper operation? If so, what might that be? And in that case, then the defect might be in the documentation, which provides no clue that any of this sort thing should even be happening. But Microsoft has long since abandoned .NET 3.5.

Example Code:

This code for a Console project already intercepts the bad exceptions, which very well may be the best thing someone can do, here. At least it lets you see them. Remember to target .NET Framework 3.5 to see the problem.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Security.Cryptography;

namespace Scratch
{
    class ProgramBad
    {
        static void Main(string[] args) {
            try { Environment.ExitCode = SecurityBad.QuickEncDecTest(args.Length>0 ? args[1] : null); }
            catch (Exception exc) { 
                Console.WriteLine("WARNING: unhandled " + exc.ToString());
                Environment.ExitCode = 1;
            } finally {
                Console.WriteLine("Press return to end program...");
                Console.ReadLine();
            }
        }
    }

    public static class SecurityBad {
        public static int QuickEncDecTest(string original = null) {
            var key = CreateRandomRSAKey();
            original = original ?? "My super-secret password";
            var encrypted = EnCrypt(original, key);
            var decrypted = DeCrypt(encrypted, key);
            int retval = decrypted.CompareTo(original);
            if (retval == 0) Console.WriteLine("Encrypt/Decrypt round trip resulted in original. PASSED!");
            else Console.WriteLine("Encrypt/Decrypt failed to result in original.    FAILED!"
                                       + Environment.NewLine
                                       + "Original '" + original + "' != round-trip '" + decrypted + "'");
            return retval;
        }

        public static byte[] CreateRandomRSAKey() {
            RSACryptoServiceProvider rsa = null;
            try {   rsa = new RSACryptoServiceProvider();
                    return rsa.ExportCspBlob(true);
            } finally {
                try { rsa.Clear(); }
                catch (Exception ce) {
                    Console.WriteLine(ce.ToString());
                }
            }
        }

        public static byte[] EnCrypt(string str, byte[] key) {
            RSACryptoServiceProvider rsa = null; // This code originally had a using(var rsa = new ...) block, but see comments in SafelyDisposeRCSP.
            try {
                rsa = new RSACryptoServiceProvider();
                rsa.ImportCspBlob(key);
                var bytConvertor = new UTF8Encoding();
                var plainData = bytConvertor.GetBytes(str);
                return rsa.Encrypt(plainData, false);
            }
            finally { if (rsa != null)
                        try { rsa.Clear(); }
                        catch (Exception ce) { Console.WriteLine(ce.ToString()); }
            }
        }

        public static string DeCrypt(byte[] alldata, byte[] key) {
            RSACryptoServiceProvider rsa = null;
            try {
                var bytConvertor = new UTF8Encoding();
                rsa = new RSACryptoServiceProvider();
                rsa.ImportCspBlob(key);
                return bytConvertor.GetString(rsa.Decrypt(alldata, false));
            } catch (Exception e) {
                Console.WriteLine("Warning: Decrypting a string failed due to the following " + e.GetType().Name);
                Console.WriteLine(e);
                return String.Empty;
            }
            finally {   if (rsa != null)
                            try { rsa.Clear(); }
                            catch (Exception ce) { Console.WriteLine(ce.ToString()); }
            }
        }
    }
}

My originally-assumed "answer" is that Microsoft has probably just broken this functionality but don't care to fix it because they no longer support .NET Framework 3.5. But I have no official proof of anything.

It seems to not happen on Windows XP, nor have I seen it on Windows 7 (both being in the recent past); it does not even happen on Windows 10 when targeting a newer version (.NET Framework >= 4.0). I have not tried to verify which builds of Windows 10 have this problem, whether it occurs in Windows 8 (does anybody even use Win8?), or whether some patch to .NET 3.5 has also broken this functionality even on Windows 7 since I last tried it there, etc. But it also seems unlikely that there is anything specifically wrong with the systems I have seen this problem on (environmental causes), so I'll assume it's a defect.

As a result, then, you have to explicitly break apart any "using" block for the RSACryptoServiceProvider object and explicitly dispose of it (with Clear() ) in a "finally" block instead so that you can catch these exceptions and either ignore them or log them as desired. Or don't target .NET 3.5. :-)

I'll put this here as an answer in case it helps anyone or no one offers anything better. Here is a pretty obvious helper function that you could use anywhere you make use of an RSACryptoServiceProvider:

        private static void SafelyDisposeRCSP(RSACryptoServiceProvider rsa, string attemptedOperation)
        {
            // This began happening in Windows 10, inexplicably, with a non-sensical error,
            // "The process cannot access the file because it is being used by another process." or, "Access is denied";
            // procmon determined that the system call generating this exception was all
            // about a file that the rsa object apparently uses under %USERPROFILE%\AppData\... when it tries to rename/move it from
            // Roaming/Microsoft/Crypto/RSA/%USERGUID%/%OBJECTGUID% to Local/Temp/csp%XXXX%.tmp, as if it failed to rename it because
            // it didn't have permission to do so (makes no sense).
            // But why should anyone care?  We're done with the rsa object by now!  
            // Here we just assume this is a serious defect somewhere in the framework.

            // So here we just swallow the exception with a log message, 
            // and that's the only reason for not doing a using block;
            // we could not separate such bogus disposal exceptions from valid exceptions with a "using" block.

            if (rsa != null)
                try { rsa.Clear(); }
                catch (Exception ce) {
                    var msg = "After successfully " + attemptedOperation + ", the RSACryptoServiceProvider.Dispose() method threw a "
                                + ce.GetType().Name;
                    if (ce.Message.StartsWith("Access is denied") ||
                        ce.Message.StartsWith("The process cannot access the file because it is being used by another process."))
                        // note: the ce.Message usually ends with an Environment.NewLine, so we don't need to insert one:
                        Console.WriteLine(msg + ":  " + ce.Message + "Working around this defect in the .NET framework by ignoring the bogus exception.");
                    else {   // Haven't really seen this, not sure why it could happen, but maybe it is because of using a different language?
                        Console.WriteLine("Warning: " + msg);
                        Console.WriteLine(ce.ToString()); // we will log the full stack trace just in case it is important to investigate
                    }
                }
        }

You could also conjoin that disjunctive condition (in the "if" statement) with ce is CryptographicException , but I saw little point in adding that.

Example of using it:

        public static byte[] CreateRSAKey() {
            RSACryptoServiceProvider rcsp = null;
            try {
                rcsp = new RSACryptoServiceProvider();
                return rcsp.ExportCspBlob(true);
            } finally {
                SafelyDisposeRCSP(rcsp, "exporting key");
            }
        }

        // ...

        public static ... Encrypt(...) {
        // ...
            } finally {
                SafelyDisposeRCSP(rcsp, "encrypting string");
            }
        // ...

        // ... etc. ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM