changeset 2022:fefbe71c2272

Possibility to use PKCS#11 authentication for hardware security modules with Orthanc peers
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 17 Jun 2016 17:09:50 +0200
parents bd143a77eb7a
children 7fe860db9664
files CMakeLists.txt Core/HttpClient.cpp Core/HttpClient.h Core/WebServiceParameters.cpp Core/WebServiceParameters.h NEWS OrthancServer/OrthancInitialization.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Include/orthanc/OrthancCPlugin.h Resources/CMake/LibCurlConfiguration.cmake Resources/CMake/LibP11Configuration.cmake Resources/CMake/OpenSslConfiguration.cmake Resources/Configuration.json
diffstat 13 files changed, 395 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Jun 15 17:20:52 2016 +0200
+++ b/CMakeLists.txt	Fri Jun 17 17:09:50 2016 +0200
@@ -32,6 +32,7 @@
 SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Build the ServeFolders plugin")
 SET(BUILD_MODALITY_WORKLISTS ON CACHE BOOL "Build the sample plugin to serve modality worklists")
 SET(USE_DCMTK_361 OFF CACHE BOOL "Use forthcoming DCMTK version 3.6.1 in static builds (instead of 3.6.0)")
+SET(ENABLE_PKCS11 OFF CACHE BOOL "Enable PKCS#11 for HTTPS client authentication using hardware security modules and smart cards")
 
 # Advanced parameters to fine-tune linking against system libraries
 SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
@@ -46,7 +47,8 @@
 SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl")
 SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL")
 SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib")
-SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml)")
+SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml")
+SET(USE_SYSTEM_LIBP11 ON CACHE BOOL "Use the system version of libp11 (PKCS#11 wrapper library)")
 
 # Experimental options
 SET(USE_PUGIXML ON CACHE BOOL "Use the Pugixml parser (turn off only for debug)")
@@ -77,7 +79,6 @@
 
 
 
-
 #####################################################################
 ## List of source files
 #####################################################################
@@ -331,6 +332,18 @@
 endif()
 
 
+if (ENABLE_PKCS11)
+  if (ENABLE_SSL)
+    add_definitions(-DORTHANC_PKCS11_ENABLED=1)
+    include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibP11Configuration.cmake)
+  else()
+    message(FATAL_ERROR "OpenSSL is required to enable PKCS#11")
+  endif()
+else()
+  add_definitions(-DORTHANC_PKCS11_ENABLED=0)  
+endif()
+
+
 
 #####################################################################
 ## Autogeneration of files
@@ -418,6 +431,7 @@
   ${PUGIXML_SOURCES}
   ${SQLITE_SOURCES}
   ${ZLIB_SOURCES}
+  ${LIBP11_SOURCES}
 
   ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c
   ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp
--- a/Core/HttpClient.cpp	Wed Jun 15 17:20:52 2016 +0200
+++ b/Core/HttpClient.cpp	Fri Jun 17 17:09:50 2016 +0200
@@ -43,6 +43,21 @@
 #include <boost/thread/mutex.hpp>
 
 
+#if ORTHANC_PKCS11_ENABLED == 1
+
+#include <openssl/engine.h>
+#include <libp11.h>
+
+// Include the "libengine-pkcs11-openssl" from the libp11 package
+extern "C"
+{
+#pragma GCC diagnostic error "-fpermissive"
+#include <libp11/eng_front.c>
+}
+
+#endif
+
+
 extern "C"
 {
   static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status)
@@ -82,10 +97,42 @@
     std::string     httpsCACertificates_;
     std::string     proxy_;
     long            timeout_;
+    bool            pkcs11Initialized_;
+
+#if ORTHANC_PKCS11_ENABLED == 1
+    static ENGINE* LoadPkcs11Engine()
+    {
+      // This function mimics the "ENGINE_load_dynamic" function from
+      // OpenSSL, in file "crypto/engine/eng_dyn.c"
+
+      ENGINE* engine = ENGINE_new();
+      if (!engine)
+      {
+        LOG(ERROR) << "Cannot create an OpenSSL engine for PKCS11";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (!bind_helper(engine) ||
+          !ENGINE_add(engine))
+      {
+        LOG(ERROR) << "Cannot initialize the OpenSSL engine for PKCS11";
+        ENGINE_free(engine);
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      // If the "ENGINE_add" worked, it gets a structural
+      // reference. We release our just-created reference.
+      ENGINE_free(engine);
+
+      assert(!strcmp("pkcs11", PKCS11_ENGINE_ID));
+      return ENGINE_by_id(PKCS11_ENGINE_ID);
+    }
+#endif
 
     GlobalParameters() : 
       httpsVerifyPeers_(true),
-      timeout_(0)
+      timeout_(0),
+      pkcs11Initialized_(false)
     {
     }
 
@@ -144,6 +191,69 @@
       boost::mutex::scoped_lock lock(mutex_);
       return timeout_;
     }
+
+    bool IsPkcs11Initialized()
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+      return pkcs11Initialized_;
+    }
+
+
+#if ORTHANC_PKCS11_ENABLED == 1
+    void InitializePkcs11(const std::string& module,
+                          const std::string& pin,
+                          bool verbose)
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+
+      if (pkcs11Initialized_)
+      {
+        LOG(ERROR) << "The PKCS11 engine has already been initialized";
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+
+      if (module.empty() ||
+          !Toolbox::IsRegularFile(module))
+      {
+        LOG(ERROR) << "The PKCS11 module must be a path to one shared library (DLL or .so)";
+        throw OrthancException(ErrorCode_InexistentFile);
+      }
+
+      ENGINE* engine = LoadPkcs11Engine();
+      if (!engine)
+      {
+        LOG(ERROR) << "Cannot create an OpenSSL engine for PKCS11";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", module.c_str(), 0))
+      {
+        LOG(ERROR) << "Cannot configure the OpenSSL dynamic engine for PKCS11";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (verbose)
+      {
+        ENGINE_ctrl_cmd_string(engine, "VERBOSE", NULL, 0);
+      }
+
+      if (!pin.empty() &&
+          !ENGINE_ctrl_cmd_string(engine, "PIN", pin.c_str(), 0)) 
+      {
+        LOG(ERROR) << "Cannot set the PIN code for PKCS11";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+  
+      if (!ENGINE_init(engine))
+      {
+        LOG(ERROR) << "Cannot initialize the OpenSSL dynamic engine for PKCS11";
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      LOG(WARNING) << "The PKCS11 engine has been successfully initialized";
+      pkcs11Initialized_ = true;
+    }
+#endif
   };
 
 
@@ -242,7 +352,8 @@
 
   HttpClient::HttpClient() : 
     pimpl_(new PImpl), 
-    verifyPeers_(true)
+    verifyPeers_(true),
+    pkcs11Enabled_(false)
   {
     Setup();
   }
@@ -269,6 +380,8 @@
                            service.GetCertificateKeyPassword());
     }
 
+    SetPkcs11Enabled(service.IsPkcs11Enabled());
+
     SetUrl(service.GetUrl() + uri);
   }
 
@@ -329,8 +442,9 @@
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
 
+#if ORTHANC_SSL_ENABLED == 1
     // Setup HTTPS-related options
-#if ORTHANC_SSL_ENABLED == 1
+
     if (verifyPeers_)
     {
       CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str()));
@@ -344,6 +458,57 @@
     }
 #endif
 
+    // Setup the HTTPS client certificate
+    if (!clientCertificateFile_.empty() &&
+        pkcs11Enabled_)
+    {
+      LOG(ERROR) << "Cannot enable both client certificates and PKCS#11 authentication";
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (pkcs11Enabled_)
+    {
+#if ORTHANC_PKCS11_ENABLED == 1
+      if (GlobalParameters::GetInstance().IsPkcs11Initialized())
+      {
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLENGINE, "pkcs11"));
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "ENG"));
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "ENG"));
+      }
+      else
+      {
+        LOG(ERROR) << "Cannot use PKCS11 for a HTTPS request, because it has not been initialized";
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+#else
+      LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS11";
+      throw OrthancException(ErrorCode_InternalError);
+#endif
+    }
+    else if (!clientCertificateFile_.empty())
+    {
+#if ORTHANC_SSL_ENABLED == 1
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM"));
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str()));
+
+      if (!clientCertificateKeyPassword_.empty())
+      {
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str()));
+      }
+
+      // NB: If no "clientKeyFile_" is provided, the key must be
+      // prepended to the certificate file
+      if (!clientCertificateKeyFile_.empty())
+      {
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM"));
+        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str()));
+      }
+#else
+      LOG(ERROR) << "This version of Orthanc is compiled without OpenSSL support, cannot use HTTPS client authentication";
+      throw OrthancException(ErrorCode_InternalError);
+#endif
+    }
+
     // Reset the parameters from previous calls to Apply()
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->userHeaders_));
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L));
@@ -376,26 +541,6 @@
       CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, proxy_.c_str()));
     }
 
-    // Set the HTTPS client certificate
-    if (!clientCertificateFile_.empty())
-    {
-      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM"));
-      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str()));
-
-      if (!clientCertificateKeyPassword_.empty())
-      {
-        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str()));
-      }
-
-      // NB: If no "clientKeyFile_" is provided, the key must be
-      // prepended to the certificate file
-      if (!clientCertificateKeyFile_.empty())
-      {
-        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM"));
-        CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str()));
-      }
-    }
-
     switch (method_)
     {
     case HttpMethod_Get:
@@ -540,6 +685,12 @@
   
   void HttpClient::GlobalInitialize()
   {
+#if ORTHANC_SSL_ENABLED == 1
+    CheckCode(curl_global_init(CURL_GLOBAL_ALL));
+#else
+    CheckCode(curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL));
+#endif
+
     CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT));
   }
 
@@ -605,4 +756,19 @@
     clientCertificateKeyFile_ = certificateKeyFile;
     clientCertificateKeyPassword_ = certificateKeyPassword;
   }
+
+
+  void HttpClient::InitializePkcs11(const std::string& module,
+                                    const std::string& pin,
+                                    bool verbose)
+  {
+#if ORTHANC_PKCS11_ENABLED == 1
+    LOG(INFO) << "Initializing PKCS#11 using " << module 
+              << (pin.empty() ? "(no PIN provided)" : "(PIN is provided)");
+    GlobalParameters::GetInstance().InitializePkcs11(module, pin, verbose);    
+#else
+    LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS11";
+    throw OrthancException(ErrorCode_InternalError);
+#endif
+  }
 }
--- a/Core/HttpClient.h	Wed Jun 15 17:20:52 2016 +0200
+++ b/Core/HttpClient.h	Fri Jun 17 17:09:50 2016 +0200
@@ -62,6 +62,7 @@
     std::string clientCertificateFile_;
     std::string clientCertificateKeyFile_;
     std::string clientCertificateKeyPassword_;
+    bool pkcs11Enabled_;
 
     void Setup();
 
@@ -179,6 +180,16 @@
                               const std::string& certificateKeyFile,
                               const std::string& certificateKeyPassword);
 
+    void SetPkcs11Enabled(bool enabled)
+    {
+      pkcs11Enabled_ = enabled;
+    }
+
+    bool IsPkcs11Enabled() const
+    {
+      return pkcs11Enabled_;
+    }
+
     const std::string& GetClientCertificateFile() const
     {
       return clientCertificateFile_;
@@ -198,6 +209,10 @@
   
     static void GlobalFinalize();
 
+    static void InitializePkcs11(const std::string& module,
+                                 const std::string& pin,
+                                 bool verbose);
+
     static void ConfigureSsl(bool httpsVerifyPeers,
                              const std::string& httpsCACertificates);
 
--- a/Core/WebServiceParameters.cpp	Wed Jun 15 17:20:52 2016 +0200
+++ b/Core/WebServiceParameters.cpp	Fri Jun 17 17:09:50 2016 +0200
@@ -43,11 +43,20 @@
 {
   WebServiceParameters::WebServiceParameters() : 
     advancedFormat_(false),
-    url_("http://localhost:8042/")
+    url_("http://localhost:8042/"),
+    pkcs11Enabled_(false)
   {
   }
 
 
+  void WebServiceParameters::ClearClientCertificate()
+  {
+    certificateFile_.clear();
+    certificateKeyFile_.clear();
+    certificateKeyPassword_.clear();
+  }
+
+
   void WebServiceParameters::SetClientCertificate(const std::string& certificateFile,
                                                   const std::string& certificateKeyFile,
                                                   const std::string& certificateKeyPassword)
@@ -92,6 +101,7 @@
     assert(peer.isArray());
 
     advancedFormat_ = false;
+    pkcs11Enabled_ = false;
 
     if (peer.size() != 1 && 
         peer.size() != 3)
@@ -167,6 +177,18 @@
                            GetStringMember(peer, "CertificateKeyFile", ""),
                            GetStringMember(peer, "CertificateKeyPassword", ""));
     }
+
+    if (peer.isMember("Pkcs11"))
+    {
+      if (peer["Pkcs11"].type() == Json::booleanValue)
+      {
+        pkcs11Enabled_ = peer["Pkcs11"].asBool();
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+    }
   }
 
 
--- a/Core/WebServiceParameters.h	Wed Jun 15 17:20:52 2016 +0200
+++ b/Core/WebServiceParameters.h	Fri Jun 17 17:09:50 2016 +0200
@@ -47,6 +47,7 @@
     std::string certificateFile_;
     std::string certificateKeyFile_;
     std::string certificateKeyPassword_;
+    bool        pkcs11Enabled_;
 
     void FromJsonArray(const Json::Value& peer);
 
@@ -85,6 +86,8 @@
       password_ = password;
     }
 
+    void ClearClientCertificate();
+
     void SetClientCertificate(const std::string& certificateFile,
                               const std::string& certificateKeyFile,
                               const std::string& certificateKeyPassword);
@@ -104,6 +107,16 @@
       return certificateKeyPassword_;
     }
 
+    void SetPkcs11Enabled(bool pkcs11Enabled)
+    {
+      pkcs11Enabled_ = pkcs11Enabled;
+    }
+
+    bool IsPkcs11Enabled() const
+    {
+      return pkcs11Enabled_;
+    }
+
     void FromJson(const Json::Value& peer);
 
     void ToJson(Json::Value& value) const;
--- a/NEWS	Wed Jun 15 17:20:52 2016 +0200
+++ b/NEWS	Fri Jun 17 17:09:50 2016 +0200
@@ -5,6 +5,7 @@
 -------
 
 * HTTPS client certificates can be associated with Orthanc peers to enhance security over Internet
+* Possibility to use PKCS#11 authentication for hardware security modules with Orthanc peers
 * New option "--logfile" to output the Orthanc log to the given file
 * Support of SIGHUP signal (restart Orthanc only if the configuration files have changed)
 
--- a/OrthancServer/OrthancInitialization.cpp	Wed Jun 15 17:20:52 2016 +0200
+++ b/OrthancServer/OrthancInitialization.cpp	Fri Jun 17 17:09:50 2016 +0200
@@ -424,6 +424,48 @@
   }
 
 
+  static void ConfigurePkcs11(const Json::Value& config)
+  {
+    if (config.type() != Json::objectValue ||
+        !config.isMember("Module") ||
+        config["Module"].type() != Json::stringValue)
+    {
+      LOG(ERROR) << "No path to the PKCS#11 module (DLL or .so) is provided for HTTPS client authentication";
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    std::string pin;
+    if (config.isMember("Pin"))
+    {
+      if (config["Pin"].type() == Json::stringValue)
+      {
+        pin = config["Pin"].asString();
+      }
+      else
+      {
+        LOG(ERROR) << "The PIN number in the PKCS#11 configuration must be a string";
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+    }
+
+    bool verbose = false;
+    if (config.isMember("Verbose"))
+    {
+      if (config["Verbose"].type() == Json::booleanValue)
+      {
+        verbose = config["Verbose"].asBool();
+      }
+      else
+      {
+        LOG(ERROR) << "The Verbose option in the PKCS#11 configuration must be a Boolean";
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+    }
+
+    HttpClient::InitializePkcs11(config["Module"].asString(), pin, verbose);
+  }
+
+
 
   void OrthancInitialize(const char* configurationFile)
   {
@@ -435,10 +477,6 @@
     SSL_load_error_strings();
     OpenSSL_add_all_algorithms();
     ERR_load_crypto_strings();
-
-    curl_global_init(CURL_GLOBAL_ALL);
-#else
-    curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL);
 #endif
 
     InitializeServerEnumerations();
@@ -447,6 +485,11 @@
     ReadGlobalConfiguration(configurationFile);
     ValidateGlobalConfiguration();
 
+    if (configuration_.isMember("Pkcs11"))
+    {
+      ConfigurePkcs11(configuration_["Pkcs11"]);
+    }
+
     HttpClient::GlobalInitialize();
 
     RegisterUserMetadata();
@@ -488,8 +531,6 @@
     DJDecoderRegistration::cleanup();
 #endif
 
-    curl_global_cleanup();
-
 #if ORTHANC_SSL_ENABLED == 1
     // Finalize OpenSSL
     // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
--- a/Plugins/Engine/OrthancPlugins.cpp	Wed Jun 15 17:20:52 2016 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Fri Jun 17 17:09:50 2016 +0200
@@ -1811,6 +1811,8 @@
       client.SetClientCertificate(certificate, key, password);
     }
 
+    client.SetPkcs11Enabled(p.pkcs11);
+
     for (uint32_t i = 0; i < p.headersCount; i++)
     {
       if (p.headersKeys[i] == NULL ||
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Wed Jun 15 17:20:52 2016 +0200
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Jun 17 17:09:50 2016 +0200
@@ -4946,6 +4946,7 @@
     const char*                 certificateFile;
     const char*                 certificateKeyFile;
     const char*                 certificateKeyPassword;
+    uint8_t                     pkcs11;
   } _OrthancPluginCallHttpClient2;
 
 
@@ -4975,6 +4976,7 @@
    * (can be <tt>NULL</tt> if no client certificate or if not using HTTPS).
    * @param certificateKeyPassword Password to unlock the key of the client certificate 
    * (can be <tt>NULL</tt> if no client certificate or if not using HTTPS).
+   * @param pkcs11 Enable PKCS#11 client authentication for hardware security modules and smart cards.
    * @return 0 if success, or the error code if failure.
    **/
   ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginHttpClient(
@@ -4993,7 +4995,8 @@
     uint32_t                    timeout,
     const char*                 certificateFile,
     const char*                 certificateKeyFile,
-    const char*                 certificateKeyPassword)
+    const char*                 certificateKeyPassword,
+    uint8_t                     pkcs11)
   {
     _OrthancPluginCallHttpClient2 params;
     memset(&params, 0, sizeof(params));
@@ -5013,6 +5016,7 @@
     params.certificateFile = certificateFile;
     params.certificateKeyFile = certificateKeyFile;
     params.certificateKeyPassword = certificateKeyPassword;
+    params.pkcs11 = pkcs11;
 
     return context->InvokeService(context, _OrthancPluginService_CallHttpClient2, &params);
   }
--- a/Resources/CMake/LibCurlConfiguration.cmake	Wed Jun 15 17:20:52 2016 +0200
+++ b/Resources/CMake/LibCurlConfiguration.cmake	Fri Jun 17 17:09:50 2016 +0200
@@ -35,6 +35,7 @@
     add_definitions(
       #-DHAVE_LIBSSL=1
       -DUSE_OPENSSL=1
+      -DHAVE_OPENSSL_ENGINE_H=1
       -DUSE_SSLEAY=1
       )
   endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LibP11Configuration.cmake	Fri Jun 17 17:09:50 2016 +0200
@@ -0,0 +1,52 @@
+SET(LIBP11_SOURCES_DIR ${CMAKE_BINARY_DIR}/libp11-0.4.0)
+SET(LIBP11_URL "www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/beid/libp11-0.4.0.tar.gz")
+SET(LIBP11_MD5 "00b3e41db5be840d822bda12f3ab2ca7")
+DownloadPackage(${LIBP11_MD5} ${LIBP11_URL} "${LIBP11_SOURCES_DIR}")
+
+file(COPY
+  ${LIBP11_SOURCES_DIR}/src/eng_front.c
+  DESTINATION ${AUTOGENERATED_DIR}/libp11)
+
+
+if (STATIC_BUILD OR NOT USE_SYSTEM_LIBP11)
+  include_directories(${LIBP11_SOURCES_DIR}/src)
+
+  set(LIBP11_SOURCES 
+    ${LIBP11_SOURCES_DIR}/src/eng_back.c
+    #${LIBP11_SOURCES_DIR}/src/eng_front.c
+    ${LIBP11_SOURCES_DIR}/src/eng_parse.c
+    ${LIBP11_SOURCES_DIR}/src/libpkcs11.c
+    ${LIBP11_SOURCES_DIR}/src/p11_attr.c
+    ${LIBP11_SOURCES_DIR}/src/p11_cert.c
+    ${LIBP11_SOURCES_DIR}/src/p11_ec.c
+    ${LIBP11_SOURCES_DIR}/src/p11_err.c
+    ${LIBP11_SOURCES_DIR}/src/p11_front.c
+    ${LIBP11_SOURCES_DIR}/src/p11_key.c
+    ${LIBP11_SOURCES_DIR}/src/p11_load.c
+    ${LIBP11_SOURCES_DIR}/src/p11_misc.c
+    ${LIBP11_SOURCES_DIR}/src/p11_rsa.c
+    ${LIBP11_SOURCES_DIR}/src/p11_slot.c
+    )
+
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+    list(APPEND LIBP11_SOURCES 
+      ${LIBP11_SOURCES_DIR}/src/atfork.c
+      )
+  endif()
+
+else()
+  check_include_file_cxx(libp11.h HAVE_LIBP11_H)
+  if (NOT HAVE_LIBP11_H)
+    message(FATAL_ERROR "Please install the libp11-dev package")
+  endif()
+
+  check_library_exists(p11 PKCS11_login "" HAVE_LIBP11_LIB)
+  if (NOT HAVE_LIBP11_LIB)
+    message(FATAL_ERROR "Please install the libp11-dev package")
+  endif()
+
+  link_libraries(p11)
+endif()
--- a/Resources/CMake/OpenSslConfiguration.cmake	Wed Jun 15 17:20:52 2016 +0200
+++ b/Resources/CMake/OpenSslConfiguration.cmake	Fri Jun 17 17:09:50 2016 +0200
@@ -25,9 +25,6 @@
     -DOPENSSL_NO_BF 
     -DOPENSSL_NO_CAMELLIA
     -DOPENSSL_NO_CAST 
-    -DOPENSSL_NO_EC
-    -DOPENSSL_NO_ECDH
-    -DOPENSSL_NO_ECDSA
     -DOPENSSL_NO_EC_NISTP_64_GCC_128
     -DOPENSSL_NO_GMP
     -DOPENSSL_NO_GOST
@@ -99,6 +96,21 @@
     ${OPENSSL_SOURCES_DIR}/ssl
     )
 
+  if (ENABLE_PKCS11)
+    list(APPEND OPENSSL_SOURCES_SUBDIRS
+      # EC, ECDH and ECDSA are necessary for PKCS11
+      ${OPENSSL_SOURCES_DIR}/crypto/ec
+      ${OPENSSL_SOURCES_DIR}/crypto/ecdh
+      ${OPENSSL_SOURCES_DIR}/crypto/ecdsa
+      )
+  else()
+    add_definitions(
+      -DOPENSSL_NO_EC
+      -DOPENSSL_NO_ECDH
+      -DOPENSSL_NO_ECDSA
+      )
+  endif()
+
   foreach(d ${OPENSSL_SOURCES_SUBDIRS})
     AUX_SOURCE_DIRECTORY(${d} OPENSSL_SOURCES)
   endforeach()
@@ -181,8 +193,10 @@
     ${OPENSSL_SOURCES_DIR}/crypto/x509v3/v3nametest.c
     ${OPENSSL_SOURCES_DIR}/crypto/ssl/heartbeat_test.c
     ${OPENSSL_SOURCES_DIR}/crypto/constant_time_test.c
+    ${OPENSSL_SOURCES_DIR}/crypto/ec/ecp_nistz256_table.c
     )
 
+
   if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
     set_source_files_properties(
       ${OPENSSL_SOURCES}
--- a/Resources/Configuration.json	Wed Jun 15 17:20:52 2016 +0200
+++ b/Resources/Configuration.json	Fri Jun 17 17:09:50 2016 +0200
@@ -171,7 +171,8 @@
     /**
      * This is another, more advanced format to define Orthanc
      * peers. It notably allows to specify a HTTPS client certificate
-     * in the PEM format, as in the "--cert" option of curl.
+     * in the PEM format (as in the "--cert" option of curl), or to
+     * enable PKCS#11 authentication for smart cards.
      **/
     // "peer" : {
     //   "Url" : "http://localhost:8043/",
@@ -179,7 +180,8 @@
     //   "Password" : "alicePassword",
     //   "CertificateFile" : "client.crt",
     //   "CertificateKeyFile" : "client.key",
-    //   "CertificateKeyPassword" : "certpass"
+    //   "CertificateKeyPassword" : "certpass",
+    //   "Pkcs11" : false
     // }
   },
 
@@ -288,6 +290,16 @@
   // (such as PatientName). By default, the search is
   // case-insensitive, which does not follow the DICOM standard.
   "CaseSensitivePN" : false,
+
+  // Configure PKCS#11 to use hardware security modules (HSM) and
+  // smart cards when carrying on HTTPS client authentication.
+  /**
+     "Pkcs11" : {
+       "Module" : "/usr/local/lib/libbeidpkcs11.so",
+       "Pin" : "1234",
+       "Verbose" : true
+     }
+   **/
   
   // Register a new tag in the dictionary of DICOM tags that are known
   // to Orthanc. Each line must contain the tag (formatted as 2