当前位置: 首页 > backend >正文

【DICOM之加密传输】DICOM实现TLS加密传输的一些经验

本文由Markdown语法编辑器编辑完成.

1. 背景:

近期在做一个国外客户的项目, 而且涉及到了一些注册相关的需求. 在医疗领域, 有很多法律法规是要求医疗影像(包含了患者的隐私信息), 在传输和保存时, 都需要进行一定的加密处理.

因此, 最近这段时间的工作, 就涉及到了这两个方面, 一个是DICOM影像在传输过程中的加密, 一个是DICOM影像在保存时的加密.

本文先探讨, DICOM影像在传输过程中的加密.

2. 方案调研:

DICOM标准中, 第15章, 是关于安全方面的说明.
https://dicom.nema.org/medical/dicom/current/output/html/part15.html

在章节的开始部分, 提到了:

This Part of the DICOM Standard specifies Security and System Management Profiles to
which implementations may claim conformance. Security and System Management Profiles 
are defined by referencing externally developed standard protocols, such as TLS, ISCL, DHCP, and LDAP, 
with attention to their use in a system that uses DICOM Standard protocols for information interchange.DICOM 标准的这一部分规定了安全和系统管理配置文件,实施方案可声称符合这些配置文件。
安全和系统管理配置文件的定义参考了外部开发的标准协议,如 TLS、ISCL、DHCP 和 LDAP,
并关注它们在使用 DICOM 标准协议进行信息交换的系统中的使用。

在上面的说明中, 提到了加密传输时, 需要遵守的协议, 如TLS协议等.

在医疗领域, 不同的编程语言都提供了对dicom标准的支持, 主要分为:

编程语言项目支持
C++DCMTK
JAVADCM4CHEE
Pythonpydicom

目前我在项目中, 主要是用到了Java的dcm4chee和C++的dcmtk.

因此, 我调研的主要方向, 也是涉及到dcmtk和dcm4chee的.

2.1 DCMTK实现加密传输

dcmtk中, 有两个比较重要的工具, 一个是用于模拟客户端, 推送dicom影像的storescu, 另一个则是模拟服务端, 接收dicom影像的storescp. 那么, 如何使用这两个工具, 来模拟加密传输的过程呢?

在调研的过程中, 主要是参考了这篇文章的步骤, 进行了基本的验证工作.
https://www.linkedin.com/pulse/dicom-experiments-dcmtk-tls-gabriel-r%C3%BCeck

这篇主要讲述了, 如果用证书生成工具, 首先生成证书和密钥. 然后, 启动storescp时, 携带证书和密钥启动.

然后storescu在模拟客户端发送图像时, 需要携带证书和密钥发送, 这样, 才可以成功地把图像, 通过加密传输的方式, 传输到服务端.
在这里插入图片描述

根据上述博文中的基本流程, 绘制了上述的流程图.
从流程图中, 大致可以看出DICOM要想实现加密传输, 是需要服务端在启动时, 指定模拟下发的权威证书(cacert.pem), 以及基于此权威证书, 下发的证书和密钥的.
而客户端在服务端已经启动了加密传输时, 则在发送图像时, 也需要携带相应的证书和密钥, 才能实现传输过程的加密, 完成加密通讯.

基本的测试指令如下:

服务端启用加密传输:

storescp +B +xa -dhl -uf --aetitle miniPACS -d -od /data/miniPACS/TLS-DICM +tls mycert/key1 mycert/cert1 --add-cert-file myca/cacert.pem 2762

服务端的指令含义:
–aetitle: 服务端的实例名称: miniPACS;
+tls: 开启加密传输, 紧接着是: key1, cert1;
–add-cert-file: 指定权威证书的路径: cacert.pem

而客户端发送图像的指令:

storescu +tls mycert/key2 mycert/cert2 --add-cert-file myca/cacert.pem localhost 2762 +sd /dcm-files

客户端的指令含义:
+tls: 启动加密传输, 携带证书和密钥: keys2, cert2;
–add-cert-file: 携带权威证书 cacert.pem;
localhost 2762: 服务端的aeTitle和aePort;
+sd: 后面接着是本次要发送的dcm影像的文件夹名称, scan directory;

2.2 基于dcm4chee的加密传输:

dcm4chee是java语言,实现dicom标准的项目.dcm4chee如何实现加密传输,在其官网的wiki中也有说明:
https://dcm4che.atlassian.net/wiki/spaces/ee2/pages/2556078/Setting+up+DCM4CHEE+with+TLS+encryption
我主要是基于这篇文章的介绍,进行了模拟.

它的基本步骤,和dcmtk实现加密传输的步骤基本一致,也是先使用证书生成工具,生成证书和密钥.然后,服务端启动的时候,携带证书和密钥启动.客户端在向dcm4chee服务端发送图像时,携带证书和密钥.

只不过java里面,生成证书的工具,以及证书的保存格式,和之前dcmtk测试时用到的不太一样而已.
在这里插入图片描述

这里简要介绍一下大致流程:

2.2.1 keytool工具生成证书和密钥

keytool: 是 Java 开发工具包(JDK)中自带的一个命令行工具,主要用于管理 Java 密钥库(Java Key Store,JKS)和证书。

它提供了一系列功能,可用于创建、存储和管理密钥对、证书以及证书链等,在 Java 安全相关的开发和配置中非常实用。下面详细介绍其主要功能和常见使用场景。

keytool的主要功能包括:

a. 创建密钥库和密钥对

使用 keytool可以创建一个新的密钥库文件,并在其中生成密钥对。密钥对包含公钥和私钥,常用于数字签名、加密和解密等操作。

keytool -genkey -keyalg RSA --keystore node2.jks -alias node2

运行此命令后,会出现要求用户输入生成证书相关的提示,提示输入用户的:
first and last name;
name of your organizational unit;
city or locality;
state or province;
country code;

b. 导入和导出证书

keytool 可以将证书导入到密钥库中,也可以从密钥库中导出证书。

keytool -export -file node2.cert -keystore node2.jks -alias node2
keytool -import -file node2.cert -keystore trust.jks -alias node2

执行完上述的-import的指令后,便可以将证书 node2.cert, 加入到信任库 trust.jks里面.
在这里插入图片描述

可以看到上述截图的最后两行,提示: Trust this certificate? [no].
当选择yes后,即将这个 node2.cert, 加入到了trust.jks中.

2.2.2 dcm4chee启动时,携带证书和信任库启动

现在手头已经准备好了证书和信任库,那么dcm4chee, 作为一个模拟PACS (storescp)的角色,如何让它在启动的时候,携带证书和信任库启动呢?

在上面提到的链接: (https://dcm4che.atlassian.net/wiki/spaces/ee2/pages/2556078/Setting+up+DCM4CHEE+with+TLS+encryption)的第4步,是实现步骤.就是将node2.jks和trust.jks, 拷贝到dcm4chee的服务器conf目录下面.

Copy Key/Truststore files to dcm4chee installation directory:
cp node2.jks trust.jks DCM4CHEE_HOME/server/default/conf

然后再重新启动dcm4chee的server即可.

那么在dcm4chee的源码中,是如何来加载这个配置的呢?

这个过程,耗费了我很长的时间,就是到底dcm4chee如何来使用这个证书和信任库呢.咨询了好几个AI工具,虽然都给出了看似正确的逻辑,但实际调试时,却都提示各种各样的问题.

最终,我还是在dcm4chee的源码中,找到了答案.

其实就是使用了比较笨的方法,关键字搜索的功能.在VS Code中,把dcm4chee的master分支的项目加载进去后,在左侧打开搜索栏,搜索了几个跟加密传输有关的关键字.因为在wiki中,提到了信任库(trust.jks), 因此我就在搜索栏里面按照"trust.jks"进行搜索:

后来在搜索结果中,看到了比较关键的一段逻辑:
在这里插入图片描述

在这里插入图片描述

这个函数是来自于: dcm4che-tool中的: CLIUtils.java里面的: configureTLS().

第二幅截图中,展示了dcm4che如何从命令行cl中,读取到传入的参数,一个是key-store, 一个是trust-store. 也就是我们刚才生成的那两个文件: node2.jks和trust.jks.

node2.jks, 可以通过命令行工具,转化成p12格式的文件.

测试过程发现,在加载key-store文件时,既可以加载p12格式的文件,也可以直接加载jks格式的文件.因此,我就直接加载了生成的node2.jks文件.关键代码如下:

private void configureTLS(String aeTitle, String ip, Integer port) throws Exception{// 获取当前所有支持的加密套件.SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();String[] cipherSuites = factory.getSupportedCipherSuites();for (String cipherSuite : cipherSuites) {logger.info("当前连接支持的所有加密套件包括: {}", cipherSuite);}conn.setTlsCipherSuites(cipherSuites);conn.setTlsProtocols(new String[]{"TLSv1.2"});conn.setTlsNeedClientAuth(false);// String keyStoreURL = cl.getOptionValue("key-store", "resource:key.p12");// String keyStoreType =  cl.getOptionValue("key-store-type", "PKCS12");// String keyStorePass = cl.getOptionValue("key-store-pass", "secret");// String keyPass = cl.getOptionValue("key-pass", keyStorePass);// String trustStoreURL = cl.getOptionValue("trust-store", "resource:cacerts.p12");// String trustStoreType =  cl.getOptionValue("trust-store-type", "PKCS12");// String trustStorePass = cl.getOptionValue("trust-store-pass", "secret");String keyStoreURL = "/usr/share/mypacs/lib/node2.jks";String keyStoreType =  "jks";String keyStorePass = "xxxxx";String keyPass = keyStorePass;String trustStoreURL = "/usr/share/mypacs/lib/trust.jks";String trustStoreType =  "jks";String trustStorePass = "xxxxxx";try {if (!keyStoreURL.isEmpty())device.setKeyManager(SSLManagerFactory.createKeyManager(keyStoreType, keyStoreURL, keyStorePass, keyPass));device.setTrustManager(SSLManagerFactory.createTrustManager(trustStoreType, trustStoreURL, trustStorePass));logger.info("device启动前, 配置TLS相关的keyStore和trustStore完成.");} catch (GeneralSecurityException e) {throw new IOException(e);}conn.setHostname(ip);conn.setPort(port);ae.setAETitle(aeTitle);ae.setAssociationAcceptor(true);ae.addConnection(conn);ae.setSupportedCharacterSets("ISO_IR 192", "GB18030");configureTransferCapability(ae);// device存储支持dicom的网络端点的物理端信息等device.addConnection(conn);device.addApplicationEntity(ae);device.setLimitOpenAssociations(this.limitOpenAssociations);logger.info("limitOpenAssociations: " + this.limitOpenAssociations);device.setStationName(AET);device.setDimseRQHandler(createServiceRegistry());ExecutorService executorService = Executors.newCachedThreadPool();ScheduledExecutorService scheduledExecutorService =Executors.newSingleThreadScheduledExecutor();device.setScheduledExecutor(scheduledExecutorService);device.setExecutor(executorService);
}

经过这样的配置后,再启动dcm4chee或类似的java pacs的项目时,它就是携带了证书和信任库的方式启动的了.

http://www.xdnf.cn/news/7534.html

相关文章:

  • 二、【环境搭建篇】:Django 和 Vue3 开发环境准备
  • Spark离线数据处理实例
  • 20250520在全志H3平台的Nano Pi NEO CORE开发板上运行Ubuntu Core16.04.3时跑通4G模块EC20
  • 大模型——多模态检索的RAG系统架构设计
  • CentOS系统上挂载磁盘
  • 【图像大模型】Stable Diffusion 3 Medium:多模态扩散模型的技术突破与实践指南
  • CentOS Stream安装MinIO教程
  • 算力:数智时代的核心生产力引擎​
  • idea 插件开发自动发布到 nexus 私服中(脚本实例)
  • 界面控件DevExpress WinForms v24.2——PDF Viewer功能升级
  • Visual Studio 2019/2022:当前不会命中断点,还没有为该文档加载任何符号。
  • 基于海绵结构的密码杂凑算法Master
  • 云原生主要架构模式
  • C++(4)if的终极使用 +三目运算符
  • Java 08集合
  • 网络安全之网络攻击spring临时文件利用
  • 2024年热门AI趋势及回顾
  • CPQ报价系统多层战略,加快企业销售周期
  • 利用Spring Boot和Redis构建高性能缓存系统
  • List优雅分组
  • 开源CMS系统中哪些常见的安全漏洞最需要注意?
  • AWS CodePipeline+ Elastic Beanstalk(AWS中国云CI/CD)
  • HCIP实验五
  • MyBatis实战指南(一)MyBatis入门基础与利用IDEA从零开始搭建你的第一个MyBatis系统
  • linux关闭某端口暂用的进程
  • 【前端开发】Uniapp日期时间选择器:实现分钟动态步长设置
  • 链表面试题9之环形链表进阶
  • 微服务架构中的多进程通信--内存池、共享内存、socket
  • Canvas SVG BpmnJS编辑器中Canvas与SVG职能详解
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Rotating Navigation (旋转导航)