Bug 19274 - ProtectedData.Protect(...) fails when ~/.config/.mono/keypairs directory is created by WebRequest.
Summary: ProtectedData.Protect(...) fails when ~/.config/.mono/keypairs directory is c...
Alias: None
Product: Class Libraries
Classification: Mono
Component: System.Security ()
Version: master
Hardware: PC Mac OS
: High normal
Target Milestone: Untriaged
Assignee: marcos.henrich
: 21800 ()
Depends on:
Reported: 2014-04-24 07:25 UTC by Matt Ward
Modified: 2014-09-23 09:07 UTC (History)
8 users (show)

Is this bug a regression?: ---
Last known good build:

NUnit test for ProtectedData.Protect(…) (1.60 KB, application/octet-stream)
2014-04-24 07:25 UTC, Matt Ward

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Matt Ward 2014-04-24 07:25:55 UTC
Created attachment 6646 [details]
NUnit test for ProtectedData.Protect(…)

When the ~/.config/.mono/keypairs directory is empty the System.Security.Cryptography.ProtectedData.Protect (…) fails with the following exception:

System.Security.Cryptography.CryptographicException: Data protection failed. ---> System.Security.Cryptography.CryptographicException: Improperly protected user's key pairs in '/Users/username/.config/.mono/keypairs'.
  at Mono.Security.Cryptography.KeyPairPersistence.get_UserPath () [0x000d0] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/Mono.Security/Mono.Security.Cryptography/KeyPairPersistence.cs:232 
  at Mono.Security.Cryptography.KeyPairPersistence.get_Filename () [0x0007b] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/Mono.Security/Mono.Security.Cryptography/KeyPairPersistence.cs:141 
  at Mono.Security.Cryptography.KeyPairPersistence.Load () [0x00000] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/Mono.Security/Mono.Security.Cryptography/KeyPairPersistence.cs:167 
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) [0x0000c] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs:113 
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) [0x00023] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs:85 
  at Mono.Security.Cryptography.ManagedProtection.GetKey (DataProtectionScope scope) [0x0002d] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/System.Security/Mono.Security.Cryptography/ManagedProtection.cs:254 
  at Mono.Security.Cryptography.ManagedProtection.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) [0x00156] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/System.Security/Mono.Security.Cryptography/ManagedProtection.cs:106 
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) [0x00030] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/System.Security/System.Security.Cryptography/ProtectedData.cs:61 
  --- End of inner exception stack trace ---
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) [0x0004a] in /Volumes/build-root-ramdisk/mono-3.4.0/mcs/class/System.Security/System.Security.Cryptography/ProtectedData.cs:65 
  at KeyPairsTests.KeyPairsTests.EncryptStringAfterMakingHttpRequest () [0x00034] in /Users/matt/Projects/KeyPairsTests/KeyPairsTests.cs:34 

Test environment:

Mono JIT compiler version 3.4.0 ((no/75c5d4a Mon Mar 31 15:45:41 EDT 2014)
Mac OSX: 10.8.5

To reproduce:

An empty key pair directory will be generated by Mono if you read a response back from a https url using:

	var request = (HttpWebRequest)WebRequest.Create ("https://www.google.com");
	request.GetResponse ();

Then using the ProtectedData.Protect(…) will fail.

MUnit test case is attached which reproduces the CryptographicException. Pre-requisite is to delete the ~/.config/.mono/keypairs directory before running the test.
Comment 1 Matt Ward 2014-04-24 09:15:34 UTC
This has nothing to do with the directory being empty but seems to be a permission problem with the directory that is created when reading a response back from the WebRequest. Changed the bug title to reflect this.


    drwxr-xr-x  2 username  staff   68 24 Apr 12:30 keypairs


    drwx------  3 username  staff  102 24 Apr 14:14 keypairs
Comment 2 xambugz 2014-05-06 09:42:11 UTC
chmod 700 ~/.config/.mono/keypairs

After doing this, the cryptographic exception is not thrown anymore.  But the 700 permissions should happen automatically.  Should not require manual intervention.
Comment 5 Jérémie Laval 2014-08-04 15:29:45 UTC
*** Bug 21800 has been marked as a duplicate of this bug. ***
Comment 6 marcos.henrich 2014-08-07 06:35:04 UTC
I tried to reproduce the issue but it appears to be fixed in master.
Comment 7 Rodrigo Kumpera 2014-08-07 08:50:12 UTC

Bug #21800 mentions is happening very recently, it's worth checking the test case they have there.
Comment 8 marcos.henrich 2014-08-07 10:06:46 UTC
If the path ~/.config/.mono/keypairs somehow already exists with the wrong permissions then the exception will be thrown.

Should we try to change the folder permissions before throwing the exception?

Link to the code throwing the exceptions:
Comment 9 Rodrigo Kumpera 2014-08-11 14:17:57 UTC
Sebastien, wouldn't fixing the permissions after the fact be a solution to avoid this unreproducible problems?
Comment 10 xambugz 2014-08-11 16:12:07 UTC
Yes, the ~/.config/.mono/keypairs directory is safely assumed to be mono-specific (because .mono), and yes it's possible to enter a permanently bad state, where the bug-affected version of mono will continue poisoning later versions and causing them to throw exceptions (if the directory gets created with bad permissions on let's say 3.4.0, then even after upgrading to 3.6.0 the directory still has bad permissions).  So yes it's a good idea moving forward (hopefully 3.6.0?) to automatically detect and correct the problem condition, by doing the chmod instead of throwing the exception.

In the present state, it is the responsibility of the application to automatically check, detect, and correct, the permissions of that directory before using ProtectedData.  This should not be necessary in userland.
Comment 11 Sebastien Pouliot 2014-08-11 16:17:12 UTC
@Kumpara Yes, but it would be a partial solution.

You cannot be sure that the application will have the permission to change (fix) the permission. The code can be changed to "try" to fix them (if possible) but should continue* to throw if they cannot be fixed.

* otherwise you're effectively (and silently) exposing your keys to every process running on your system (e.g. shared web usage).
Comment 12 Rodrigo Kumpera 2014-08-11 18:25:43 UTC

Could you change it to follow Sebastien's suggestion?
Comment 13 marcos.henrich 2014-08-12 14:21:19 UTC
The following pull request implements what was suggested:
Comment 14 marcos.henrich 2014-09-23 09:07:20 UTC
Fixed in master 2d52cec1ce23c71b2729b4a4e20e5b40e8c54ae3.

Only tested in mac.

It should be tested in others OS/filesystems by doing:

chmod 777 ~/.config/.mono/keypairs

And checking that after running:

var request = (HttpWebRequest)WebRequest.Create ("https://www.google.com");
request.GetResponse ();

The permission of ~/.config/.mono/keypairs are set to 700.