Win10. NET 3.5 RSACryptoServiceProvider.Dispose бросает CryptographicException (доступ запрещен или нарушение обмена) - PullRequest
0 голосов
/ 04 февраля 2020

Сводка:

Метод Dispose () объекта RSACryptoServiceProvider выбрасывает CryptographicException без видимой причины. Похоже, что это происходит из-за плохого программирования в классе SafeProvHandle, который он использует для базового провайдера на неуправляемом уровне, где он пытается работать с временным файлом. Я предполагаю, что мой вопрос в основном «почему это происходит?», Но, конечно, с конечной целью: «как программисту лучше всего этого избежать?»

Контекст:

Старое консольное приложение, предназначенное для NET Framework 3.5 для процессора типа x86, но работающее на Windows 10 x64 Pro (в частности, 10.0.17134 Build 17134). Приложение не имеет каких-либо явных настроек в конфигурации или манифесте приложения, говорящих о том, что оно поддерживает windows 10. Большинство вещей работают просто отлично, как и на Windows XP (!), Но это одно из пары странных отличий Я столкнулся с. На самом деле, я почти уверен, что это раньше работало просто (без исключения) на Windows 10 некоторое время назад, и я не уверен, почему это изменилось (я знаю, что программа работала, и она будет иметь должен был расшифровать один из его параметров, даже если он пытается это сделать, но получает это исключение). Когда тот же код предназначен для более новых платформ (> =. NET Framework 4), это исключение не генерируется, но оно имеет небольшую ценность, когда приложение застряло на 3.5 (как у меня, по причинам, неважным здесь). Это не имеет никакого отношения к любой другой платформе - это происходит даже в простом консольном приложении.

Исключение:

Исключение выдается, когда какой-либо объект RSACryptoServiceProvider имеет расположены. Чтобы изолировать его, можно перейти от блока «using» к структуре «try / finally», а затем использовать метод Clear () в блоке finally (метод Dispose () сделан недоступным, и Сейчас я делаю это вместо того, чтобы иметь блок «using» только для того, чтобы я мог выделить точную точку, в которую выдается исключение). Обычно ToString () исключения дает строку примерно так:

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

(я заменил путь к своему источнику на% PROJECT_DIR%).

Этот появляется сразу после любой успешной попытки расшифровать строку. Но после любой успешной попытки зашифровать строку последующий вызов Clear приводит к возникновению CryptographicException с той же трассировкой стека, но с другим Message : доступ запрещен.

Поэтому сообщение об исключении будет быть одной из двух форм:

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

Я также выделил демонстрационный код в очень маленький проект. Он показывает то же поведение за исключением того, что по крайней мере иногда и Encrypt и Decrypt приводят к «Доступ запрещен». Так что именно то, что вы получаете, когда может меняться. Но я собрал следующие детали из исходного приложения:

Неуместность:

В любом случае, даже мой клиентский код в непосредственной близости от вызова метода-нарушителя не является явным пытается что-то сделать с файлом, поэтому сообщение об исключении совершенно бесполезно. На самом деле, это вводит в заблуждение, потому что изначально код перехватывал исключение только на гораздо более высоком уровне, когда мой клиентский код был на самом деле пытался записывать или читать из файла настроек XML, где имя пользователя и пароль для соединения с базой данных оба зашифрованы, но это была скорее ошибка этого клиентского кода, не имеющего отношения к исключению в месте, где его несвязанность с файлом настроек XML будет ясна. И кто бы мог ожидать, что избавление от объекта, не рекламирующего какое-либо использование файловой системы, вызовет этот тип исключения? Но мы можем предположить, что исключения относятся к чему-то внутреннему к тому, как работает RSACryptoServiceProvider.

Подробности причины низкого уровня:

Поскольку исключение не дает представления о том, к какому файлу оно может относиться, единственный способ, который я смог выяснить, - это одновременно использовать procmon (из SysInternals ) для отслеживания системных вызовов. Как вы увидите ниже, кажется, что объект RSACryptoServiceProvider создает временный файл, возможно, для хранения некоторой информации о состоянии, а затем он пытается переименовать (переместить?) Этот файл в другой временный путь к файлу (оба находятся в AppData). под профилем пользователя). Либо эта операция, либо повторное открытие (создание) завершается неудачей. Ничего из этого не описано в документации, поэтому трудно догадаться, какова реальная цель этого действия. В любом случае, как показано в приведенной выше трассировке стека, он выдает исключение из Dispose () SafeProvHandle (который, как я предполагаю, является всего лишь производным классом со спецификацией типа c, расширяющим SafeHandle ), который является одним из двух обернутых элементов дескриптора объекта RSACryptoServiceProvider.

Еще одна странность заключается в том, что всякий раз, когда у меня останавливается код в отладчике, где выбрасывается исключение, исходный файл имеет 1318 все байты установлены в 0, поэтому файл фактически не содержит никакой полезной информации. Какова его цель? Я не знаю. Кроме того, я попытался выполнить операцию переименования вручную, и она прошла успешно, и ничего необычного не произошло. Так почему же RSACryptoServiceProvider не может это сделать? Я не знаю, но, возможно, выполнение этого из Windows Explorer включает в себя различные системные вызовы.

Из procmon, вот некоторые соответствующие крипто-события, которые выполнял процесс, приводящий к созданию исключения. Я заменил префикс пути моего домашнего каталога на% USERPROFILE%, и я предполагаю, что это SID пользователя с% USERSID%, чтобы уменьшить размер этих строк, которые были вставлены здесь в формате с разделителями табуляции, но я не думаю, что переполнение стека сохраняет вкладки:

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

Вскоре после этого возникает другое исключение при удалении другого экземпляра RSACryptoServiceProvider, который использовался для расшифровки. Используемое имя файла c с указанием объекта изменилось на другой идентификатор, но в противном случае операции и их результаты следуют одному и тому же шаблону, и результат сообщения об одном и том же исключении.

Чуть позже программа переходит зашифровать строку. На этот раз события немного отличаются: последняя капля просто ЗАПРЕЩЕНА ДО ДОСТУПА, а не ОБЩАЯ НАРУШЕНИЕ:

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 

Исключение также отличается:

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

Обратите внимание, однако, что он все еще возникает в результате удаления SafeProvHandle в объекте RSACryptoProviderService.

В обоих случаях выполняются одни и те же 2 операции ( SetDispositionInformationFile , SetRenameInformationFile ) с тот же сбой (ACCESS DENIED), но в первом случае (после выполнения Decrypt) эта пара операций выполняется только один раз и не приводит к возникновению исключения, тогда как в последнем случае (после выполнения Encrypt) пара операций выполняется дважды, а затем выдается исключение. В первом случае исключение не генерируется до тех пор, пока не произойдет последующий сбой (НАРУШЕНИЕ ОБМЕНА), возникающий при попытке создать файл (если он уже существует) для записи без совместного использования. Разумеется, возможно, что если после дешифрования распоряжение не получило такого совместного нарушения, оно также могло бы в конечном итоге вызвать исключение для отказа [ACCESS DENIED] - но зачем спекулировать? Как я упоминал ранее, мой упрощенный демонстрационный пример кода может (время от времени) только получать ошибку ACCESS DENIED, но я не уверен, почему она должна отличаться.

Вывод:

Позвольте мне предположить, что это какой-то дефект в коде Crypto в реализации Windows 10. NET 3.5. Или есть какое-то состояние окружающей среды, которое мешает его правильной работе? Если это так, что это может быть? И в этом случае дефект может быть в документации, которая не дает ни малейшего представления о том, что подобные вещи должны происходить. Но Microsoft уже давно отказалась. NET 3.5.

Пример кода:

Этот код для проекта консоли уже перехватывает недопустимые исключения, которые вполне могут быть лучшее, что кто-то может сделать, здесь. По крайней мере, это позволяет вам видеть их. Не забудьте нацелиться. NET Framework 3.5, чтобы увидеть проблему.

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()); }
            }
        }
    }
}

1 Ответ

0 голосов
/ 05 февраля 2020

Мой первоначально предполагаемый «ответ» заключается в том, что Microsoft, вероятно, только что нарушила эту функциональность, но не хочет исправлять ее, потому что они больше не поддерживают. NET Framework 3.5. Но у меня нет никаких официальных доказательств чего-либо.

Кажется, это не происходит на Windows XP, и я не видел этого на Windows 7 (оба в недавнем прошлом); это даже не происходит в Windows 10 при нацеливании на более новую версию (. NET Framework> = 4.0). Я не пытался проверить, какие сборки Windows 10 имеют эту проблему, возникает ли она в Windows 8 (кто-нибудь вообще использует Win8?) Или какой-либо патч для. NET 3.5 также нарушил эту функциональность на Windows 7 с тех пор, как я в последний раз там пробовал, и т.д. c. Но также маловероятно, что с системами, в которых я видел эту проблему (причины окружающей среды), что-то конкретно не так, поэтому я предполагаю, что это дефект.

В результате, вам придется явно разбить любой блок "using" для объекта RSACryptoServiceProvider и явно удалить его (с помощью Clear () ) в блок "finally", чтобы вы могли перехватывать эти исключения и либо игнорировать их, либо регистрировать их как желательно. Или не цель. NET 3.5. : -)

Я поставлю это здесь в качестве ответа на случай, если это кому-нибудь поможет или никто не предложит ничего лучше. Вот довольно очевидная вспомогательная функция, которую вы можете использовать везде, где вы используете 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
                    }
                }
        }

Вы также можете соединить это дизъюнктивное условие (в операторе "if") с ce is CryptographicException, но я не видел смысла в добавлении этого.

Пример использования:

        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. ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...