diff OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.cpp @ 4438:4a4e33c9082d

configuration options for DICOM TLS in Orthanc SCU
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 Jan 2021 16:53:35 +0100
parents d9473bd5ed43
children f4dbdb2dcba6
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.cpp	Wed Jan 06 17:27:28 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.cpp	Thu Jan 07 16:53:35 2021 +0100
@@ -27,13 +27,17 @@
 #include "../Logging.h"
 #include "../OrthancException.h"
 #include "../SerializationToolbox.h"
+#include "../SystemToolbox.h"
 #include "NetworkingCompatibility.h"
 
 #include <boost/thread/mutex.hpp>
 
-// By default, the timeout for client DICOM connections is set to 10 seconds
-static boost::mutex  defaultTimeoutMutex_;
-static uint32_t defaultTimeout_ = 10;
+// By default, the default timeout for client DICOM connections is set to 10 seconds
+static boost::mutex  defaultConfigurationMutex_;
+static uint32_t      defaultTimeout_ = 10;
+static std::string   defaultOwnPrivateKeyPath_;
+static std::string   defaultOwnCertificatePath_;
+static std::string   defaultTrustedCertificatesPath_;
 
 
 namespace Orthanc
@@ -50,25 +54,37 @@
   
   uint32_t DicomAssociationParameters::GetDefaultTimeout()
   {
-    boost::mutex::scoped_lock lock(defaultTimeoutMutex_);
+    boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
     return defaultTimeout_;
   }
 
 
+  void DicomAssociationParameters::SetDefaultParameters()
+  {
+    boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
+    timeout_ = defaultTimeout_;
+    ownPrivateKeyPath_ = defaultOwnPrivateKeyPath_;
+    ownCertificatePath_ = defaultOwnCertificatePath_;
+    trustedCertificatesPath_ = defaultTrustedCertificatesPath_;
+  }
+
+
   DicomAssociationParameters::DicomAssociationParameters() :
     localAet_("ORTHANC"),
-    timeout_(GetDefaultTimeout())
+    timeout_(0)  // Will be set by SetDefaultParameters()
   {
     remote_.SetApplicationEntityTitle("ANY-SCP");
+    SetDefaultParameters();
   }
 
     
   DicomAssociationParameters::DicomAssociationParameters(const std::string& localAet,
                                                          const RemoteModalityParameters& remote) :
     localAet_(localAet),
-    timeout_(GetDefaultTimeout())
+    timeout_(0)  // Will be set by SetDefaultParameters()
   {
     SetRemoteModality(remote);
+    SetDefaultParameters();
   }
 
   const std::string &DicomAssociationParameters::GetLocalApplicationEntityTitle() const
@@ -142,9 +158,67 @@
   }
 
 
+  void DicomAssociationParameters::CheckDicomTlsConfiguration() const
+  {
+    if (!remote_.IsDicomTlsEnabled())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls, "DICOM TLS is not enabled");
+    }
+    else if (ownPrivateKeyPath_.empty())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls,
+                             "DICOM TLS - No path to the private key of the local certificate was provided");
+    }
+    else if (ownCertificatePath_.empty())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls,
+                             "DICOM TLS - No path to the local certificate was provided");
+    }
+    else if (trustedCertificatesPath_.empty())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls,
+                             "DICOM TLS - No path to the trusted remote certificates was provided");
+    }
+  }
+  
+  void DicomAssociationParameters::SetOwnCertificatePath(const std::string& privateKeyPath,
+                                                         const std::string& certificatePath)
+  {
+    ownPrivateKeyPath_ = privateKeyPath;
+    ownCertificatePath_ = certificatePath;
+  }
+
+  void DicomAssociationParameters::SetTrustedCertificatesPath(const std::string& path)
+  {
+    trustedCertificatesPath_ = path;
+  }
+
+  const std::string& DicomAssociationParameters::GetOwnPrivateKeyPath() const
+  {
+    CheckDicomTlsConfiguration();
+    return ownPrivateKeyPath_;
+  }
+    
+  const std::string& DicomAssociationParameters::GetOwnCertificatePath() const
+  {
+    CheckDicomTlsConfiguration();
+    return ownCertificatePath_;
+  }
+
+  const std::string& DicomAssociationParameters::GetTrustedCertificatesPath() const
+  {
+    CheckDicomTlsConfiguration();
+    return trustedCertificatesPath_;
+  }
+
+
+
   static const char* const LOCAL_AET = "LocalAet";
   static const char* const REMOTE = "Remote";
-  static const char* const TIMEOUT = "Timeout";  // New in Orthanc in 1.7.0
+  static const char* const TIMEOUT = "Timeout";                           // New in Orthanc in 1.7.0
+  static const char* const OWN_PRIVATE_KEY = "OwnPrivateKey";             // New in Orthanc 1.9.0
+  static const char* const OWN_CERTIFICATE = "OwnCertificate";            // New in Orthanc 1.9.0
+  static const char* const TRUSTED_CERTIFICATES = "TrustedCertificates";  // New in Orthanc 1.9.0
 
   
   void DicomAssociationParameters::SerializeJob(Json::Value& target) const
@@ -158,6 +232,34 @@
       target[LOCAL_AET] = localAet_;
       remote_.Serialize(target[REMOTE], true /* force advanced format */);
       target[TIMEOUT] = timeout_;
+
+      // Don't write the DICOM TLS parameters if they are not required
+      if (ownPrivateKeyPath_.empty())
+      {
+        target.removeMember(OWN_PRIVATE_KEY);
+      }
+      else
+      {
+        target[OWN_PRIVATE_KEY] = ownPrivateKeyPath_;
+      }
+      
+      if (ownCertificatePath_.empty())
+      {
+        target.removeMember(OWN_CERTIFICATE);
+      }
+      else
+      {
+        target[OWN_CERTIFICATE] = ownCertificatePath_;
+      }
+      
+      if (trustedCertificatesPath_.empty())
+      {
+        target.removeMember(TRUSTED_CERTIFICATES);
+      }
+      else
+      {
+        target[TRUSTED_CERTIFICATES] = trustedCertificatesPath_;
+      }
     }
   }
 
@@ -167,11 +269,43 @@
     if (serialized.type() == Json::objectValue)
     {
       DicomAssociationParameters result;
-    
+
+      if (!serialized.isMember(REMOTE))
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
       result.remote_ = RemoteModalityParameters(serialized[REMOTE]);
       result.localAet_ = SerializationToolbox::ReadString(serialized, LOCAL_AET);
       result.timeout_ = SerializationToolbox::ReadInteger(serialized, TIMEOUT, GetDefaultTimeout());
 
+      if (serialized.isMember(OWN_PRIVATE_KEY))
+      {
+        result.ownPrivateKeyPath_ = SerializationToolbox::ReadString(serialized, OWN_PRIVATE_KEY);
+      }
+      else
+      {
+        result.ownPrivateKeyPath_.clear();
+      }
+
+      if (serialized.isMember(OWN_CERTIFICATE))
+      {
+        result.ownCertificatePath_ = SerializationToolbox::ReadString(serialized, OWN_CERTIFICATE);
+      }
+      else
+      {
+        result.ownCertificatePath_.clear();
+      }
+
+      if (serialized.isMember(TRUSTED_CERTIFICATES))
+      {
+        result.trustedCertificatesPath_ = SerializationToolbox::ReadString(serialized, TRUSTED_CERTIFICATES);
+      }
+      else
+      {
+        result.trustedCertificatesPath_.clear();
+      }
+      
       return result;
     }
     else
@@ -187,8 +321,77 @@
                       << seconds << " seconds (0 = no timeout)";
 
     {
-      boost::mutex::scoped_lock lock(defaultTimeoutMutex_);
+      boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
       defaultTimeout_ = seconds;
     }
   }
+
+
+  void DicomAssociationParameters::SetDefaultOwnCertificatePath(const std::string& privateKeyPath,
+                                                                const std::string& certificatePath)
+  {
+    if (!privateKeyPath.empty() &&
+        !certificatePath.empty())
+    {
+      CLOG(INFO, DICOM) << "Setting the default TLS certificate for DICOM SCU connections: " 
+                        << privateKeyPath << " (key), " << certificatePath << " (certificate)";
+
+      if (certificatePath.empty())
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange, "No path to the default DICOM TLS certificate was provided");
+      }
+      
+      if (privateKeyPath.empty())
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange,
+                               "No path to the private key for the default DICOM TLS certificate was provided");
+      }
+      
+      if (!SystemToolbox::IsRegularFile(privateKeyPath))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + privateKeyPath);
+      }
+
+      if (!SystemToolbox::IsRegularFile(certificatePath))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + certificatePath);
+      }
+      
+      {
+        boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
+        defaultOwnPrivateKeyPath_ = privateKeyPath;
+        defaultOwnCertificatePath_ = certificatePath;
+      }
+    }
+    else
+    {
+      boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
+      defaultOwnPrivateKeyPath_.clear();
+      defaultOwnCertificatePath_.clear();
+    }
+  }    
+
+  
+  void DicomAssociationParameters::SetDefaultTrustedCertificatesPath(const std::string& path)
+  {
+    if (!path.empty())
+    {
+      CLOG(INFO, DICOM) << "Setting the default trusted certificates for DICOM SCU connections: " << path;
+
+      if (!SystemToolbox::IsRegularFile(path))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + path);
+      }
+      
+      {
+        boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
+        defaultTrustedCertificatesPath_ = path;
+      }
+    }
+    else
+    {
+      boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
+      defaultTrustedCertificatesPath_.clear();
+    }
+  }
 }