Share via


Install x509 certificate and set private key permissions

Question

Wednesday, April 11, 2012 5:12 PM | 1 vote

Greetings,

I am trying to install a certificate from a .p12 file into the Local Machine - Personal store and set the permissions for Everyone to full control.  I can acheive this manually with mmc on W7 (or mmc + WSE Certificate Tool on XP.)  When attempting with the vb.net code below I get no errors but when I try to access the security tab (Manage Private Keys in mmc on W7, or WSE Certificate Tools on XP) I get the error, "No keys found for certificate!" 

I am using the following code to install the certificate, which seems to be working.  However I am not convinced its placing the private key in the correct place.  I have tried two methods for setting permissions on the private key and the second method, which locates the private key file indicates the private key is being stored under users\my-user-account\..  Should that be a public account

Dim x As New X509Certificate2
x.Import(thisNode.Tag, Keytool_Pass, X509KeyStorageFlags.MachineKeySet And X509KeyStorageFlags.Exportable And X509KeyStorageFlags.PersistKeySet)

s = New X509Store(StoreName.My, StoreLocation.LocalMachine)
s.Open(OpenFlags.ReadWrite)

Dim certCollection As New X509Certificate2Collection
certCollection.Add(x)
s.AddRange(certCollection)

The first method I tried for setting permissions is this, which I found here:
http://stackoverflow.com/questions/425688/how-to-set-read-permission-on-the-private-key-file-of-x-509-certificate-from-ne

Dim id As New Principal.SecurityIdentifier("S-1-1-0")
Dim rsa As Cryptography.RSACryptoServiceProvider = x.PrivateKey

If rsa IsNot Nothing Then
   Dim cspParams = New Cryptography.CspParameters(rsa.CspKeyContainerInfo.ProviderType, _
                                                  rsa.CspKeyContainerInfo.ProviderName, _
                                                  rsa.CspKeyContainerInfo.KeyContainerName) _
                   With {.Flags = Cryptography.CspProviderFlags.UseExistingKey And Cryptography.CspProviderFlags.UseMachineKeyStore, _
                         .CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity}
   cspParams.CryptoKeySecurity.AddAccessRule(New CryptoKeyAccessRule(id, CryptoKeyRights.FullControl, AccessControlType.Allow))

   Dim rsa2 As Cryptography.RSACryptoServiceProvider = New Cryptography.RSACryptoServiceProvider(cspParams)

End If

The second method I tried is this, which I found here:
http://www.codeproject.com/script/Forums/View.aspx?fid=1649&msg=2062983

   Private Shared Sub PlaceInStore(ByVal cert As X509Certificate2)
      Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)

      Try
         store.Open(OpenFlags.ReadWrite)

         If Not store.Certificates.Contains(cert) Then
            store.Add(cert)
         End If

         Dim indexInStore As Integer = store.Certificates.IndexOf(cert)
         cert = store.Certificates(indexInStore)

         AddEveryoneAccessToCertificate(cert)
      Finally
         store.Close()
      End Try
   End Sub

   Private Shared Sub AddEveryoneAccessToCertificate(ByVal cert As X509Certificate2)
      Dim rsa As RSACryptoServiceProvider = TryCast(cert.PrivateKey, RSACryptoServiceProvider)

      If rsa IsNot Nothing Then
         Dim keyfilepath As String = FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName)

         Dim file As New FileInfo((keyfilepath & "\") + rsa.CspKeyContainerInfo.UniqueKeyContainerName)

         Dim fs As FileSecurity = file.GetAccessControl()

         ' Dim account As New NTAccount(user)
         'fs.AddAccessRule(New FileSystemAccessRule(account, FileSystemRights.FullControl, AccessControlType.Allow))

         Dim id As New Principal.SecurityIdentifier("S-1-1-0")
         fs.AddAccessRule(New FileSystemAccessRule(id, FileSystemRights.FullControl, AccessControlType.Allow))

         file.SetAccessControl(fs)
      End If
   End Sub

   Private Shared Function FindKeyLocation(ByVal keyFileName As String) As String
      Dim text1 As String = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
      Dim text2 As String = text1 & "\Microsoft\Crypto\RSA\MachineKeys"
      Dim textArray1 As String() = Directory.GetFiles(text2, keyFileName)
      If textArray1.Length > 0 Then
         Return text2
      End If
      Dim text3 As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
      Dim text4 As String = text3 & "\Microsoft\Crypto\RSA\"
      textArray1 = Directory.GetDirectories(text4)
      If textArray1.Length > 0 Then
         For Each text5 As String In textArray1
            textArray1 = Directory.GetFiles(text5, keyFileName)
            If textArray1.Length <> 0 Then
               Return text5
            End If
         Next
      End If
      Return "Private key exists but is not accessible"
   End Function

Your help and advice is appreciated. 

Thanks,
Glenn

All replies (7)

Wednesday, May 2, 2012 8:08 PM ✅Answered | 1 vote

I am posting to let future problem solvers know of the solution and close this thread out.  Unfortantely this was a very easy fix, if only Eric hadn't cut this thread off and let others continue to work with me, I might have saved the $250 support charge.  The problem was in the initial installation of the certificate.  In the .Import command, I was using And to join the X509KeyStorageFlags, where I should have been using Or. 

This is my final working code to install the certificate.  I am also using my first method, documented above, to set the permissions on the key. 

Dim x As New X509Certificate2x.Import(thisNode.Tag, Keytool_Pass, X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.Exportable Or X509KeyStorageFlags.PersistKeySet)s = New X509Store(StoreName.My, StoreLocation.LocalMachine)s.Open(OpenFlags.ReadWrite)s.Add(x)

I have tested that it works for W7 and XP environments. 

Thanks,
Glenn


Thursday, April 12, 2012 6:36 AM

Hi Glenn,

Welcome to the MSDN Forum.

Let's move on this issue step by step.

Would you like to tell me what it the result of this:

'This part is your code
Dim x As New X509Certificate2
x.Import(thisNode.Tag, Keytool_Pass, X509KeyStorageFlags.MachineKeySet And X509KeyStorageFlags.Exportable And X509KeyStorageFlags.PersistKeySet)

s = New X509Store(StoreName.My, StoreLocation.LocalMachine)
s.Open(OpenFlags.ReadWrite)

Dim certCollection As New X509Certificate2Collection
certCollection.Add(x)
s.AddRange(certCollection)

'your code ends
If s.Certificates.Contains(x) Then
  MsgBox("Add successfully")
Else
  MsgBox("Add Failed")
End If

'Close the Store
s.Close()

I look forward you.

Best regards,

Mike Feng
MSDN Community Support | Feedback to us
Please remember to mark the replies as answers if they help and unmark them if they provide no help.


Thursday, April 12, 2012 5:43 PM

Hi Mike.  Thank you for working with me. 

Per your suggestion I have tried just the install portion of the code and there is a problem.  The code executes successfully but I am sure the issue is here.  I can't say for sure on W7 but on XP when a certificate is installed through the mmc to the Local Machine store, the private key is stored under the All Users profile.  I suspect something similar for W7.    However when I use the code to find the Key File Location it indicates it is under my profile instead of All Users.  I believe this is the issue or at least must be solved first. 

Further proof is that when I go to view the private key permissions for the newly installed certificate (either with mmc > Manage Private Keys for W7, or using WSE Certificate Tool on XP) I get an error indicating the Key doesn't exist.  On W7 the error is "No keys found for certificate" and on XP the error is "Keyset doesn't exist."

I have some screenshots in this word document to visualize what I am describing.  http://www.box.com/s/0e09b3426e73f4b2daf9

Thanks,
Glenn


Friday, April 13, 2012 7:26 AM

Hi Glenn,

Thank you for further information. Based on this, I think I understand you very well.

You have done this:

1. Installed a certificate by your code;

2. set the permission;

3. Retrieve the physical certificate file from disk. But this step fails.

Am I right?

If so, I am performing on this issue now, and I will get back to you as soon as possible. I appreciate your patience.

Best regards,

Mike Feng
MSDN Community Support | Feedback to us
Please remember to mark the replies as answers if they help and unmark them if they provide no help.


Friday, April 13, 2012 3:03 PM

Hi Mike,

In my previous reply I did this:

  1. Install certificate with my code.
  2. use code only to get private key file location.
  3. use UI tool to try and check permissions on the private key.

From this I deduce the certificate install is not working as expected. 

What I ultimately want to do is,

  1. install certificate with my code.
  2. use code to set permissions on private key to allow everyone read or full control

Many thanks,
Glenn


Friday, April 13, 2012 8:42 PM

I have done some further experiments with this portion of code installing the certificate in the store.

x.Import(thisNode.Tag, Keytool_Pass, X509KeyStorageFlags.MachineKeySet And X509KeyStorageFlags.Exportable And X509KeyStorageFlags.PersistKeySet)

The inclusion of either the X509KeyStorageFlags.MachineKeySet or X509KeyStorageFlags.Exportable flags causes the errors when I try to view the certificate key permissions in the mmc / wse certificate tool. 

If I run this instead I am able to view the permissions and in fact the code which applys the permissions for everyone is working. 

x.Import(thisNode.Tag, Keytool_Pass, X509KeyStorageFlags.PersistKeySet)

The problem I have now is the locations which this code is installing the private keys.  Even though I open the Machine Key Store and .Add it there, it saves the key under my user profile.  Here is what I mean...

On Windows 7

Installed in console, key is correctly located:
C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\......

Installed with code, key is incorrectly located:
C:\Users\AppData\Roaming\Microsoft\Crypto\RSA\......

On Windows XP

Installed in console, key is correctly located:
C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys\......

Installed with code, key is incorrectly located:
C:\Documents and Settings\Glenn Holden\Application Data\Microsoft\Crypto\RSA\.......

How do I get the code to install in the right place as the console does it.

Many thanks,
Glenn


Monday, April 16, 2012 3:52 AM

Dear Customer,

 

Your question falls into the paid support category which requires a more in-depth level of support. Please visit the below link to see the various paid support options that are available to better meet your needs.

http://support.microsoft.com/default.aspx?id=fh;en-us;offerprophone   

Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.

Regards,
Eric Yang
Microsoft Online Community Support