Hello everyone!
I am having an issue I cannot wrap my head around. I have configured Orthanc with mTLS with appropriate trusted certificates to three applications. Specific to TLS, my configuration looks like:
"DicomTlsCertificate": "/etc/orthanc/tls/orthanc.crt",
"DicomTlsEnabled": true,
"DicomTlsPrivateKey": "/etc/orthanc/tls/orthanc.key",
"DicomTlsTrustedCertificates": "/etc/orthanc/tls/trusted.crt",
This Orthanc is deployed in a K8s cluster and DICOM port 4242 is exposed via a NodePort. Complete configuration posted at the bottom.
Certificate Generation
All self-signed certificates were generated with
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout application_1.key -out application_1.crt -subj "/CN=app1.iesbx"
Working Commands (echoscu
, storescu
, findscu
)
Sending C-ECHO command to Orthanc server works, and logs the following:
$ echoscu -ll trace +tls certs/application_1.key certs/application_1.crt +cf certs/orthanc.crt 10.43.157.109 4242
D: $dcmtk: echoscu v3.6.6 2021-01-14 $
D:
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk16/dicom.dic
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk16/private.dic
D: Request Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.6
D: Our Implementation Version Name: OFFIS_DCMTK_366
D: Their Implementation Class UID:
D: Their Implementation Version Name:
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: ECHOSCU
D: Called Application Name: ANY-SCP
D: Responding Application Name: ANY-SCP
D: Our Max PDU Receive Size: 16384
D: Their Max PDU Receive Size: 0
D: Presentation Contexts:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =VerificationSOPClass
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax(es):
D: =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation: none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response: none
D: ======================= END A-ASSOCIATE-RQ ======================
I: Requesting Association
T: DUL FSM Table: State: 1 Event: 0
T: DUL Event: A-ASSOCIATE request (local user)
T: DUL Action: AE 1 Transport Connect
D: setting network send timeout to 60 seconds
D: setting network receive timeout to 60 seconds
T: checking whether environment variable TCP_BUFFER_LENGTH is set
T: environment variable TCP_BUFFER_LENGTH not set, using the system defaults
T: checking whether environment variable TCP_NODELAY is set
T: environment variable TCP_NODELAY not set, using the default value (0)
T: Starting TLS client handshake
D: ================== BEGIN TLS CONNECTION DETAILS =================
D: Transport connection: TLS over TCP/IP
D: Protocol : TLSv1.3
D: Ciphersuite : TLS_AES_256_GCM_SHA384, encryption: 256 bits
D: Peer X.509v3 Certificate
D: Subject : /CN=orthanc.iesbx.dev.flywheel.io
D: Issued by : /CN=orthanc.iesbx.dev.flywheel.io
D: Serial no. : -1
D: Validity : not before May 5 16:19:32 2025 GMT, not after May 5 16:19:32 2026 GMT
D: Public key : RSA, 2048 bits
D: =================== END TLS CONNECTION DETAILS ==================
T: DUL FSM Table: State: 4 Event: 1
T: DUL Event: Transport conn confirmation (local)
T: DUL Action: AE 2 Send Associate RQ PDU
D: Constructing Associate RQ PDU
T: setting timeout for first PDU to be read to 30 seconds
T: Receiving data via select()
T: Read PDU HEAD TCP: 02 00 00 00 00 b8
T: Read PDU HEAD TCP: type: 02, length: 184 (b8)
T: DUL FSM Table: State: 5 Event: 2
T: DUL Event: A-ASSOCIATE-AC PDU (on transport)
T: DUL Action: AE 3 Associate Confirmation Accept
D: PDU Type: Associate Accept, PDU Length: 184 + 6 bytes PDU header
D: 02 00 00 00 00 b8 00 01 00 00 41 4e 59 2d 53 43
D: 50 20 20 20 20 20 20 20 20 20 45 43 48 4f 53 43
D: 55 20 20 20 20 20 20 20 20 20 00 00 00 00 00 00
D: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
D: 00 00 00 00 00 00 00 00 00 00 10 00 00 15 31 2e
D: 32 2e 38 34 30 2e 31 30 30 30 38 2e 33 2e 31 2e
D: 31 2e 31 21 00 00 19 01 00 00 00 40 00 00 11 31
D: 2e 32 2e 38 34 30 2e 31 30 30 30 38 2e 31 2e 32
D: 50 00 00 3a 51 00 00 04 00 00 40 00 52 00 00 1b
D: 31 2e 32 2e 32 37 36 2e 30 2e 37 32 33 30 30 31
D: 30 2e 33 2e 30 2e 33 2e 36 2e 39 55 00 00 0f 4f
D: 46 46 49 53 5f 44 43 4d 54 4b 5f 33 36 39
D: Parsing an A-ASSOCIATE PDU
T: PDU type: 2 (A-ASSOCIATE AC), PDU Length: 184
T: DICOM Protocol: 1
T: Called AP Title: ANY-SCP
T: Calling AP Title: ECHOSCU
T: Parsing remaining 116 bytes of A-ASSOCIATE PDU
T: Next item type: 10
T: Subitem parse: Type 10, Length 0021, Content: 1.2.840.10008.3.1.1.1
T: Successfully parsed Application Context
T: Parsing remaining 91 bytes of A-ASSOCIATE PDU
T: Next item type: 21
T: Parsing Presentation Context: (21), Length: 25
T: Presentation Context ID: 01
T: Parsing remaining 21 bytes of Presentation Context
T: Next item type: 40
T: Subitem parse: Type 40, Length 0017, Content: 1.2.840.10008.1.2
T: Successfully parsed Transfer Syntax
T: Successfully parsed Presentation Context
T: Parsing remaining 62 bytes of A-ASSOCIATE PDU
T: Next item type: 50
T: Parsing user info field (50), Length: 58
T: Parsing remaining 58 bytes of User Information
T: Next item type: 51
T: Maximum PDU Length: 16384
T: Successfully parsed Maximum PDU Length
T: Parsing remaining 50 bytes of User Information
T: Next item type: 52
T: Subitem parse: Type 52, Length 0027, Content: 1.2.276.0.7230010.3.0.3.6.9
T: Parsing remaining 19 bytes of User Information
T: Next item type: 55
T: Subitem parse: Type 55, Length 0015, Content: OFFIS_DCMTK_369
T: Successfully parsed User Information
D: Association Parameters Negotiated:
D: ====================== BEGIN A-ASSOCIATE-AC =====================
D: Our Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.6
D: Our Implementation Version Name: OFFIS_DCMTK_366
D: Their Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.9
D: Their Implementation Version Name: OFFIS_DCMTK_369
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: ECHOSCU
D: Called Application Name: ANY-SCP
D: Responding Application Name: ANY-SCP
D: Our Max PDU Receive Size: 16384
D: Their Max PDU Receive Size: 16384
D: Presentation Contexts:
D: Context ID: 1 (Accepted)
D: Abstract Syntax: =VerificationSOPClass
D: Proposed SCP/SCU Role: Default
D: Accepted SCP/SCU Role: Default
D: Accepted Transfer Syntax: =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation: none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response: none
D: ======================= END A-ASSOCIATE-AC ======================
I: Association Accepted (Max Send PDV: 16372)
I: Sending Echo Request (MsgID 1)
T: DcmItem::insert() Element (0000,0000) VR="UL" inserted at beginning
T: DcmItem::insert() Element (0000,0100) VR="US" inserted
T: DcmItem::insert() Element (0000,0110) VR="US" inserted
T: DcmItem::insert() Element (0000,0800) VR="US" inserted
T: DcmItem::insert() Element (0000,0002) VR="UI" inserted
T: DIMSE Command to be sent on Presentation Context ID: 1
T: DIMSE Command to send:
T:
T: # Dicom-Data-Set
T: # Used TransferSyntax: Little Endian Explicit
T: (0000,0000) UL 0 # 4, 1 CommandGroupLength
T: (0000,0002) UI =VerificationSOPClass # 18, 1 AffectedSOPClassUID
T: (0000,0100) US 48 # 2, 1 CommandField
T: (0000,0110) US 1 # 2, 1 MessageID
T: (0000,0800) US 257 # 2, 1 CommandDataSetType
T:
T: DIMSE sendDcmDataset: sending 68 bytes
T: DUL FSM Table: State: 6 Event: 8
T: DUL Event: P-DATA request primitive
T: DUL Action: DT 1 Send P DATA PDU
T: DIMSE receiveCommand
T: Read PDU HEAD TCP: 04 00 00 00 00 54
T: Read PDU HEAD TCP: type: 04, length: 84 (54)
T: DUL FSM Table: State: 6 Event: 9
T: DUL Event: P-DATA-TF PDU (on transport)
T: DUL Action: DT 2 Indicate P DATA PDU Received
D: DcmDataset::read() TransferSyntax="Little Endian Implicit"
T: DcmItem::insert() Element (0000,0000) VR="UL" inserted at beginning
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::insert() Element (0000,0002) VR="UI" inserted
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::insert() Element (0000,0100) VR="US" inserted
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::insert() Element (0000,0120) VR="US" inserted
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::insert() Element (0000,0800) VR="US" inserted
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::insert() Element (0000,0900) VR="US" inserted
T: DcmItem::readSubItem() returns error = Normal
T: DcmItem::read() returns error = Normal
T: DcmDataset::read() returns error = Normal
T: DIMSE receiveCommand: 1 PDVs (78 bytes), PresID=1
T: DIMSE Command Received:
T:
T: # Dicom-Data-Set
T: # Used TransferSyntax: Little Endian Implicit
T: (0000,0002) UI =VerificationSOPClass # 18, 1 AffectedSOPClassUID
T: (0000,0100) US 32816 # 2, 1 CommandField
T: (0000,0120) US 1 # 2, 1 MessageIDBeingRespondedTo
T: (0000,0800) US 257 # 2, 1 CommandDataSetType
T: (0000,0900) US 0 # 2, 1 Status
T:
T: DcmItem::searchSubFromHere() Element (0000,0100) found
T: DcmItem::searchSubFromHere() Element (0000,0100) found
T: DcmItem::searchSubFromHere() Element (0000,0120) found
T: DcmItem::searchSubFromHere() Element (0000,0800) found
T: DcmItem::searchSubFromHere() Element (0000,0900) found
T: DcmItem::searchSubFromHere() Element (0000,0002) found
I: Received Echo Response (Success)
I: Releasing Association
T: DUL FSM Table: State: 6 Event: 10
T: DUL Event: A-RELEASE request primitive
T: DUL Action: AR 1 Send Release RQ
T: Receiving data via select()
T: Read PDU HEAD TCP: 06 00 00 00 00 04
T: Read PDU HEAD TCP: type: 06, length: 4 (04)
T: DUL FSM Table: State: 7 Event: 12
T: DUL Event: A-RELEASE-RP PDU (on transport)
T: DUL Action: AR 3 Confirm Release
This is a successful response, indicating that my mTLS configuration works fine. This TLS configuration is also working with C-FIND and C-STORE.
Failing Command (movescu
)
However, C-MOVE, despite not supporting TLS, presents me with a TLS-related error. Output from C-MOVE command:
$ movescu -ll trace -od tmp --port 5104 -S -aet FW-REAPER -aec FW-RECEIVER -k QueryRetrieveLevel="SERIES" -k SeriesInstanceUID="1.3.6.1.4.1.5962.1.3.4.1.20040826185059.5457" -k StudyInstanceUID="1.3.6.1.4.1.5962.1.2.4.20040826185059.5457" 10.43.157.109 4242
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk16/dicom.dic
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk16/private.dic
T: DcmItem::insert() Element (0020,000d) VR="UI" inserted at beginning
T: DcmItem::insert() Element (0020,000e) VR="UI" inserted
T: DcmItem::insert() Element (0008,0052) VR="CS" inserted at beginning
D: $dcmtk: movescu v3.6.6 2021-01-14 $
D:
D: Request Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.6
D: Our Implementation Version Name: OFFIS_DCMTK_366
D: Their Implementation Class UID:
D: Their Implementation Version Name:
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: FW-REAPER
D: Called Application Name: FW-RECEIVER
D: Responding Application Name: FW-RECEIVER
D: Our Max PDU Receive Size: 16384
D: Their Max PDU Receive Size: 0
D: Presentation Contexts:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =FINDStudyRootQueryRetrieveInformationModel
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax(es):
D: =LittleEndianExplicit
D: =BigEndianExplicit
D: =LittleEndianImplicit
D: Context ID: 3 (Proposed)
D: Abstract Syntax: =MOVEStudyRootQueryRetrieveInformationModel
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax(es):
D: =LittleEndianExplicit
D: =BigEndianExplicit
D: =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation: none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response: none
D: ======================= END A-ASSOCIATE-RQ ======================
I: Requesting Association
T: DUL FSM Table: State: 1 Event: 0
T: DUL Event: A-ASSOCIATE request (local user)
T: DUL Action: AE 1 Transport Connect
D: setting network send timeout to 60 seconds
D: setting network receive timeout to 60 seconds
T: checking whether environment variable TCP_BUFFER_LENGTH is set
T: environment variable TCP_BUFFER_LENGTH not set, using the system defaults
T: checking whether environment variable TCP_NODELAY is set
T: environment variable TCP_NODELAY not set, using the default value (0)
T: DUL FSM Table: State: 4 Event: 1
T: DUL Event: Transport conn confirmation (local)
T: DUL Action: AE 2 Send Associate RQ PDU
D: Constructing Associate RQ PDU
T: setting timeout for first PDU to be read to 30 seconds
T: DUL FSM Table: State: 5 Event: 16
T: DUL Event: Transport connection closed
T: DUL Action: AA 4 Indicate AP Abort
F: Association Request Failed:
F: 0006:0317 Peer aborted Association (or never connected)
And logs from Orthanc:
D: DUL: disabling Nagle algorithm as defined at compilation time (DISABLE_NAGLE_ALGORITHM)
D: Association Received: 10.42.0.1
D: setting network send timeout to 60 seconds
D: setting network receive timeout to 60 seconds
E0507 03:10:52.682534 DICOM-SERVER CommandDispatcher.cpp:285] Receiving Association failed: TLS error: wrong version number
Full Configuration
Orthanc
Compiled from source Orthanc 1.12.7
. Problem also occurs on 1.12.6
.
{
"DicomAet": "FWRECEIVER",
"DicomPort": 4242,
"DicomCheckCalledAet": false,
"AcceptedTransferSyntaxes": [
"1.2.840.10008.1.2.*"
],
"UnknownSopClassAccepted": true,
"OverwriteInstances": true,
"IndexDirectory": "/data/index",
"StorageDirectory": "/data/storage",
"StorageCompression": true,
"MaximumStorageSize": 102400,
"Name": "Orthanc",
"HttpPort": 8042,
"HttpCompressionEnabled": true,
"RemoteAccessAllowed": true,
"AuthenticationEnabled": false,
"ExecuteLuaEnabled": true,
"WebDavEnabled": false,
"Plugins": [
"/opt/share/orthanc/plugins/libConnectivityChecks.so",
"/opt/share/orthanc/plugins/libDelayedDeletion.so",
"/opt/share/orthanc/plugins/libHousekeeper.so",
"/opt/share/orthanc/plugins/libOrthancExplorer2.so",
"/opt/share/orthanc/plugins/libOrthancDicomWeb.so",
"/opt/share/orthanc/plugins/libOrthancGdcm.so",
"/opt/share/orthanc/plugins/libOrthancOHIF.so",
"/opt/share/orthanc/plugins/libOrthancPostgreSQLIndex.so",
"/opt/share/orthanc/plugins/libOrthancPython.so"
],
"DicomWeb": {
"Enable": true
},
"Gdcm": {
"Throttling": 4,
"RestrictTransferSyntaxes": [
"1.2.840.10008.1.2.4.90",
"1.2.840.10008.1.2.4.91",
"1.2.840.10008.1.2.4.92",
"1.2.840.10008.1.2.4.93"
]
},
"Housekeeper": {
"Enable": true,
"LimitMainDicomTagsReconstructLevel": "Series"
},
"OrthancExplorer2": {
"Enable": true,
"IsDefaultOrthancUI": true
},
"PythonScript": "/orthanc/script.py",
"DicomModalities": {
"FW-REAPER": {
"AET": "FW-REAPER",
"Host": "reaper-push-reaper-secure",
"Port": 5104,
}
},
"DicomTlsCertificate": "/etc/orthanc/tls/orthanc.crt",
"DicomTlsEnabled": true,
"DicomTlsPrivateKey": "/etc/orthanc/tls/orthanc.key",
"DicomTlsTrustedCertificates": "/etc/orthanc/tls/trusted.crt",
"RegisteredUsers": {}
}
OpenSSL on application
$ openssl -v
OpenSSL 3.3.2 3 Sep 2024 (Library: OpenSSL 3.3.2 3 Sep 2024)
OpenSSL on Orthanc container/pod
$ openssl -v
OpenSSL 3.5.0 8 Apr 2025 (Library: OpenSSL 3.5.0 8 Apr 2025)
Application built with OpenSSL 3.3.3 11 Feb 2025 (Library: OpenSSL 3.3.3 11 Feb 2025)
also presents the same problem.
Summary:
C-ECHO, C-STORE, and C-FIND work with mTLS enabled, whereas C-MOVE produces the error Association failed: TLS error: wrong version number
. Really not sure why this is happening and I would appreciate some help on this.