{"id":15581,"date":"2011-02-16T00:01:00","date_gmt":"2011-02-16T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/02\/16\/use-powershell-and-net-to-find-expired-certificates\/"},"modified":"2011-02-16T00:01:00","modified_gmt":"2011-02-16T00:01:00","slug":"use-powershell-and-net-to-find-expired-certificates","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-and-net-to-find-expired-certificates\/","title":{"rendered":"Use PowerShell and .NET to Find Expired Certificates"},"content":{"rendered":"<p><b><\/b><\/p>\n<p><b>Summary<\/b>: Learn how to use Windows PowerShell and Microsoft .NET classes to find expired certificates on local and remote computers.<\/p>\n<p><img decoding=\"async\" height=\"34\" width=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" align=\"left\" alt=\"Hey, Scripting Guy! Question\" border=\"0\" title=\"Hey, Scripting Guy! Question\" \/><\/p>\n<p>&nbsp; Hey, Scripting Guy! How can I use Windows PowerShell and the .NET Framework classes to work with certificates?<\/p>\n<p>&#8212; PB<\/p>\n<p>&nbsp;<\/p>\n<p><img decoding=\"async\" height=\"34\" width=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" align=\"left\" alt=\"Hey, Scripting Guy! Answer\" border=\"0\" title=\"Hey, Scripting Guy! Answer\" \/> Hello PB, <\/p>\n<p>Microsoft Scripting Guy Ed Wilson here. We continue Guest Blogger Week today with Boe Prox, currently a senior systems administrator with BAE Systems. He has been in the IT industry since 2003 and has spent the past three years working with VBScript and <a href=\"http:\/\/technet.microsoft.com\/en-us\/scriptcenter\/powershell.aspx\">Windows PowerShell<\/a> and now looks to script whatever he can, whenever he can. He is also a moderator on the <a href=\"http:\/\/bit.ly\/scriptingforum\">Hey, Scripting Guy! Forum<\/a>. You can check out his blog at <a href=\"http:\/\/boeprox.wordpress.com\">http:\/\/boeprox.wordpress.com<\/a> and also see his current project, the <a href=\"http:\/\/poshwsus.codeplex.com\/\">WSUS Administrator module<\/a>, just recently published on CodePlex. <\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4431.HSG-2-16-11-1_7442C419.jpg\"><img decoding=\"async\" height=\"440\" width=\"702\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8712.HSG-2-16-11-1_thumb_5C0F33F1.jpg\" alt=\"Photo of Boe Prox\" border=\"0\" title=\"Photo of Boe Prox\" style=\"border-bottom: 0px;border-left: 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" \/><\/a><\/p>\n<p>PKI certificates are one of those things that most system administrators know about but probably never spend a lot of time with until something goes wrong. Usually a certificate expires and causes some sort of interruption to a service in which users or the boss are asking what is happening.<\/p>\n<p>What I am first going to do is to show you how to connect to a local or remote computer and view their certificates using the .NET class, <strong>System.Security.Cryptography.X509Certificates.X509Store<\/strong>. <\/p>\n<p>There are two locations that you can connect to:<\/p>\n<ul>\n<li><strong>LocalMachine<\/strong>: Global certificates that affect the computer and user accounts such as machine certificates for network access or SSL certificates for website access. <\/li>\n<li><strong>CurrentUser<\/strong>: Certificates that are user specific, such as smart card certificates and certificates used for encryption.<\/li>\n<\/ul>\n<p>Of these two certificate store locations, only <strong>LocalMachine<\/strong> can be accessed remotely via the .NET class. Attempting to access <strong>CurrentUser<\/strong> will result in an &ldquo;Access Denied&rdquo; message because of security reasons.<\/p>\n<p>Connecting to the local machine <strong>LocalMachine<\/strong> certificate store requires a few lines of code in order to gain access to the certificates:<\/p>\n<blockquote>\n<p>$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(&#8220;My&#8221;,&#8221;LocalMachine&#8221;)<\/p>\n<p>$store.Open(&#8220;ReadOnly&#8221;)<\/p>\n<p>$store.Certificates<\/p>\n<\/blockquote>\n<p>The first thing we see here is that we are creating the object using the X509 class and calling two values that represent the store name and store location that we will be connecting to. There are several store names that are available to view, as shown in the following image.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1778.HSG-2-16-11-2_2B98AF7B.jpg\"><img decoding=\"async\" height=\"273\" width=\"504\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1856.HSG-2-16-11-2_thumb_1BC8F4AA.jpg\" alt=\"Image of available store names\" border=\"0\" title=\"Image of available store names\" style=\"border-bottom: 0px;border-left: 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" \/><\/a><\/p>\n<p>For this article, we are going to focus on the <strong>LocalMachine<\/strong> store name as that is where we will be looking for web SSL certificates, domain controller certificates, and machine certificates.<\/p>\n<p>The next line of code has us setting the flag that we will be using when we open up the store. Because we are only viewing the certificates on the store and nothing else, we opt to go with the <b>ReadOnly<\/b> flag. There are other flags available when opening the store, as shown in the following image<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8304.HSG-2-16-11-3_323B9331.jpg\"><img decoding=\"async\" height=\"202\" width=\"504\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8306.HSG-2-16-11-3_thumb_1722C423.jpg\" alt=\"Image of available flags\" border=\"0\" title=\"Image of available flags\" style=\"border-bottom: 0px;border-left: 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" \/><\/a><\/p>\n<p>Finally, we are able to view the machine certificates within <b>store<\/b> in the third line, as shown in the following image.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0361.HSG-2-16-11-4_06C74C6A.jpg\"><img decoding=\"async\" height=\"66\" width=\"604\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0842.HSG-2-16-11-4_thumb_0B5D5724.jpg\" alt=\"Image of viewing machine certificates\" border=\"0\" title=\"Image of viewing machine certificates\" style=\"border-bottom: 0px;border-left: 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" \/><\/a><\/p>\n<p>If anyone has used the <b>Certificate<\/b> provider in Windows PowerShell before, this will look very familiar. In fact, look at the output of the <b>each<\/b> command I run for both the .NET class and using the <b>Certificate<\/b> provider:<\/p>\n<blockquote>\n<p>PS C:\\Users\\boe&gt; $store = New-Object System.Security.Cryptography.X509Certificates.X509Store(&#8220;My&#8221;,&#8221;LocalMachine&#8221;)<\/p>\n<p>PS C:\\Users\\boe&gt; $store.Open(&#8220;ReadOnly&#8221;)<\/p>\n<p>PS C:\\Users\\boe&gt; ($store.certificates | Select -First 1).gettype() | Format-Table Name, BaseType -auto<\/p>\n<p>Name BaseType<\/p>\n<p>&#8212;- &#8212;&#8212;&#8211;<\/p>\n<p>X509Certificate2 System.Security.Cryptography.X509Certificates.X509Certificate<\/p>\n<p>PS C:\\Users\\boe&gt; (GCI cert:\/localmachine\/my | Select -First 1).gettype() | Format-Table Name,BaseType -auto<\/p>\n<p>Name BaseType<\/p>\n<p>&#8212;- &#8212;&#8212;&#8211;<\/p>\n<p>X509Certificate2 System.Security.Cryptography.X509Certificates.X509Certificate<\/p>\n<\/blockquote>\n<p>The <b>BaseType<\/b> of each is <b>System.Security.Cryptography.X509Certificates.X509Certificate<\/b>,<b> <\/b>which might make you wonder: &ldquo;Boe, why in the world are we using the .NET classes when we can so easily perform this with the provider?&rdquo; <\/p>\n<p>Remember when I mentioned that you could use the .NET class to connect to a certificate store on a remote machine? Well, here lies the limitation of the provider, at least if you do not have Windows PowerShell <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/windows+powershell\/getting+started\/remoting\/\">remoting<\/a> enabled in your environment. With the provider, though you can more easily view all of the certificates in both the <strong>LocalMachine<\/strong> and <strong>CurrentUser<\/strong> store locations with nothing more than a line of code, you are unable to use it to make a query to a remote machine without first initiating a remote Windows PowerShell session to that server.<\/p>\n<p>Using the same code snippet earlier, we make one small change to the store name by adding the UNC path to the system:<\/p>\n<blockquote>\n<p>$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(&#8220;\\\\dc1\\My&#8221;,&#8221;LocalMachine&#8221;)<\/p>\n<p>$store.Open(&#8220;ReadOnly&#8221;)<\/p>\n<p>$store.Certificates<\/p>\n<\/blockquote>\n<p>The results are shown in the following image.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7217.HSG-2-16-11-5_105F94D3.jpg\"><img decoding=\"async\" height=\"116\" width=\"604\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0755.HSG-2-16-11-5_thumb_273E664F.jpg\" alt=\"Image of script results\" border=\"0\" title=\"Image of script results\" style=\"border-bottom: 0px;border-left: 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" \/><\/a><\/p>\n<p>Pretty cool, huh?<\/p>\n<p>You can do this using the certificate provider, assuming you not only have Windows PowerShell 2.0 installed on both your client and the remote system, but also have remoting enabled on the remote system. However, if your place of business is still running Windows PowerShell 1.0 or has not made the leap to allow Windows PowerShell remoting for one reason or another, using the .NET classes to make the remote connection is your best bet.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Locating Expiring Certificates<\/b><\/p>\n<p>Now that we have made a connection to the remote <strong>LocalMachine<\/strong> store on a server, let us go ahead and find out if any certificates are going to expire within the next 14 days.<\/p>\n<p>Using my existing connection, we will look at the <b>NotAfter<\/b> property&mdash;the property that tells us the expiration date of the certificate&mdash;to find out if it has expired or if it will expire within 14 days. The <b>NotAfter<\/b> property of the certificate represents the local time that the given certificate will expire. With 10 lines of code, I can run a query against a remote server to find any certificates that are due to expire in the next 14 days.<\/p>\n<blockquote>\n<p>#Number of days to look for expiring certificates<\/p>\n<p>$threshold = 14<\/p>\n<p>#Set deadline date<\/p>\n<p>$deadline = (Get-Date).AddDays($threshold)<\/p>\n<p>$store=new-object System.Security.Cryptography.X509Certificates.X509Store(&#8220;\\\\dc1\\my&#8221;,&#8221;LocalMachine&#8221;)<\/p>\n<p>$store.open(&#8220;ReadOnly&#8221;)<\/p>\n<p>$store.certificates | % {<\/p>\n<p>If ($_.NotAfter -lt $deadline) {<\/p>\n<p>$_ | Select Issuer, Subject, NotAfter, @{Label=&#8221;ExpiresIn&#8221;; Expression={($_.NotAfter &#8211; (Get-Date)).Days}}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>Issuer Subject NotAfter ExpiresIn<\/p>\n<p>&#8212;&#8212; &#8212;&#8212;- &#8212;&#8212;&#8211; &#8212;&#8212;&#8212;<\/p>\n<p>CN=Root CA, DC=r&#8230; 1\/25\/2011 10:32:&#8230; 5<\/p>\n<\/blockquote>\n<p>From the looks of it, there is one certificate that is going to expire in five days. As you can tell, I added the custom property <b>ExpiresIn<\/b>, which lists how many days until the certificate expires to the output, by using the <b>@{Label=&rdquo;&rdquo;;Expression={}}<\/b> hash table. Imagine running this across your domain and finding out if you had certificates expiring before it became an issue! It is as easy as adding a few more lines of code to use a <b>ForEach<\/b> loop to iterate through all of the computers. Better yet, configure the code to send an email with this information, save as a script, and then set the script up as a scheduled job. You will then have this process completely automated.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Locate Expired Certificates<\/b><\/p>\n<p>Building on what I covered with locating expiring certificates, I will slightly modify the existing code to find certificates that have expired.<\/p>\n<blockquote>\n<p>$store=new-object System.Security.Cryptography.X509Certificates.X509Store(&#8220;\\\\dc1\\my&#8221;,&#8221;LocalMachine&#8221;)<\/p>\n<p>$store.open(&#8220;ReadOnly&#8221;)<\/p>\n<p>$store.certificates | % {<\/p>\n<p>If ($_.NotAfter -lt (Get-Date)) {<\/p>\n<p>$_ | Select Issuer, Subject, @{Label=&#8221;ExpiredOn&#8221;;Expression={$_.NotAfter}}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>Issuer Subject ExpiredOn<\/p>\n<p>&#8212;&#8212; &#8212;&#8212;- &#8212;&#8212;&#8211;<\/p>\n<p>CN=Root CA, DC=r&#8230; 1\/24\/2011 04:13:&#8230;<\/p>\n<\/blockquote>\n<p>Just like that, we found a certificate that has expired which leaves us to decide whether it needs to be renewed or removed from the store. Of course, the best part is this can easily be automated to send out an email to you if any expired certificates are found.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Get-PKICertificates Function<\/b><\/p>\n<p>To assist with performing queries against local and remote systems and find expiring or expired certificates, I wrote an advanced function that you could use to accomplish this task. This advanced function allows you to supply a local or remote system, determine whether to use the <strong>LocalMachine<\/strong> or <strong>CurrentUser<\/strong> store, and search store names other than the &ldquo;My&rdquo; name. In addition, you can set the <b>OpenFlag<\/b> that you want to use when you query the store. The script is available for download in the <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/a2a500e5-1dd2-4898-9721-ed677399679c\">Script Repository<\/a>. <\/p>\n<p>Here are a few examples of using the advanced function to locate certificates.<\/p>\n<blockquote>\n<p><b>Listing Certificates That Expire in 14 Days<\/b><\/p>\n<p>PS C:\\Users\\boe&gt; Get-PKICertificates -comp dc1 -StoreLocation LocalMachine -StoreName My -ExpiresIn 14 | Format-Table Su<\/p>\n<p>bject, FriendlyName,Issuer, @{Label=&#8221;ExpiresIn&#8221;;Expression={($_.NotAfter &#8211; (get-Date)).Days}} -auto<\/p>\n<p>Subject FriendlyName Issuer ExpiresIn<\/p>\n<p>&#8212;&#8212;- &#8212;&#8212;&#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;<\/p>\n<p>DC1 CN=Root CA, DC=rivendell, DC=com 4<\/p>\n<p>&nbsp;<\/p>\n<p><i><\/i><\/p>\n<p><b>List All Certificates on a Remote Computer<\/b><\/p>\n<p>PS C:\\Users\\boe&gt; Get-PKICertificates -comp dc1 -StoreLocation LocalMachine -StoreName My<\/p>\n<p>Thumbprint Subject<\/p>\n<p>&#8212;&#8212;&#8212;- &#8212;&#8212;-<\/p>\n<p>A1CECD6C1B0A61F3D784EC29C60BD0D8F2FA46BD<\/p>\n<p>7A42B8E32FABF3B85B1BAEF5D6FF6F29EB03C58E CN=Root CA, DC=rivendell, DC=com<\/p>\n<p>3B0D348B90D663293067F8C481075927016F56CE CN=dc1.rivendell.com<\/p>\n<p>10E740A73045250E9AF0778C7DAABB2E1F22C6D9 CN=dc1, OU=Scripting, O=Prox, L=Bellevue, S=NE, C=US<\/p>\n<p>&nbsp;<\/p>\n<p><b><\/b><\/p>\n<p><b>List Certificates on Multiple Computers<\/b><\/p>\n<p>PS C:\\Users\\boe&gt; Get-PKICertificates -comp dc1,boe-laptop -StoreLocation LocalMachine -StoreName My<\/p>\n<p>Thumbprint Subject<\/p>\n<p>&#8212;&#8212;&#8212;- &#8212;&#8212;-<\/p>\n<p>A1CECD6C1B0A61F3D784EC29C60BD0D8F2FA46BD<\/p>\n<p>7A42B8E32FABF3B85B1BAEF5D6FF6F29EB03C58E CN=Root CA, DC=rivendell, DC=com<\/p>\n<p>3B0D348B90D663293067F8C481075927016F56CE CN=dc1.rivendell.com<\/p>\n<p>10E740A73045250E9AF0778C7DAABB2E1F22C6D9 CN=dc1, OU=Scripting, O=Prox, L=Bellevue, S=NE, C=US<\/p>\n<p>211E73160B9E67F8C0EEBAB7007CF68589F392D1 CN=boe-laptop<\/p>\n<\/blockquote>\n<p>That is it for today. I hope you enjoyed my guest blog on querying for certificates using the .NET classes, and I would also like to thank Ed for allowing me this great opportunity to be a guest blogger for today.<\/p>\n<p>&nbsp;<\/p>\n<p>Boe, I want to thank you for sharing with us and for being our guest blogger. Guest Blogger Week will continue tomorrow when Doug Finke will join us. <\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/social.technet.microsoft.com\/Forums\/en\/ITCG\/threads\/\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b><\/b><\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn how to use Windows PowerShell and Microsoft .NET classes to find expired certificates on local and remote computers. &nbsp; Hey, Scripting Guy! How can I use Windows PowerShell and the .NET Framework classes to work with certificates? &#8212; PB &nbsp; Hello PB, Microsoft Scripting Guy Ed Wilson here. We continue Guest Blogger Week [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[162,217,56,3,63,45],"class_list":["post-15581","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-boe-prox","tag-certificates","tag-guest-blogger","tag-scripting-guy","tag-security","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Learn how to use Windows PowerShell and Microsoft .NET classes to find expired certificates on local and remote computers. &nbsp; Hey, Scripting Guy! How can I use Windows PowerShell and the .NET Framework classes to work with certificates? &#8212; PB &nbsp; Hello PB, Microsoft Scripting Guy Ed Wilson here. We continue Guest Blogger Week [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/15581","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=15581"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/15581\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=15581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=15581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=15581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}