{"id":143,"date":"2020-04-26T16:03:00","date_gmt":"2020-04-26T14:03:00","guid":{"rendered":"https:\/\/caipirinha.spdns.org\/wp\/?p=143"},"modified":"2020-04-26T16:03:00","modified_gmt":"2020-04-26T14:03:00","slug":"dicom-experiments-with-dcmtk-and-tls","status":"publish","type":"post","link":"https:\/\/caipirinha.spdns.org\/wp\/?p=143","title":{"rendered":"DICOM Experiments with dcmtk and TLS"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Executive Summary<\/h2>\n\n\n\n<p>In this small article, I would like to point out how we can use the command line tools from the <a href=\"https:\/\/dicom.offis.de\/dcmtk.php.en\">dcmtk toolkit<\/a> in order to play around with <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> images, setting up a <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> recipient and how we integrate <a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> (encryption) in the whole setup.<\/p>\n\n\n\n<p>The <a href=\"https:\/\/dicom.offis.de\/dcmtk.php.en\">dcmtk toolkit<\/a> is a powerful set of command line tools which enable us to play around with <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a>, program test environments, transform images between <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> and other common formats (like JPEG or PNG) and simulate a simple <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a> or a worklist server. It does not feature all tools that you would wish to have (e.g., dealing with MP4 videos), but it&#8217;s powerful enough to start with.<\/p>\n\n\n\n<p>As always, I recommend that you execute all scripts on a properly installed Linux machine. (Windows will also do it, but it&#8217;s&#8230; uh!) Fortunately, the  <a href=\"https:\/\/dicom.offis.de\/dcmtk.php.en\">dcmtk toolkit<\/a> is available for most Linuxes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sending DICOM images<\/h2>\n\n\n\n<p>Let&#8217;s start with a simple setup. We need a folder structure like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gabriel@caipirinha:~&gt; tree \/reserve\/playground\/\n\/reserve\/playground\/\n\u251c\u2500\u2500 images\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000001\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000002\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000003\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000004\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000005\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000006\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000007\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 IM000008\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 IM000009\n\u2514\u2500\u2500 pacs<\/pre>\n\n\n\n<p>The folder <em>images<\/em> will contain some sample <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> images. Either you have some from your own medical examinations, or you download some demo pictures from the web. The folder pacs will serve as recipient of our transmitted <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> images.<br>We furthermore need two (<a href=\"https:\/\/de.wikipedia.org\/wiki\/Bash_(Shell)\">bash<\/a>) shells open, and in the first shell, we start a storescp instance:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescp +B +xa -dhl -uf -aet PACS -od \/reserve\/playground\/pacs 10400<\/pre>\n\n\n\n<p>This command enables a recipient of <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> images; it will be our &#8220;mini&#8221;  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>, and it will just store all the data it receives in the folder <strong>\/reserve\/playground\/pacs.<\/strong><\/p>\n\n\n\n<p>The storescp command has a range of options which you might explore using &#8220;man storescp&#8221; on your Linux machine. Here, we use the options:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>+B: <em>write data exactly as read<\/em>. Actually, I use this option as it seems to write directly on disk. On a weaker Raspberry Pi I did have issues with large files until I started to use this option. So: recommended.<\/li><li>+xa: <em>accept all supported transfer syntaxes<\/em>. That&#8217;s what you expect from a test system.<\/li><li>-dhl: <em>disable hostname lookup<\/em>. In our test environment, we do not want to complicate things by using DNS and reverse DNS lookups.<\/li><li>-uf: <em>generate filename from instance UID (default)<\/em>. This means that you will get files names from the instance UID. They are not exactly &#8220;telling&#8221; filenames, but they refer to some instance UID that comes from your other test systems or from a worklist or from whereever.<\/li><li>-aet: <em>set my AE title<\/em>. This is the AE title, an important identifier in the  <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> world. In reality, this would probably be a name referring to the respective hospital owning the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a> or to the manufacturer of the  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>.<\/li><\/ul>\n\n\n\n<p>You can also see that the instance is listening on TCP port 10400. This is <strong>not<\/strong> the standardized port for <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> operation; in fact, in reality you would use port 104. But as we want to test around not using the root account, we will not use <a href=\"https:\/\/www.w3.org\/Daemon\/User\/Installation\/PrivilegedPorts.html\">privileged ports<\/a>.<\/p>\n\n\n\n<p>In the second shell, we transmit a <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> image by invoking a storescu command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescu localhost 10400 \/reserve\/playground\/images\/IM000001<\/pre>\n\n\n\n<p>This will transfer the <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> image <strong>IM000001<\/strong> to the storescp and ultimately store it in the folder <strong>\/reserve\/playground\/pacs<\/strong>. The whole operation looks like in the image below. Do not get bothered by the messages of the storescu command; it just evidences that even commercially taken <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> images do not always fulfil the specs. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"958\" height=\"729\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/ohne_TLS-1.jpg\" alt=\"\" class=\"wp-image-145\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/ohne_TLS-1.jpg 958w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/ohne_TLS-1-300x228.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/ohne_TLS-1-768x584.jpg 768w\" sizes=\"auto, (max-width: 958px) 100vw, 958px\" \/><figcaption>storescp and storescu [no TLS]<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Sending DICOM images with TLS<\/h2>\n\n\n\n<p>Now, let&#8217;s switch one gear higher and use <a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> encryption for the transfer of our <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> image. We need some preparation though, and for that, we need to download a script named <strong>dcmtk_ca.pl<\/strong>. You can get that at Github, for example (<a href=\"https:\/\/github.com\/InsightSoftwareConsortium\/DCMTK\/blob\/master\/dcmtls\/tests\/dcmtk_ca.pl\">here<\/a>). Save it and make it executable. Of course, you need a Perl interpreter, but as you work on a well installed Linux machine, it is simply available.<br>We first need to create a (self-generated) root certificate and subsequently two key pairs. On this <a href=\"https:\/\/forum.dcmtk.org\/viewtopic.php?t=2946\">blog post<\/a>, you can find the commands for that, and those are:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.\/dcmtk_ca.pl newca \/reserve\/playground\/myca<br>mkdir \/reserve\/playground\/mycert<br>.\/dcmtk_ca.pl mkcert -des no \/reserve\/playground\/myca\/ \/reserve\/playground\/mycert\/cert1 \/reserve\/playground\/mycert\/key1<br>.\/dcmtk_ca.pl mkcert -des no \/reserve\/playground\/myca\/ \/reserve\/playground\/mycert\/cert2 \/reserve\/playground\/mycert\/key2<\/pre>\n\n\n\n<p>During the course of the execution of these commands, you will be asked several questions which refer to attributes used in <a href=\"https:\/\/de.wikipedia.org\/wiki\/X.509\">X.509<\/a> certificates, like &#8220;organizational unit&#8221;, &#8230; The values that you give do not matter here in our test environment. But for certificates used in production environment, those attributes should be populated with meaningful values, of course.<\/p>\n\n\n\n<p>The command sequence above has created our root certificate in the file <strong>\/reserve\/playground\/myca\/cacert.pem<\/strong>, and two key pairs in the folder  <strong>\/reserve\/playground\/mycert\/<\/strong>. We will now repeat the transmission of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> image as shown in the previous chapter, however, using a <a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> connection. Hence, in our first shell, we will issue the command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescp +B +xa -dhl -uf --aetitle PACS -od \/reserve\/playground\/pacs +tls \/reserve\/playground\/mycert\/key1 \/reserve\/playground\/mycert\/cert1 --add-cert-file \/reserve\/playground\/myca\/cacert.pem 2762<\/pre>\n\n\n\n<p>which will  be our &#8220;mini&#8221; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a> again, and in the second shell we issue the command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescu +tls \/reserve\/playground\/mycert\/key2 \/reserve\/playground\/mycert\/cert2 --add-cert-file \/reserve\/playground\/myca\/cacert.pem localhost 2762 \/reserve\/playground\/images\/IM000001<\/pre>\n\n\n\n<p>You can see that for this example, I have used TCP port 2762 which is also the recommendation from <a href=\"http:\/\/dicom.nema.org\/dicom\/2013\/output\/chtml\/part15\/sect_B.3.html\">NEMA<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"711\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS-1024x711.jpg\" alt=\"\" class=\"wp-image-149\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS-1024x711.jpg 1024w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS-300x208.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS-768x533.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS.jpg 1047w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>storescp and storescu [with TLS], self-generated certificates<\/figcaption><\/figure>\n\n\n\n<p>In this example, we have used a self-generated root certificate (<strong>cacert.pem<\/strong>) and derived two key pairs from this. But in real life, you might have an existing certificate that shall be used. My web server (<a href=\"https:\/\/caipirinha.spdns.org\/\">caipirinha.spdns.org<\/a>) for example, has a real certificate (thanks to the <a href=\"https:\/\/letsencrypt.org\/\">Let&#8217;s Encrypt<\/a> initiative), and now, we want to use this certificate for the storescp sequence simulating our &#8220;mini&#8221; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>. In order to do so, I will start the storescp instance as <em>root<\/em> user (because on my machine, only <em>root<\/em> can read the server key). On a production system, this is exactly what you <strong>shall not<\/strong> do; rather than that, you will have to find a way for you to make the server key available for the user which shall run the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>, and this user <strong>should not<\/strong> be a normal user. But for our test here, this is OK. So, in that case, the storescp command would be:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescp +B +xa -dhl -uf --aetitle PACS -od \/reserve\/playground\/pacs +tls \/etc\/letsencrypt\/live\/caipirinha.spdns.org\/privkey.pem \/etc\/letsencrypt\/live\/caipirinha.spdns.org\/cert.pem +cf \/reserve\/playground\/myca\/cacert.pem 2762<\/pre>\n\n\n\n<p>The corresponding storescu command is:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">storescu +tls \/reserve\/playground\/mycert\/key2 \/reserve\/playground\/mycert\/cert2 --add-cert-file \/reserve\/playground\/caipirinha-spdns-org-zertifikatskette.pem localhost 2762 \/reserve\/playground\/images\/IM000001<\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"711\" src=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS_caipirinha-1024x711.jpg\" alt=\"\" class=\"wp-image-150\" srcset=\"https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS_caipirinha-1024x711.jpg 1024w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS_caipirinha-300x208.jpg 300w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS_caipirinha-768x533.jpg 768w, https:\/\/caipirinha.spdns.org\/wp\/wp-content\/uploads\/mit_TLS_caipirinha.jpg 1047w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption> storescp and storescu [with TLS], one real certificate and one self-generated certificate<\/figcaption><\/figure>\n\n\n\n<p>You will realize here that in this example, the storescp instance has the root certificate of the storescu instance (which is the self-generated root certificate) whereas the storescu instance has the root ceritifcate chain of the storescp instance (which I extracted using the <a href=\"https:\/\/www.mozilla.org\/de\/firefox\/new\/\">Firefox<\/a> browser and accessing my own page <a href=\"https:\/\/caipirinha.spdns.org\/\">https:\/\/caipirinha.spdns.org\/)<\/a>. The reason for this crossover mentioning of the root certificates is simple:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The <strong>storescp<\/strong> instance needs the root certificate (chain) of the storescu instance in order to verify whether the certificate that the <strong>storescu<\/strong> instance presents is valid.<\/li><li>The <strong>storescu<\/strong> instance needs to the root certificate (chain) of the storescp instance in order to verify  whether the certificate that the <strong>storescp<\/strong> instance presents is valid. <\/li><\/ul>\n\n\n\n<p>This might put a problem for hospital installations. Very rarely would you be able to add self-generated root certificates to your <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>, and therefore, the recommendation is that, when using <a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> in your hospital environment, the probably best approach is that all certificates (key pairs) are generated from the very same certificate authority that was used to issue the key pair for the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>. How certificate and key information is set up in various <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a>-capable devices depend on the device. In one device I came across, all related certificates and keys could be saved on the hard disk where I created a folder D:\\Zertifikate and subsequently entered the related information in a <a href=\"https:\/\/de.wikipedia.org\/wiki\/JavaScript_Object_Notation\">JSON<\/a> object for the <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> configuration. That looked like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n ...\n \"DefaultSendOptions\": {\n    \"sendExecutable\": \"storescu-tls.exe\",\n    ...\n    },\n    \"args\": &#91;\n      \"--log-level\",\n      \"info\",\n      ...\n      \"+tls\",\n      \"D:\/Zertifikate\/key\",\n      \"D:\/Zertifikate\/cert\",\n      \"+cf\",\n      \"D:\/Zertifikate\/caipirinha-ca-chain.pem\"\n    ],\n    ...\n}<\/code><\/pre>\n\n\n\n<p>In this example, I still used a self-created certificate and key for the <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a>-enabled device that shall talk to the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a>, but the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a> itself uses a real certificate. You can see that the <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a>-enabled device also uses the storescu command in order to transmit <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> data. This is not unusual, as most device manufacturers would use the <a href=\"https:\/\/dicom.offis.de\/dcmtk.php.en\">dcmtk toolkit<\/a> rather than programming <a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> routines from scratch.<\/p>\n\n\n\n<p>I hope that this small article can help you to better understand the two related commands storescp and storescu with the focus on <a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> encryption during the communication.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Links<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/dicom.offis.de\/\">DICOM@Offis<\/a><\/li><li><a href=\"https:\/\/dicom.offis.de\/dcmtk.php.en\">dcmtk toolkit<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Key Words<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/DICOM\">DICOM<\/a> (<strong>D<\/strong>igital <strong>I<\/strong>maging and <strong>Co<\/strong>mmunications in <strong>M<\/strong>edicine)<\/li><li><a href=\"https:\/\/de.wikipedia.org\/wiki\/JavaScript_Object_Notation\">JSON<\/a> (<strong>J<\/strong>ava<strong>S<\/strong>cript <strong>O<\/strong>bject <strong>N<\/strong>otation)<\/li><li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Picture_archiving_and_communication_system\">PACS<\/a> (<strong>P<\/strong>icture <strong>A<\/strong>rchiving and <strong>C<\/strong>ommunication <strong>S<\/strong>ystem)<\/li><li><a href=\"https:\/\/de.wikipedia.org\/wiki\/Transport_Layer_Security\">TLS<\/a> (<strong>T<\/strong>ransport <strong>L<\/strong>ayer <strong>S<\/strong>ecurity)<\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Executive Summary In this small article, I would like to point out how we can use the command line tools from the dcmtk toolkit in order to play around with DICOM images, setting up a DICOM recipient and how we integrate TLS (encryption) in the whole setup. The dcmtk toolkit is a powerful set of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35],"tags":[81],"class_list":["post-143","post","type-post","status-publish","format-standard","hentry","category-it","tag-dicom"],"_links":{"self":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=143"}],"version-history":[{"count":7,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/143\/revisions"}],"predecessor-version":[{"id":154,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=\/wp\/v2\/posts\/143\/revisions\/154"}],"wp:attachment":[{"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/caipirinha.spdns.org\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}