<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ngetchell.com - Posts tagged with &quot;TLS&quot;</title><description>All blog posts tagged with &quot;TLS&quot;</description><link>https://ngetchell.com/</link><item><title>Fun with Certificates</title><link>https://ngetchell.com/blog/fun-with-certificates/</link><guid isPermaLink="true">https://ngetchell.com/blog/fun-with-certificates/</guid><description>Disect certificates for fun and profit.</description><pubDate>Mon, 12 Feb 2024 11:59:41 GMT</pubDate><content:encoded>
&lt;p&gt;After years of running websites, I&apos;ve come across a few simple tricks to help to deal with certificates.
This post is as much for me as it is for the internet. 
I always have issues rewriting or rediscovering this stuff and figured I&apos;d post it so I don&apos;t forget it.&lt;/p&gt;



&lt;h2&gt;Inspecting a Certificate File&lt;/h2&gt;



&lt;p&gt;If you have a local certificate you&apos;d like to inspect, I use openssl to interrogate the file.
This will decode the base64 encoding of the certificate in human readable form.&lt;/p&gt;



&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;openssl&lt;/span&gt;&lt;span&gt; x509&lt;/span&gt;&lt;span&gt; -in&lt;/span&gt;&lt;span&gt; ngetchell.com.cer&lt;/span&gt;&lt;span&gt; -text&lt;/span&gt;&lt;span&gt; -noout&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Slap a &lt;code&gt;| grep After&lt;/code&gt; to the command to get a quick readout of the certificate expiration date.&lt;/p&gt;



&lt;h2&gt;Retrieving Certificate Information from a Web Server&lt;/h2&gt;



&lt;p&gt;Every once in a while, you need to determine the state of an TLS certificate.
&lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot;&gt;SSL Labs&lt;/a&gt; provides a comprehensive inspection of a given site, but it does come with some limits.
For one, it will only check a web server sitting on port 443.
Another is, you have to do it all one at a time.&lt;/p&gt;



&lt;p&gt;If you don’t need to delve deep into the security of the web server, or you need to rapidly iterate through a list of domains, you can use this.&lt;/p&gt;



&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Function&lt;/span&gt;&lt;span&gt; Get-TLSCertificate&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;CmdletBinding&lt;/span&gt;&lt;span&gt;()]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    param&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        $DomainName,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        $Port&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    $request&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;System.Net.Sockets.TcpClient&lt;/span&gt;&lt;span&gt;]::new(&lt;/span&gt;&lt;span&gt;$DomainName,&lt;/span&gt;&lt;span&gt; $Port&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    $stream&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;System.Net.Security.SslStream&lt;/span&gt;&lt;span&gt;]::new(&lt;/span&gt;&lt;span&gt;$request.GetStream&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    $stream.AuthenticateAsClient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$DomainName&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;PSCustomObject&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        DomainName&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $DomainName&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Port&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $Port&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        TargetHostname&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.TargetHostName&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Subject&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.Subject&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Thumbprint&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.Thumbprint&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        NotAfter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.NotAfter&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        NotBefore&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.NotBefore&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ExpirationCountdown&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;New-TimeSpan&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;end &lt;/span&gt;&lt;span&gt;$stream.RemoteCertificate.NotAfter&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;start &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Get-Date&lt;/span&gt;&lt;span&gt;) ).Days&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Issuer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.Issuer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        TLSProtocol&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.SslProtocol&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        NegotiatedCipherSuite&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.NegotiatedCipherSuite&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        SignatureAlgorithm&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; $stream.RemoteCertificate.SignatureAlgorithm.FriendlyName&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Extensions&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; @&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$stream.RemoteCertificate.Extensions.oid.friendlyname&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; select &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;unique )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Get-TLSCertificate&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;DomainName ngetchell &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Port &lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I&apos;ve used this during mass certificate rotations to confirm we&apos;d done our job.
When migrating from one wildcard certificate to another, it is important to double-check your work.
We started with an export of our DNS zone and iterated through each sub-domain to make sure the thumbprint changed.
It caught a few that we thought we&apos;d switched but missed for whatever reason.
The script also helped to unearth some services that we believed were behind a load balancer but turns out were not.&lt;/p&gt;



&lt;h2&gt;Generating a CSR from a template file&lt;/h2&gt;



&lt;p&gt;As more and more services support the ACME protocol, manually generating certificate signing requests becomes less and less useful.
Still, there are times you need to know how to generate a certificate.
This template also allows you to add additional Subject Alternative Names, which is useful if your service is known by many names.&lt;/p&gt;



&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;default_bits = 2048&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;default_keyfile = /etc/path/to/private.key&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;encrypt_key = no&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;default_md = sha256&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;prompt = no&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;utf8 = yes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;distinguished_name = my_req_distinguished_name&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;req_extensions = my_extensions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[ my_req_distinguished_name ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;C = US&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ST = Parts Unknown&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;L = Death Valley&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;O  = Blog&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CN = gitlab-web.example.com&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[ my_extensions ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;basicConstraints=CA:FALSE&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;subjectAltName=@my_subject_alt_names&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;subjectKeyIdentifier = hash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[ my_subject_alt_names ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS.1 = gitlab-web.example.mobile&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS.2 = gitlab-web.example.net&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;To invoke the template, you run the following.&lt;/p&gt;



&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;openssl&lt;/span&gt;&lt;span&gt; req&lt;/span&gt;&lt;span&gt; -new&lt;/span&gt;&lt;span&gt; -out&lt;/span&gt;&lt;span&gt; example.csr&lt;/span&gt;&lt;span&gt; -config&lt;/span&gt;&lt;span&gt; csr.conf&lt;/span&gt;&lt;span&gt; -key&lt;/span&gt;&lt;span&gt; private.key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>OpenSSL</category><category>PowerShell</category><category>TLS</category></item></channel></rss>