<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>dicom tls on Digi Hunch</title><link>https://static.digihunch.com/tag/dicom-tls/</link><description>Recent content in dicom tls on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Mon, 12 May 2025 23:27:51 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/dicom-tls/index.xml" rel="self" type="application/rss+xml"/><item><title>DICOM testing over TLS</title><link>https://static.digihunch.com/2023/02/dicom-testing-with-tls/</link><pubDate>Sat, 18 Feb 2023 01:05:00 -0400</pubDate><guid>https://static.digihunch.com/2023/02/dicom-testing-with-tls/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/dicom-testing-feature.webp" alt="Featured image of post DICOM testing over TLS" /&gt;&lt;p class="wp-block-paragraph"&gt;I have two open-source projects to deploy a medical imaging application on different platforms. In both of them, I define DICOM validation scenario, and provide steps to test DICOM traffic with TLS. The steps have been working well, until a recent change in Envoy broke the testing, and led me to revisit the test scenario. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a nutshell, I changed from &lt;code&gt;dcm4che&lt;/code&gt; to &lt;code&gt;dcmtk&lt;/code&gt; binary builds. I&amp;#8217;ve also expanded the test case from a self-signed server certificate to one involving a self-signed CA. Although this test is about TLS for DICOM traffic, the principles apply to any traffic at TCP level. If you just need instruction for DICOM validation on &lt;a href="https://github.com/digihunch/orthweb"&gt;Orthweb&lt;/a&gt; or &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; projects, skip the Background section. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-background"&gt;Background&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To test DICOM traffic with command line tool, I was investigating between &lt;code&gt;dcm4che&lt;/code&gt; and &lt;code&gt;dcmtk&lt;/code&gt;. Both are open-source projects with builds for multiple platforms. My DICOM test is as simple as a C-Echo command and a C-Store command using the tool, with TLS enabled. Once they work, other DICOM commands usually work as well. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have been primarily using &lt;code&gt;dcm4che&lt;/code&gt; as I was familiar with its previous version from my old job. For example, I can issue a C-ECHO with TLS using storescu command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./storescu -c ORTHANC@ec2-54-243-91-148.compute-1.amazonaws.com:11112 --tls12 --tls-aes --trust-store server.truststore --trust-store-pass Password123!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To turn that test into a C-Store test, simply add a DCM file as input:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./storescu -c ORTHANC@ec2-54-243-91-148.compute-1.amazonaws.com:11112 --tls12 --tls-aes --trust-store server.truststore --trust-store-pass Password123! MY.DCM&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The C-Store output tracks each DIMSE command and return codes. Note that in the command, we specify &amp;#8211;tls12 as the version, and with &amp;#8211;tls-aes switch we enabled AES or 3DES encryption. We also specified a file for trust store and password to the trust store. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is when I first frowned over &lt;code&gt;dcm4che&lt;/code&gt;. The &lt;code&gt;dcm4che&lt;/code&gt; utility is a Java-based program we have to take an extra step of turning certificate into Java trust store. &lt;a href="https://static.digihunch.com/2018/11/the-java-confusions/"&gt;Different JVM versions&lt;/a&gt; may also cause different behaviours in the test. What later prompted me to switch to &lt;code&gt;dcmtk&lt;/code&gt; is that with &lt;code&gt;dcm4che&lt;/code&gt; I came across a weird &lt;a href="https://github.com/digihunch/orthweb/issues/14#issuecomment-1320768544"&gt;error&lt;/a&gt; since Envoy proxy version 1.23, which impacted both Orthweb (Envoy proxy) and Korthweb (Istio Ingress).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moreover, &lt;code&gt;dcmtk&lt;/code&gt; is available as a &lt;a href="https://formulae.brew.sh/formula/dcmtk#default"&gt;HomeBrew package&lt;/a&gt;, &lt;a href="https://packages.ubuntu.com/search?keywords=dcmtk"&gt;Ubuntu package&lt;/a&gt;, and &lt;a href="https://packages.debian.org/buster/dcmtk"&gt;Debian package&lt;/a&gt;. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;The test case, data and the tool&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can install dcmtk utility simply with &lt;code&gt;brew install dcmtk&lt;/code&gt;, and then we need its &lt;code&gt;echoscu&lt;/code&gt; and &lt;code&gt;storescu&lt;/code&gt; commands with correct TLS options. The DICOM data I used for testing is a CT exam available for download &lt;a href="https://download.digihunch.com/dicom_data/covid_study.zip"&gt;here&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Before mocking with &lt;code&gt;dcmtk&lt;/code&gt;&amp;#8216;s TLS options, we first need to understand what would be a good test. Previously I have used a single self-signed certificate on server. It is an over-simplified scenario that is far from a real-life certificate chain, and also does not test client certificate. If I also self-sign the client certificate, the client and server certificates are signed by entirely different parties and have no trust relationship, making it an invalid test case for client certificate.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a full-blown testing with TLS, we should have two levels of CA as below:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="646px" viewBox="-0.5 -0.5 646 421" style="max-width:100%;max-height:421px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="645" height="420" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="345" y="300" width="280" height="100" rx="15" ry="15" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 278px; height: 1px; padding-top: 307px; margin-left: 346px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="485" y="319" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="505" y="350" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 365px; margin-left: 506px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="560" y="369" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="325" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 332px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="428" y="344" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="375" y="350" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 365px; margin-left: 376px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="369" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="345" y="130" width="280" height="100" rx="15" ry="15" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 278px; height: 1px; padding-top: 137px; margin-left: 346px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="485" y="149" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="505" y="180" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 195px; margin-left: 506px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="560" y="199" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="155" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 162px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="428" y="174" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="375" y="180" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 195px; margin-left: 376px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="25" y="180" width="240" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 238px; height: 1px; padding-top: 245px; margin-left: 27px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Intermediate CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="27" y="249" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Intermediate CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="132.5" y="270" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 285px; margin-left: 134px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="289" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="195" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 202px; margin-left: 121px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="135" y="220" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 136px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="239" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="25" y="20" width="240" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 238px; height: 1px; padding-top: 85px; margin-left: 27px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Root CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="27" y="89" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Root CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="132.5" y="110" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 125px; margin-left: 134px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="129" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="35" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 42px; margin-left: 121px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="54" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="135" y="60" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 75px; margin-left: 136px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="79" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 300 L 188 358 L 353.63 357.52" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 358.88 357.5 L 351.89 361.02 L 353.63 357.52 L 351.87 354.02 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 359px; margin-left: 281px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="281" y="363" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 242.5 285 L 428 285 L 427.55 226.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 427.51 221.12 L 431.06 228.09 L 427.55 226.37 L 424.06 228.14 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 285px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="306" y="288" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 140 L 187.5 188.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 187.5 193.88 L 184 186.88 L 187.5 188.63 L 191 186.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 163px; margin-left: 187px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="187" y="166" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 242.5 125 L 285 125 L 285 68 L 261.37 67.61" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 256.12 67.52 L 263.18 64.14 L 261.37 67.61 L 263.06 71.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 91px; margin-left: 296px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;self-sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="296" y="94" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;self-sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The chart represents a typical hierarchy of three-level certificate authorities. Sometimes we need simplicity in our testing, and it is reasonable to simplify the diagram to the following, with one CA that issues certificate for both client and server:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="601px" viewBox="-0.5 -0.5 601 301" style="max-width:100%;max-height:301px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="600" height="300" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="290" y="180" width="280" height="90" rx="13.5" ry="13.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 278px; height: 1px; padding-top: 187px; margin-left: 290px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="568" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="450" y="220" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 451px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="239" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="305" y="195" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 202px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="373" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="320" y="220" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 321px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="375" y="239" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="290" y="40" width="280" height="90" rx="13.5" ry="13.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 278px; height: 1px; padding-top: 47px; margin-left: 290px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="568" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="450" y="80" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 95px; margin-left: 451px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="99" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="305" y="55" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 62px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="373" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="320" y="80" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 95px; margin-left: 321px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="375" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="20" y="50" width="190" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe flex-start; width: 188px; height: 1px; padding-top: 177px; margin-left: 22px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Test CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="22" y="177" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Test CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="77.5" y="140" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 155px; margin-left: 79px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="133" y="159" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="65" y="65" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 72px; margin-left: 66px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="133" y="84" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="80" y="90" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 105px; margin-left: 81px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="135" y="109" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 132.5 170 L 133 228 L 298.63 227.52" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 303.88 227.5 L 296.89 231.02 L 298.63 227.52 L 296.87 224.02 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 229px; margin-left: 226px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="226" y="233" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 190 160 L 373 160 L 372.58 126.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 372.51 121.12 L 376.1 128.07 L 372.58 126.37 L 369.1 128.16 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 247px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="247" y="163" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 147.5 L 230 148 L 230 98 L 206.37 97.61" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 201.12 97.52 L 208.18 94.14 L 206.37 97.61 L 208.06 101.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 118px; margin-left: 241px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;self-sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="241" y="121" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;self-sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Our DICOM validation testing will be based on both approaches depending on the project and deployment option. For Orthweb project and the Helm-chart driven option in Korthweb, we have one level of CA. For the GitOps and manual option in Korthweb, we have two levels of CA. When configuring testing, it is important to have the diagram above in mind.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition, we should also be aware of the limitation of the testing. As a personal project I will not pay for the certificates. I have to self-sign the certificate of the CA so there is no way to derive trust on this CA from another level. As a result, we must tell the client and server to trust the CA. The steps to create the needed certificates are: &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Generate a key pair for Test CA. Generate the certificate for Test CA by self-signing its own public key&lt;/li&gt;&#10;&lt;li&gt;Generate a key pair for the (DICOM) server. Generate the certificate for the server by signing its public key with Test CA&amp;#8217;s private key&lt;/li&gt;&#10;&lt;li&gt;Generate a key pair for the (DICOM) client. Generate the certificate for the client by signing its public key with Test CA&amp;#8217;s private key&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because the client now also has its certificate, we can test with and without client certificate, using the following &lt;code&gt;dcmtck&lt;/code&gt; switches:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;-d&lt;/strong&gt; (shorthand for &amp;#8211;debug): print out detailed DICOM communication log. For succinct output, use -v (shorthand for &amp;#8211;verbose) instead.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+tla&lt;/strong&gt; (shorthand for &amp;#8211;anonymous-tls): enable anonymous TLS (without client certificate)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+tls&lt;/strong&gt; (shorthand for &amp;#8211;enable-tls): enable full TLS (with client certificate), followed by client key and certificate files&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;-rc&lt;/strong&gt; (shorthand for &amp;#8211;require-peer-cert): &amp;#8211;require-peer-cert, require peer (server) certificate&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+cf&lt;/strong&gt; (shorhand for &amp;#8211;add-cert-file): &amp;#8211;add-cert-file, add server certificate so client can trust it.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For C-ECHO, the testing commands with and without client certificate look like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echoscu -aet TESTER -aec ORTHANC -d +tla -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echoscu -aet TESTER -aec ORTHANC -d +tls client.key client.crt -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For C-STORE, the testing commands with and without client certificate look like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ storescu -aet TESTER -aec ORTHANC -d +tla -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_Images/COVID/56364823.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ storescu -aet TESTER -aec ORTHANC -d +tls client.key client.crt -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_Images/COVID/56364823.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;As to how to create the certificates, in the post &lt;em&gt;&lt;a href="https://static.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;Creating X.509 TLS certificate in Kubernetes&lt;/a&gt;&lt;/em&gt;, I discussed different ways to create certificates for testing, including using openssl. In the next two sections, I will discuss them in further details.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Orthweb Test&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The orthweb project runs Docker containers on an EC2 instance. In the &lt;a href="https://github.com/digihunch/orthweb/blob/e13c590121b6d1bd22ec6092f4f6542c9680d4fc/terraform/modules/ec2/userdata1.sh"&gt;cloud-init script&lt;/a&gt; of the EC2 instance, we self-sign a test CA with openssl. Then we create key and certificate for server and client respectively:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IssuerComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;issuer.orthweb.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ClientComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient.orthweb.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ServerComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ca-central-1.compute.amazonaws.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -x509 -sha256 -newkey rsa:4096 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$IssuerComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;info@www.digihunch.com -keyout /tmp/ca.key -out /tmp/ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ServerComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;orthweb@www.digihunch.com -addext extendedKeyUsage&lt;span style="color:#f92672"&gt;=&lt;/span&gt;serverAuth -addext subjectAltName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;DNS:orthweb.digihunch.com,DNS:$IssuerComName -keyout /tmp/server.key -out /tmp/server.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in /tmp/server.csr -CA /tmp/ca.crt -CAkey /tmp/ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out /tmp/server.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ClientComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;orthweb@www.digihunch.com -keyout /tmp/client.key -out /tmp/client.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in /tmp/client.csr -CA /tmp/ca.crt -CAkey /tmp/ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out /tmp/client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When simulating DICOM activities from the client side, we supply the three files (&lt;code&gt;ca.key&lt;/code&gt;, &lt;code&gt;client.crt&lt;/code&gt; and &lt;code&gt;client.key&lt;/code&gt;) to the &lt;code&gt;echoscu&lt;/code&gt; and &lt;code&gt;storescu&lt;/code&gt; executables, to issue DIMSE commands on top of TLS from client side, as shown in the previous section. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Korthweb Test&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Currently, the Korthweb project deploys to Kubernetes cluster in three approaches, including two types of ingress controllers: Istio CRD (manual and GitOps deployment options) and Traefik CRD (Helm Chart driven deployment options). With both approaches, it is fairly straightforward to validate the HTTPS port. We export the CA certificate and run a curl command such as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -HHost:web.orthweb.com -k -X GET https://web.orthweb.com:443/app/explorer.html -u admin:orthanc --cacert ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that by default, the &lt;code&gt;curl&lt;/code&gt; command adds SNI (server name indication) extension by default to its TLS &lt;code&gt;ClientHello&lt;/code&gt; Message (even without -HHost switch). It acts like a &lt;a href="https://www.ssllabs.com/ssltest/clients.html"&gt;modern browser&lt;/a&gt;. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="465" src="https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark.webp" alt="" class="wp-image-12895" srcset="https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark-300x136.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark-768x349.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the ingress controller side, most ingress controllers use SNI to drive request routing (because Host field in payload is encrypted). For example, Traefik Proxy has &lt;a href="https://traefik.io/blog/announcing-traefik-proxy-2-7/"&gt;HostSNI&lt;/a&gt; matching rule. With Istio, the &lt;a href="https://istio.io/latest/docs/reference/config/networking/gateway/#Port"&gt;document&lt;/a&gt; states that: TLS implies the connection will be routed based on the SNI header to the destination. With DICOM traffic the Ingress also expects the client to make use of SNI extension in the TLS ClientHello message. The ingress supports multiple sites so the SNI even has an impact of which TLS certificate the ingress serves to the client. We can use openssl to examine which certificate an ingress serves. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl s_client -showcerts -connect dicom.orthweb.com:11112 -servername dicom.orthweb.com &amp;lt; /dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl s_client -showcerts -connect dicom.orthweb.com:11112 &amp;lt; /dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The second command without &lt;code&gt;-servername&lt;/code&gt; switch constructs an ClientHello message without SNI. The ingress may not have a clue of what certificate to serve, depending on its own implementation of TLS protocol. We can also force TLS version with a switch such as &lt;code&gt;-tls1_2&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to open-source DICOM client, neither &lt;code&gt;dcm4che&lt;/code&gt; or &lt;code&gt;dcmtk&lt;/code&gt; puts SNI in the TLS request. This created some limitation with my testing. Luckily I do not have multiple routing destinations for now so I only need to direct all DICOM traffic to a service. When I use &lt;a href="https://github.com/digihunch/korthweb/blob/13984a4057eb8b68bc7d15626635e171fdb0f69f/manual/orthanc.yaml#L187"&gt;Istio ingress&lt;/a&gt;, I was able to set hosts to &amp;#8220;*&amp;#8221; so the Ingress does not care missing SNI extension in the client request. With Traefik proxy, I had to set &lt;a href="https://github.com/digihunch/korthweb/blob/27a1a6e2f80b811c0ac593c95f912987a888ca03/helm/orthanc/templates/ingressroute.yaml#L11"&gt;sniStrict&lt;/a&gt; to false, and also forgo client certificate check. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The workaround is different per ingress implementation. Even worse, depending on what the available workaround can achieve, the testing steps vary as well. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, I perform DICOM validation (Istio ingress) with the steps below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: generate client key pair&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient.bhs.orthweb.com/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient@www.digihunch.com -keyout bhs.client.key -out bhs.client.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: export intermediate CA credentials&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n bhs-orthweb get secret int-ca-secret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.tls\.key}&amp;#39;&lt;/span&gt; | base64 -d &amp;gt; bhs.int.ca.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n bhs-orthweb get secret int-ca-secret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.tls\.crt}&amp;#39;&lt;/span&gt; | base64 -d &amp;gt; bhs.int.ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: get intermediate CA to sign client cert &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in bhs.client.csr -CA bhs.int.ca.crt -CAkey bhs.int.ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out bhs.client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate web request (without client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -HHost:web.bhs.orthweb.com -k -X GET https://web.bhs.orthweb.com:443/app/explorer.html -u admin:orthanc --cacert bhs.int.ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate DICOM c-echo request (with client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echoscu -aet TESTER -aec ORTHANC -d +tls bhs.client.key bhs.client.crt -rc +cf bhs.int.ca.crt dicom.bhs.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate DICOM c-store request (with client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storescu -aet TESTER -aec ORTHANC -d +tls bhs.client.key bhs.client.crt -rc +cf bhs.int.ca.crt dicom.bhs.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_CT/0001.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;On the other hand, for Traefik ingress, I have to use anonymous TLS without client certificate:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echoscu -aet TESTER -aec ORTHANC -d +tla -ic dicom.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storescu -aet TESTER -aec ORTHANC -d +tla -ic dicom.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_CT/123.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The complexities with different test paths are consequences of the missing SNI capability in both DICOM toolkits. Unfortunately, the developers of the two DICOM tools are not aware of these consequences. I tried to contact &lt;code&gt;dcmtk&lt;/code&gt; about this and will see what happens.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;TLS profile&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another setting to pay close attention to is the security profile for TLS communication. These profiles defines the behaviours of &lt;code&gt;dcmtk&lt;/code&gt; when it establishes TLS connection. The dcmtk has the following security profiles:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195-nd&lt;/strong&gt; (+py default): Non-downgrading BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195&lt;/strong&gt; (+px): BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195-ex&lt;/strong&gt; (+pz): Extended BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;aes&lt;/strong&gt; (+pa): AES TLS Secure Transport Connection Profile (retired)&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;null&lt;/strong&gt; (+pn): Authenticated unencrypted communication (retired, was used in IHE ATNA)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The two at the bottom have been &lt;a href="https://www.dicomstandard.org/news/supplements/view/tls-security-profiles"&gt;retired&lt;/a&gt;. The current profiles are all based on &lt;a href="https://datatracker.ietf.org/doc/bcp195/"&gt;BCP195&lt;/a&gt;. BCP (best current practice) are sub-series of the corresponding RFC document series. The current revision of DICOM standard discusses bcp195-nd, bcp195 and bcp195-ex profiles in DICOM standard &lt;a href="https://dicom.nema.org/MEDICAL/Dicom/2022d/output/pdf/part15.pdf"&gt;chapter PS 3.15&lt;/a&gt; (B.9-B.11). For example, bcp195-nd requires that:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Implementation shall not negotiate TLS 1.0 or 1.1&lt;/li&gt;&#10;&lt;li&gt;Client and server shall prefer strict TLS configuration (as opposed to startTLS)&lt;/li&gt;&#10;&lt;li&gt;Ciphers that should be supported.&lt;/li&gt;&#10;&lt;li&gt;Recommend port 2762&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These BCP profiles were incorporated into DICOM standard since 2018 and are all based on &lt;a href="https://www.rfc-editor.org/bcp/bcp195.txt"&gt;BCP195&lt;/a&gt;. BCP 195 states in section 3.6&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;cite&gt;3.6. Server Name Indication&lt;br&gt;TLS implementations MUST support the Server Name Indication (SNI)&lt;br&gt;extension defined in Section 3 of [RFC6066] for those higher-level&lt;br&gt;protocols that would benefit from it, including HTTPS. However, the&lt;br&gt;actual use of SNI in particular circumstances is a matter of local&lt;br&gt;policy.&lt;br&gt;Rationale: SNI supports deployment of multiple TLS-protected virtual&lt;br&gt;servers on a single address, and therefore enables fine-grained&lt;br&gt;security for these virtual servers, by allowing each one to have its&lt;br&gt;own certificate.&lt;/cite&gt;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So technically, missing SNI is considered incompliant.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I find myself switching between &lt;code&gt;dcmtk&lt;/code&gt; and &lt;code&gt;dcm4che&lt;/code&gt; back and forth in the past. This time, I spent some time hoping to settle with the better tool this time. The effort is insightful but not fruitful. It is unfortunate to realize that neither supports SNI so I had to compromise the feature of my deployment. Hopefully one of those tools will catch up.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2023/01/github-action-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;GitHub Action Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/03/a-taste-of-iot-device-tracking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;A taste of IoT device tracking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automatic deployment of Orthanc on AWS</title><link>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</link><pubDate>Sun, 08 Nov 2020 00:54:06 -0400</pubDate><guid>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</guid><description>&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] I changed reverse proxy from Nginx to Envoy. &lt;a href="https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/"&gt;Here&lt;/a&gt;&amp;#8216;s the detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] Some security improvement was introduced in may 2021. &lt;a class="rank-math-link" href="https://static.digihunch.com/2021/05/secure-web-application-deployment/"&gt;Here&lt;/a&gt;&amp;#8216;s detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] &lt;a href="https://github.com/digihunch/orthweb"&gt;Here&amp;#8217;s&lt;/a&gt; the link to the orthweb repository.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this project we introduce a medical imaging web service based on Orthanc, an open-source project of DICOM server, and a pipeline to deploy such server automatically and consistently. We deploy Orthanc on AWS automatically. This little project involves a number of technical deets in DevOps, to deliver a web application prototype with an automated deployment pipeline.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-a-brief-on-imaging"&gt;A brief on imaging&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In medical imaging, scanning devices are the data collectors. It consists of various categories of scanners, such as Computed Tomography (CT), and Ultrasound (US). They are collectively referred to as modality, but vary significantly in terms of image generation and hardware manufacturing. The challenges to exchange data between these heterogeneous scanning devices and centralized computers came around as early as the 1980s, which brought about ACR-NEMA standard in 1985, under the initiative between American College Radiology (ACR) and National Electrical Manufacturers Association (NEMA). The standard lately evolved into DICOM (Digital Imaging Communication in Medicine), a comprehensive set of standard in the ISO framework that governs modern imaging data storage and exchange across several disciplines (radiology, cardiology, pathology, etc) that operate around images in medicine.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to defining a &lt;a href="http://dicom.nema.org/medical/dicom/current/output/chtml/part10/"&gt;file format&lt;/a&gt; to store imaging data, DICOM also includes an upper layer protocol that dictates how two compliant devices (referred as application entity, each identified by AE title) can negotiate a common syntax to transfer objects (e.g. an image, a report or a discovery). Upper layer refers to layer 5-7 in OSI model, or application layer in TCP/IP model.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-imaging-server"&gt;Imaging server&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once scanner acquires images from patient, they stores the exams to imaging server for persistent storage. The functionalities of such server expands overtime since 1990s and hence go by different names in different eras, such as PACS (Picture Archive and Communication Systems), VNA (Vendor Neutral Archive) and EI (enterprise imaging) archive. Regardless of naming, they can be generally seen as a highly specialized variation of enterprise content management system. They are usually hosted with a centralized database to index clinical information at patient, exam and image levels. The other key component is the persistent storage devices, usually in the form of a &lt;a href="https://en.wikipedia.org/wiki/Network-attached_storage" class="rank-math-link"&gt;NAS&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.orthanc-server.com/" class="rank-math-link"&gt;Orthanc&lt;/a&gt; is an open-source initiative for such imaging servers. It provides a DICOM endpoint, allowing scanning devices to store medical images. It also provides a web viewer allowing users to see the images stored. It is released for many platforms, including Docker images.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-infrastructure-as-code"&gt;Infrastructure as code&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Amazon Web Service (AWS) for infrastructure as service, and &lt;a href="https://www.terraform.io/" class="rank-math-link"&gt;Terraform&lt;/a&gt; as the tool to provision resources off AWS, in a reliable and consistent mechanism, known as Infrastructure-as-Code. Terraform is an alternative to CloudFormation, AWS&amp;#8217;s proprietary infrastructure-as-code technology. Terraform is developed by Hashicorp as an open-source project, and therefore is vendor neutral. It supports multiple public cloud vendor through different &lt;a href="https://www.terraform.io/docs/providers/index.html" class="rank-math-link"&gt;providers&lt;/a&gt;. Each provider accesses the vendor specific SDK. For example, the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;AWS provider&lt;/a&gt; integrates with &lt;a href="https://aws.amazon.com/tools/" class="rank-math-link"&gt;AWS SDK&lt;/a&gt;. As a result, the code used in one vendor cannot just be applied to a different vendor without a major overhaul. Terraform&amp;#8217;s current version is 0.13 as of Oct 2020, and has gone through some &lt;a href="https://www.hashicorp.com/blog/announcing-terraform-0-12" class="rank-math-link"&gt;syntax changes&lt;/a&gt; since version 0.11. Terraform also produces files for state management locally in the working directory. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When executing, Terraform combines all files in the working directory to assess variables, and create required resources. It is compatible with the most of AWS resources. For example, you can specify user data with templates when creating EC2 instances. You can also create managed service instance as long as it is supported by the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;provider&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-architecture"&gt;Architecture&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc web server stores data in sqlite by default, but also has a plugin to support &lt;a href="https://wiki.postgresql.org/wiki/Main_Page" class="rank-math-link"&gt;PostgreSQL&lt;/a&gt;, an open-source relational database. AWS has managed service (&lt;a href="https://aws.amazon.com/rds/postgresql/" class="rank-math-link"&gt;RDS&lt;/a&gt;) based on PostgreSQL. In this project, we create an RDS instance that span across two availability zones for minimum high availability. Orthanc also supports storing imaging data including pixels in PostgreSQL, which obviates the need for a dedicated file storage system.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We deploy the application in Docker&amp;#8217;s containers for compatibility and portability. The Orthanc server is shipped in &lt;a class="rank-math-link" href="https://orthanc.uclouvain.be/book/users/docker.html"&gt;Docker images&lt;/a&gt;, available in &lt;a class="rank-math-link" href="https://hub.docker.com/r/jodogne/orthanc"&gt;Docker hub&lt;/a&gt; registry. The docker environment is configured as part of EC2 instance bootstrapping, including installing packages with &lt;a href="https://static.digihunch.com/2019/02/package-repository-management-in-linux/"&gt;YUM&lt;/a&gt;, initializing and customizing environment variables. The docker-compose file, and the auxiliary configuration files are provided in the repo. The bootstrapping script installs git and pulls required files from this &lt;a class="rank-math-link" href="https://github.com/digihunch/orthweb"&gt;GitHub repo&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This demo project does not include load balancing, DNS management, or container orchestration.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-security"&gt;Security&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc&amp;#8217;s web browser &lt;a href="https://orthanc.uclouvain.be/book/faq/https.html"&gt;natively supports HTTPS&lt;/a&gt;. However, the DICOM port does not support TLS natively, as their development has made clear in the &lt;a href="https://orthanc.uclouvain.be/book/faq/security.html" class="rank-math-link"&gt;FAQ&lt;/a&gt;. This leaves a severe security vulnerability because all patient data (protected health information in HIPPA context) would be sent across the Internet in the clear, visible to every network interface along the route. To address this issue we brought in Nginx as a reverse proxy to work at TCP layer to terminate encrypted traffic for Orthanc&amp;#8217;s DICOM end point. DICOM upper layer works on top of TCP layer. &lt;/p&gt;&#10;&lt;p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="621px" viewBox="-0.5 -0.5 621 207" style="max-width:100%;max-height:207px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;path d="M 187.5 56 C 157.5 56 150 106 174 116 C 150 138 177 186 196.5 166 C 210 206 255 206 270 166 C 300 166 300 126 281.25 106 C 300 66 270 26 243.75 46 C 225 16 195 16 187.5 56 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 203px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Internet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="225" y="203" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Internet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 120 98.5 L 323.64 86.38" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 328.88 86.07 L 322.1 89.98 L 323.64 86.38 L 321.69 82.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 95px; margin-left: 220px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="220" y="98" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="540" y="56" width="80" height="120" rx="12" ry="12" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 116px; margin-left: 541px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM &lt;br&gt;Archive&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="580" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="330" y="56" width="50" height="120" rx="7.5" ry="7.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 116px; margin-left: 331px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Nginx&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="355" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Nginx&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 380 86 L 533.63 86" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 538.88 86 L 531.88 89.5 L 533.63 86 L 531.88 82.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 456px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="456" y="90" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="86" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 111px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM Device supporting TLS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="115" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM Device support&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 540 146 L 386.37 146" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 381.12 146 L 388.12 142.5 L 386.37 146 L 388.12 149.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 146px; margin-left: 459px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="459" y="149" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 330 146 L 126.33 124.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 121.11 123.62 L 128.44 120.88 L 126.33 124.18 L 127.7 127.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 125px; margin-left: 214px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="214" y="129" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 312 6 L 312 206 M 308 206 L 308 6 M 308 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 321px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="321" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 142 6 L 142 206 M 138 206 L 138 6 M 138 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="151" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank" rel="noopener noreferrer"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Nginx literature, this use case is referred to as &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/"&gt;SSL Termination for TCP Upstream Servers&lt;/a&gt;. Note that Nginx is providing layer 4 capability in this use case so the certificate and key configuration should not be placed under http section of the configuration file. This layer 4 capability in fact enables security configurations of all protocol that operates in upper layers and can be used in a broad range of situations. It is also noteworthy that Nginx can re-encrypt the traffic on the way out to upstream, for even tighter security control measure as outlined in this &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/"&gt;use case&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user nginx&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;worker_processes &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;error_log &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;log&lt;span style="color:#e6db74"&gt;/nginx/&lt;/span&gt;error&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt; warn&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pid &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;run&lt;span style="color:#e6db74"&gt;/nginx.pid;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;events {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; worker_connections 1024;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;stream {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; upstream dicom_backend {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server orthanc-backend:4242;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; listen 11112 ssl;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; proxy_pass dicom_backend;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ssl_certificate conf.d/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_certificate_key conf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;d&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_protocols SSLv3 TLSv1 TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_ciphers HIGH:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;aNULL:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;MD5:ECDH&lt;span style="color:#f92672"&gt;+&lt;/span&gt;AESGCM&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_cache shared:SSL:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;m&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_timeout &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;h&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_handshake_timeout &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;s&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;It is also helpful to use Nginx to terminate HTTPS traffic, using a pair of certificate and key. When testing with self-signed certificate I realized that Chrome browser has specific &lt;a href="https://support.apple.com/en-us/HT210176" class="rank-math-link"&gt;requirement&lt;/a&gt; on self-signed certificate, or it won&amp;#8217;t load the page. So the certificate has to be created as instructed &lt;a class="rank-math-link" href="https://eengstrom.github.io/musings/self-signed-tls-certs-v.-chrome-on-macos-catalina"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For better security, it is advisable that the RDS instance is provisioned in private subnet, with its data encrypted both in-transit and at-rest. Docker service should also manage sensitive information as &lt;a href="https://docs.docker.com/engine/swarm/secrets/" class="rank-math-link"&gt;secrets&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The deliverable is stored in this Github &lt;a href="https://github.com/digihunch/orthweb" class="rank-math-link"&gt;repo&lt;/a&gt;. The docker part of it can be executed on MacBook with PostgreSQL. The entire hardware stack represented by terraform code, can be executed against AWS to create required resources. Checkout README for further instruction. To emulate a modality, one will need a TLS supported DICOM application entity, &lt;a href="https://horosproject.org/" class="rank-math-link"&gt;Horos&lt;/a&gt; is a great project on MacOS to serve this purpose, both as DICOM-compliant sender and a viewer. Alternatively, consider some command-line based DICOM toolkit such as &lt;a href="https://support.dcmtk.org/redmine/projects/dcmtk" class="rank-math-link"&gt;dcmtk&lt;/a&gt;, or &lt;a href="https://sourceforge.net/projects/gdcm/" class="rank-math-link"&gt;grassroot dicom&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/docker-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How imaging devices talk to each other (in DICOM)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>