Removing Private Key From Certificate

Ok, this may be not be the typical every-day question but I just had a friend ask how to remove a private key from a digital certificate. As it turned out, this is tricky.

Accessing Certificates

PowerShell does provide easy access to all of your installed digital certificates through its cert: drive. So you could easily go ahead and search for any certificate. Let's say you have a certificate installed with 'test' in its subject name. Then this would be a way to get your hands on it:

dir cert:\ -recurse | ? { $_.Subject -like '*test*' }

To pick the first certificate found, you could store the result as an array and pick the first element like this:

$certs = @(dir cert:\ -recurse | ? { $_.Subject -like '*test*' })
$cert = $certs[0]
$cert


    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\Root


Thumbprint                                Subject
----------                                -------
BE20521E22EA2920478F0DC9E172B0F1A431E054  CN=test1

Now, you could access the private key stored in it:

PS> $cert.HasPrivateKey
True
PS> $cert.PrivateKey


PublicOnly           : False
CspKeyContainerInfo  : System.Security.Cryptography.CspKeyContainerInfo
KeySize              : 1024
KeyExchangeAlgorithm :
SignatureAlgorithm   :
http://www.w3.org/2000/09/xmldsig#rsa-sha1
PersistKeyInCsp      : True
LegalKeySizes        : {System.Security.Cryptography.KeySizes}

Unfortunately, there is no way of deleting the private key (at least none I found). This is strange because the oldfashioned CAPICOM used with VBScript did have the ability to remove a private key from a certificate.

After searching MSDN for a while I decided to head another route which coincidentally demonstrates some other useful tricks with certificates. In order to remove the private key, I decided to export the existing certificate without private key, then delete the installed certificate and re-import it from the exported data. Here is how you do that:

Step 1: Get The Certificate

First, make sure you retrieve the certificate you want to strip the private key from. You have seen already how to do that. For example:

$cert = @(dir cert:\CurrentUser\My -recurse | ? { $_.Subject -like '*TEST*' })[0]

You may want to check whether the certificate has a private key at all:

PS> $cert.HasPrivateKey
True

Step 2: Export Certificate Without Private Key

Next, you need to export the certificate without the private key and store it as byte array. That is pretty easy because the certificate has an Export() method you can use:

# export certificate
$bytes = $cert.Export('Cert')

Step 3: Delete Installed Certificate

Now, to get rid of the private key, delete the installed certificate. Of course, before you do that you should be aware of the consequences. You are losing the private key this way (but, after all, that was the idea). You also need admin privileges so be sure to launch PowerShell in elevated mode if you are using UAC.

Deleting a certificate is not just as straight-forward because the cert: provider implemented in PowerShell does not support deleting certificates. So you need to resort to the raw .NET framework methods. First, open the certificate store the certificate resides in (read/write mode) and then remove the certificate:

# delete certificate
$store = new-object system.security.cryptography.x509certificates.x509Store 'My', 'CurrentUser'
$store.Open('ReadWrite')
$store.Remove($cert)

Step 4: Re-Import Certificate w/o Private Key

Since you exported the certificate without private key, all you need to do is re-import the exported certificate. It will be restored but has now no private key anymore. To re-import a certificate, simply create a .NET x509Certificate2Collection object and use its Import() method to read in the byte array you exported earlier. BTW you could also specify a path to a .cert file to import regular filebased certificates this way. Then, add the first element of your certificate collection to the certificate store you deleted the certificate from:

# re-import certificate
$container = new-object
system.security.cryptography.x509certificates.x509certificate2collection
$container.Import($bytes)
$store.Add($container[0])

Step 5: Clean Up

Done. Almost. For sanitary reasons, don't forget to close the certificate store you opened earlier:

$store.close()

That's it: you successfully removed the private key from the certificate you chose. You also learned a couple of new ways to deal with certificate stores using raw .NET objects. Specifically, you have seen how you can delete and export/import certificates. Hope that helped...

Cheers

Tobias

 

 

 


Posted Sep 08 2009, 05:58 PM by Tobias
Concentrated Tech NSoftware Dell Compellent Sponsored by Idera and Concentrated Tech and NSoftware and Dell Compellent
Copyright 2011 PowerShell.com. All rights reserved.