├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── build.xml ├── doc ├── settings.png └── settings.svg ├── plugin.xml ├── src └── de │ └── tobchen │ └── tobyconnects │ └── tcssl │ ├── CertAndKeyManager.java │ ├── CertAndKeyStore.java │ ├── TCSSLConfiguration.java │ ├── TCSSLPanel.java │ ├── TCSSLPluginProperties.java │ ├── TCSSLPropertiesPlugin.java │ ├── TCSSLServicePlugin.java │ ├── TrustAllManager.java │ └── TrustSomeManager.java └── testhelp ├── client.crt ├── client.key ├── server.crt ├── server.key ├── ssl-client.py ├── ssl-server.py ├── unsecure-client.py └── unsecure-server.py /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | target/ 3 | build/ 4 | cert/ 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.server.launchMode": "Standard", 3 | "java.project.sourcePaths": [ 4 | "src" 5 | ], 6 | "java.project.referencedLibraries": [ 7 | "lib/**/*.jar" 8 | ] 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Toby Connects SSL Plugin 2 | 3 | This plugin enables SSL in Mirth Connect 4.0.1 TCP connectors. 4 | 5 | ## Manual 6 | 7 | **Disclaimer:** This plugin's developer is neither an SSL/TLS expert nor a Mirth Connect development pro. They lack the resources to thoroughly test their plugin and do not recommend its usage in production environments. 8 | 9 | ### Installation 10 | 11 | In Mirth Connect Administrator, install *tcssl.zip*. 12 | 13 | ### Usage 14 | 15 | X509 certificates and PKCS8 private key (decrypted) shall be stored in PEM files accessible to the Mirth Connect service user. They are only read once on channel deployment. Currently, only a single certificate per file is supported. 16 | 17 | Currently, Respond on New Connection (in any case) is not supported. Client mode for TCP listeners and server mode for TCP senders have yet to be tested. 18 | 19 | ![Toby Connects SSL Plugin Settings](doc/settings.png) 20 | 21 | | Item | Name | Description | 22 | |---|---|---| 23 | |A|Enabled|Enable or disable SSL support.| 24 | |B|Certificate Path|Path to X509 certificate PEM file. Required in server mode and for authorization in client mode.| 25 | |C|Key Path|Path to PKCS8 private key PEM file. Required for certificate use.| 26 | |D|Trust All Certificates|Whether to trust *all* certificates from connected sockets. In server mode, unchecking this option calls for client authentication.| 27 | |E|Trusted Certificate Paths|Paths to PEM files of trusted X509 certificates. An empty list *and* not trusting *all* certificates allow for *no* connection.| 28 | 29 | ## Development 30 | 31 | ### Dependencies 32 | 33 | - Copy dependencies from Mirth Connect installation to */lib/* 34 | - from *Mirth Connect/server-lib/* 35 | - *mirth-server.jar* 36 | - *donkey-server.jar* 37 | - from *Mirth Connect/client-lib/* 38 | - *donkey-model.jar* 39 | - *mirth-client.jar* 40 | - from *Mirth Connect/extensions/tcp/* 41 | - *tcp-shared.jar* 42 | - *tcp-server.jar* 43 | - Additionally download to */lib/* 44 | - *log4j-1.2.16.jar* 45 | - *commons-lang3-3.9.jar* 46 | - *miglayout-core-4.2.jar* 47 | - *miglayout-swing-4.2.jar* 48 | - *xstream-1.4.12.jar* 49 | 50 | This plugin is developed in OpenJDK 11 on Linux. 51 | 52 | ### Compile And Package 53 | 54 | Make sure dependencies are satisfied. 55 | 56 | In */*, to create *build/tcssl.zip*, execute: `ant -DsignAlias= -DsignPass=` 57 | 58 | Parameters `signAlias` and `signPass` are needed to sign the JAR with a certificate from the default key store. Self-signed archives can be used in Mirth Adminstrator Launcher when run with argumens `-d` and/or `-k`. 59 | 60 | ### Testing Procedure 61 | 62 | Before releasing a new version of this plugin the following tests must have been passed. 63 | 64 | */testhelp/* has client/server certificates/keys and Python programs to help testing. 65 | 66 | #### TCP Listener (Server Mode) 67 | 68 | |Step|Client SSL|Client Cert|Listener SSL|Listener Cert|Listener Trust|Expected| 69 | |---|---|---|---|---|---|---| 70 | |1|No||No|||Success| 71 | |2|No||Yes|server|All|Failure| 72 | |3|Yes|-|Yes|server|All|Success| 73 | |4|Yes|-|Yes|server|None|Failure| 74 | |5|Yes|-|Yes|server|client|Failure| 75 | |6|Yes|client|Yes|server|client|Success| 76 | 77 | #### TCP Listener (Client Mode) 78 | 79 | No procedure defined yet. 80 | 81 | #### TCP Sender (Client Mode) 82 | 83 | |Step|Sender SSL|Sender Cert|Sender Trust|Server SSL|Server Cert|Server Trust|Expected| 84 | |---|---|---|---|---|---|---|---| 85 | |1|No|||No|||Success| 86 | |2|Yes|client|All|No|||Failure| 87 | |3|Yes|client|All|Yes|server|All|Success| 88 | |4|Yes|client|None|Yes|server|All|Failure| 89 | |5|Yes|client|server|Yes|server|All|Success| 90 | |6|Yes|client|All|Yes|server|client|Success| 91 | 92 | #### TCP Sender (Sever Mode) 93 | 94 | No procedure defined yet. 95 | 96 | ### To Do 97 | 98 | - TCP Listener 99 | - Test Client Mode 100 | - Respond on New Connection 101 | - TCP Sender 102 | - Test Server Mode 103 | - General 104 | - Certificate Validation 105 | - Mirth Connect Administrator Command to Clear "Certificate & Key Store" 106 | - Allow PKCS8 Encrypted Private Key 107 | - [Allow Other Private Keys](https://github.com/openssl/openssl/blob/master/include/openssl/pem.h#L35) 108 | 109 | ### Version History 110 | 111 | #### v0.3.0 112 | 113 | - Unlocked SSL settings for TCP listener/sender in both server and client mode each. 114 | 115 | #### v0.2.0 116 | 117 | - TCP listener in server mode may require client authentication. 118 | 119 | #### v0.1.0 120 | 121 | - TCP listener in server mode works 122 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /doc/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobchen/tc-ssl-plugin/b092938fcbcb093aa5be33bad1f80e613083af7b/doc/settings.png -------------------------------------------------------------------------------- /doc/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 33 | 34 | 38 | 43 | 49 | 54 | 59 | 65 | 66 | 70 | 75 | 81 | 86 | 91 | 97 | 98 | 99 | 120 | 122 | 123 | 125 | image/svg+xml 126 | 128 | 129 | 130 | 131 | 132 | 138 | 141 | 424 | 425 | 426 | 430 | 433 | 440 | 447 | 454 | 461 | 468 | 469 | 470 | 475 | A 486 | B 497 | C 508 | D 519 | E 530 | 531 | 532 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | Toby Connects SSL Plugin 9 | Tobias Heukäufer 10 | 0.3.0 11 | 4.0.1 12 | https://github.com/tobchen 13 | This plugin enables SSL in TCP connectors. 14 | 15 | de.tobchen.tobyconnects.tcssl.TCSSLServicePlugin 16 | 17 | 18 | de.tobchen.tobyconnects.tcssl.TCSSLPropertiesPlugin 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/CertAndKeyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.io.IOException; 12 | import java.net.Socket; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.security.Principal; 15 | import java.security.PrivateKey; 16 | import java.security.cert.CertificateException; 17 | import java.security.cert.X509Certificate; 18 | import java.security.spec.InvalidKeySpecException; 19 | 20 | import javax.net.ssl.X509KeyManager; 21 | 22 | public class CertAndKeyManager implements X509KeyManager { 23 | private static final String ALIAS = "tobywoby"; 24 | 25 | private final X509Certificate CERT; 26 | private final PrivateKey KEY; 27 | 28 | public CertAndKeyManager(String certPath, String keyPath) 29 | throws CertificateException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { 30 | CERT = certPath != null ? CertAndKeyStore.readCertificate(certPath) : null; 31 | KEY = keyPath != null ? CertAndKeyStore.readKey(keyPath) : null; 32 | } 33 | 34 | @Override 35 | public String[] getClientAliases(String keyType, Principal[] issuers) { 36 | return CERT != null && CERT.getPublicKey().getAlgorithm().equals(keyType) ? new String[] { ALIAS } : null; 37 | } 38 | 39 | @Override 40 | public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { 41 | for (String type : keyType) { 42 | if (CERT != null && CERT.getPublicKey().getAlgorithm().equals(type)) { 43 | return ALIAS; 44 | } 45 | } 46 | return null; 47 | } 48 | 49 | @Override 50 | public String[] getServerAliases(String keyType, Principal[] issuers) { 51 | return CERT != null && CERT.getPublicKey().getAlgorithm().equals(keyType) ? new String[] { ALIAS } : null; 52 | } 53 | 54 | @Override 55 | public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 56 | return CERT != null && CERT.getPublicKey().getAlgorithm().equals(keyType) ? ALIAS : null; 57 | } 58 | 59 | @Override 60 | public X509Certificate[] getCertificateChain(String alias) { 61 | return CERT != null && ALIAS.equals(alias) ? new X509Certificate[] { CERT } : null; 62 | } 63 | 64 | @Override 65 | public PrivateKey getPrivateKey(String alias) { 66 | return KEY != null && ALIAS.equals(alias) ? KEY : null; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/CertAndKeyStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | import java.security.KeyFactory; 16 | import java.security.NoSuchAlgorithmException; 17 | import java.security.PrivateKey; 18 | import java.security.cert.CertificateException; 19 | import java.security.cert.CertificateFactory; 20 | import java.security.cert.X509Certificate; 21 | import java.security.spec.InvalidKeySpecException; 22 | import java.security.spec.PKCS8EncodedKeySpec; 23 | import java.util.Base64; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import java.util.zip.CRC32; 27 | import java.util.zip.Checksum; 28 | 29 | // TODO Use bouncy castle 30 | 31 | public class CertAndKeyStore { 32 | // TODO Have CERTIFIATES be of entries of list of certifactes, so multiple certs per file can be cached 33 | private final static Map> CERTIFICATES = new HashMap<>(); 34 | private final static Map> PRIVATE_KEYS = new HashMap<>(); 35 | 36 | public static X509Certificate readCertificate(String path) throws IOException, CertificateException { 37 | Entry readEntry = readFile(path); 38 | Entry certEntry = CERTIFICATES.get(path); 39 | 40 | if (certEntry != null && readEntry.CHECKSUM == certEntry.CHECKSUM) { 41 | return certEntry.CONTENT; 42 | } 43 | 44 | CertificateFactory factory = CertificateFactory.getInstance("X.509"); 45 | X509Certificate cert = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(readEntry.CONTENT)); 46 | 47 | CERTIFICATES.put(path, new Entry<>(readEntry.CHECKSUM, cert)); 48 | 49 | return cert; 50 | } 51 | 52 | public static PrivateKey readKey(String path) throws InvalidKeySpecException, IOException, NoSuchAlgorithmException { 53 | Entry readEntry = readFile(path); 54 | Entry keyEntry = PRIVATE_KEYS.get(path); 55 | 56 | if (keyEntry != null && readEntry.CHECKSUM == keyEntry.CHECKSUM) { 57 | return keyEntry.CONTENT; 58 | } 59 | 60 | String key = new String(readEntry.CONTENT) 61 | .replace("-----BEGIN PRIVATE KEY-----", "") 62 | .replace("-----END PRIVATE KEY-----", "") 63 | .replace("\n", ""); 64 | 65 | byte[] decoded = Base64.getDecoder().decode(key); 66 | PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded); 67 | KeyFactory factory = KeyFactory.getInstance("RSA"); 68 | 69 | PrivateKey privateKey = factory.generatePrivate(spec); 70 | PRIVATE_KEYS.put(path, new Entry<>(readEntry.CHECKSUM, privateKey)); 71 | 72 | return privateKey; 73 | } 74 | 75 | private static Entry readFile(String path) throws IOException { 76 | byte[] content = Files.readAllBytes(Paths.get(path)); 77 | 78 | Checksum checksum = new CRC32(); 79 | checksum.update(content); 80 | 81 | return new Entry<>(checksum.getValue(), content); 82 | } 83 | 84 | private static class Entry { 85 | private final long CHECKSUM; 86 | private final T CONTENT; 87 | 88 | public Entry(long checksum, T content) { 89 | CHECKSUM = checksum; 90 | CONTENT = content; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TCSSLConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.io.IOException; 12 | import java.net.InetAddress; 13 | import java.net.ServerSocket; 14 | import java.net.Socket; 15 | import java.util.Map; 16 | 17 | import javax.net.ssl.KeyManager; 18 | import javax.net.ssl.SSLContext; 19 | import javax.net.ssl.SSLServerSocket; 20 | import javax.net.ssl.SSLServerSocketFactory; 21 | import javax.net.ssl.SSLSocketFactory; 22 | import javax.net.ssl.TrustManager; 23 | 24 | import com.mirth.connect.connectors.tcp.DefaultTcpConfiguration; 25 | import com.mirth.connect.connectors.tcp.TcpDispatcher; 26 | import com.mirth.connect.connectors.tcp.TcpReceiver; 27 | import com.mirth.connect.donkey.model.channel.ConnectorPluginProperties; 28 | import com.mirth.connect.donkey.server.channel.Connector; 29 | 30 | public class TCSSLConfiguration extends DefaultTcpConfiguration { 31 | 32 | private ConfigType type = ConfigType.DEFAULT; 33 | private boolean trustAllCerts = false; 34 | 35 | private SSLSocketFactory socketFactory; 36 | private SSLServerSocketFactory serverSocketFactory; 37 | 38 | @Override 39 | public void configureConnectorDeploy(Connector connector) throws Exception { 40 | super.configureConnectorDeploy(connector); 41 | 42 | type = ConfigType.DEFAULT; 43 | 44 | if (connector instanceof TcpReceiver || connector instanceof TcpDispatcher) { 45 | for (ConnectorPluginProperties properties : connector.getConnectorProperties().getPluginProperties()) { 46 | if (properties instanceof TCSSLPluginProperties) { 47 | TCSSLPluginProperties sslProperties = (TCSSLPluginProperties) properties; 48 | 49 | if (sslProperties.isEnabled()) { 50 | type = ConfigType.SSL; 51 | 52 | String certPath = sslProperties.getCertPath(); 53 | String keyPath = sslProperties.getKeyPath(); 54 | if (certPath.isEmpty()) { 55 | certPath = null; 56 | } 57 | if (keyPath.isEmpty()) { 58 | keyPath = null; 59 | } 60 | KeyManager keyManager = new CertAndKeyManager(certPath, keyPath); 61 | 62 | trustAllCerts = sslProperties.doTrustAllCerts(); 63 | TrustManager trustManager = TrustAllManager.INSTANCE; 64 | if (!trustAllCerts) { 65 | trustManager = new TrustSomeManager(sslProperties.getTrustedCertPaths().toArray(new String[0])); 66 | } 67 | 68 | SSLContext context = SSLContext.getInstance("TLS"); 69 | context.init(new KeyManager[] { keyManager }, 70 | new TrustManager[] { trustManager }, 71 | null); 72 | 73 | socketFactory = context.getSocketFactory(); 74 | serverSocketFactory = context.getServerSocketFactory(); 75 | } 76 | 77 | break; 78 | } 79 | } 80 | } 81 | } 82 | 83 | @Override 84 | public ServerSocket createServerSocket(int port, int backlog) throws IOException { 85 | switch (type) { 86 | case SSL: 87 | SSLServerSocket socket = (SSLServerSocket) serverSocketFactory.createServerSocket(port, backlog); 88 | socket.setNeedClientAuth(!trustAllCerts); 89 | return socket; 90 | default: 91 | return super.createServerSocket(port, backlog); 92 | } 93 | } 94 | 95 | @Override 96 | public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { 97 | switch (type) { 98 | case SSL: 99 | SSLServerSocket socket = (SSLServerSocket) serverSocketFactory.createServerSocket(port, backlog, bindAddr); 100 | socket.setNeedClientAuth(!trustAllCerts); 101 | return socket; 102 | default: 103 | return super.createServerSocket(port, backlog, bindAddr); 104 | } 105 | } 106 | 107 | @Override 108 | public Socket createSocket() { 109 | switch (type) { 110 | case SSL: 111 | try { 112 | return socketFactory.createSocket(); 113 | } catch (IOException e) { 114 | return null; 115 | } 116 | default: 117 | return super.createSocket(); 118 | } 119 | } 120 | 121 | @Override 122 | public Socket createResponseSocket() { 123 | return super.createResponseSocket(); 124 | } 125 | 126 | @Override 127 | public Map getSocketInformation(Socket socket) { 128 | // TODO Return useful socket information 129 | 130 | Map result = super.getSocketInformation(socket); 131 | result.put("tcsslSSL", type == ConfigType.SSL); 132 | 133 | return result; 134 | } 135 | 136 | private enum ConfigType { 137 | DEFAULT, 138 | SSL 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TCSSLPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.awt.Component; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.ActionListener; 14 | import java.awt.event.ItemEvent; 15 | import java.awt.event.ItemListener; 16 | import java.util.Collections; 17 | import java.util.HashSet; 18 | 19 | import javax.swing.BoxLayout; 20 | import javax.swing.DefaultListModel; 21 | import javax.swing.JLabel; 22 | import javax.swing.JList; 23 | import javax.swing.JOptionPane; 24 | import javax.swing.JPanel; 25 | import javax.swing.JScrollPane; 26 | import javax.swing.ListSelectionModel; 27 | 28 | import com.mirth.connect.client.ui.AbstractConnectorPropertiesPanel; 29 | import com.mirth.connect.client.ui.UIConstants; 30 | import com.mirth.connect.client.ui.components.MirthButton; 31 | import com.mirth.connect.client.ui.components.MirthCheckBox; 32 | import com.mirth.connect.client.ui.components.MirthTextField; 33 | import com.mirth.connect.donkey.model.channel.ConnectorPluginProperties; 34 | import com.mirth.connect.donkey.model.channel.ConnectorProperties; 35 | import com.mirth.connect.model.Connector.Mode; 36 | 37 | import net.miginfocom.swing.MigLayout; 38 | 39 | /* 40 | * Very much inspired by HttpAuthConnectorPropertiesPanel from Mirth Connect by NextGen. 41 | */ 42 | 43 | public class TCSSLPanel extends AbstractConnectorPropertiesPanel { 44 | 45 | private MirthCheckBox enabledBox; 46 | 47 | private JLabel certPathLabel; 48 | private MirthTextField certPathField; 49 | 50 | private JLabel keyPathLabel; 51 | private MirthTextField keyPathField; 52 | 53 | private MirthCheckBox trustAllCertsBox; 54 | 55 | private JLabel trustedCertPathsLabel; 56 | private DefaultListModel trustedCertPathsModel; 57 | private JList trustedCertPathsList; 58 | 59 | private MirthButton newButton; 60 | private MirthButton deleteButton; 61 | 62 | public TCSSLPanel() { 63 | initComponents(); 64 | initToolTips(); 65 | initLayout(); 66 | } 67 | 68 | @Override 69 | public ConnectorPluginProperties getProperties() { 70 | TCSSLPluginProperties properties = new TCSSLPluginProperties(); 71 | 72 | properties.setEnabled(enabledBox.isSelected()); 73 | 74 | properties.setCertPath(certPathField.getText()); 75 | properties.setKeyPath(keyPathField.getText()); 76 | 77 | properties.setTrustAllCerts(trustAllCertsBox.isSelected()); 78 | properties.setTrustedCertPaths(new HashSet<>(Collections.list(trustedCertPathsModel.elements()))); 79 | 80 | return properties; 81 | } 82 | 83 | @Override 84 | public void setProperties(ConnectorProperties connectorProperties, ConnectorPluginProperties properties, Mode mode, String transportName) { 85 | TCSSLPluginProperties sslProperties = (TCSSLPluginProperties) properties; 86 | 87 | enabledBox.setSelected(sslProperties.isEnabled()); 88 | 89 | certPathField.setText(sslProperties.getCertPath()); 90 | keyPathField.setText(sslProperties.getKeyPath()); 91 | 92 | trustAllCertsBox.setSelected(sslProperties.doTrustAllCerts()); 93 | 94 | trustedCertPathsModel.clear(); 95 | trustedCertPathsModel.addAll(sslProperties.getTrustedCertPaths()); 96 | 97 | updateActivations(); 98 | } 99 | 100 | @Override 101 | public ConnectorPluginProperties getDefaults() { 102 | return new TCSSLPluginProperties(); 103 | } 104 | 105 | @Override 106 | public boolean checkProperties(ConnectorProperties connectorProperties, ConnectorPluginProperties properties, 107 | Mode mode, String transportName, boolean highlight) { 108 | return true; 109 | } 110 | 111 | @Override 112 | public void resetInvalidProperties() { } 113 | 114 | @Override 115 | public Component[][] getLayoutComponents() { 116 | return null; 117 | } 118 | 119 | @Override 120 | public void setLayoutComponentsEnabled(boolean enabled) { } 121 | 122 | private void initComponents() { 123 | setBackground(UIConstants.BACKGROUND_COLOR); 124 | 125 | enabledBox = new MirthCheckBox("Enabled"); 126 | enabledBox.setBackground(getBackground()); 127 | enabledBox.addItemListener(new ItemListener() { 128 | @Override 129 | public void itemStateChanged(ItemEvent e) { 130 | updateActivations(); 131 | } 132 | }); 133 | 134 | certPathLabel = new JLabel("Certificate Path:"); 135 | certPathField = new MirthTextField(); 136 | certPathField.setBackground(getBackground()); 137 | 138 | keyPathLabel = new JLabel("Key Path:"); 139 | keyPathField = new MirthTextField(); 140 | keyPathField.setBackground(getBackground()); 141 | 142 | trustAllCertsBox = new MirthCheckBox("Trust All Certificates"); 143 | trustAllCertsBox.setBackground(getBackground()); 144 | trustAllCertsBox.addItemListener(new ItemListener() { 145 | @Override 146 | public void itemStateChanged(ItemEvent e) { 147 | updateActivations(); 148 | } 149 | }); 150 | 151 | trustedCertPathsLabel = new JLabel("Trusted Certificate Paths:"); 152 | trustedCertPathsModel = new DefaultListModel<>(); 153 | trustedCertPathsList = new JList<>(trustedCertPathsModel); 154 | trustedCertPathsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 155 | 156 | newButton = new MirthButton("New..."); 157 | newButton.addActionListener(new ActionListener() { 158 | @Override 159 | public void actionPerformed(ActionEvent e) { 160 | newTrustedCertClicked(); 161 | } 162 | }); 163 | deleteButton = new MirthButton("Delete"); 164 | deleteButton.addActionListener(new ActionListener() { 165 | @Override 166 | public void actionPerformed(ActionEvent e) { 167 | deleteTrustedCertClicked(); 168 | } 169 | }); 170 | } 171 | 172 | private void initToolTips() { 173 | enabledBox.setToolTipText("Enable or disable SSL."); 174 | certPathField.setToolTipText("Path to connector X509 certificate PEM file. Required for server mode, optional for client mode."); 175 | keyPathField.setToolTipText("Path to connector PKCS8 private key PEM file. Required for certificates."); 176 | trustedCertPathsList.setToolTipText("Paths to trusted X509 certificate PEM files."); 177 | newButton.setToolTipText("Add new path to trusted certificate list."); 178 | deleteButton.setToolTipText("Remove selected path from trusted certificate list."); 179 | } 180 | 181 | private void initLayout() { 182 | JScrollPane scrollPane = new JScrollPane(trustedCertPathsList); 183 | JPanel pane = new JPanel(); 184 | pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS)); 185 | pane.add(newButton); 186 | pane.add(deleteButton); 187 | 188 | setLayout(new MigLayout()); 189 | add(enabledBox, "span"); 190 | add(certPathLabel, "newline, right"); 191 | add(certPathField, "w 400"); 192 | add(keyPathLabel, "newline, right"); 193 | add(keyPathField, "w 400"); 194 | add(trustAllCertsBox, "newline, span"); 195 | add(trustedCertPathsLabel, "newline, span"); 196 | add(scrollPane, "newline, span, h 100, w 400"); 197 | add(pane, "newline"); 198 | } 199 | 200 | private void updateActivations() { 201 | certPathLabel.setEnabled(enabledBox.isSelected()); 202 | certPathField.setEnabled(enabledBox.isSelected()); 203 | keyPathLabel.setEnabled(enabledBox.isSelected()); 204 | keyPathField.setEnabled(enabledBox.isSelected()); 205 | trustAllCertsBox.setEnabled(enabledBox.isSelected()); 206 | trustedCertPathsList.setEnabled(enabledBox.isSelected() && !trustAllCertsBox.isSelected()); 207 | newButton.setEnabled(enabledBox.isSelected() && !trustAllCertsBox.isSelected()); 208 | deleteButton.setEnabled(enabledBox.isSelected() && !trustAllCertsBox.isSelected()); 209 | } 210 | 211 | private void newTrustedCertClicked() { 212 | String path = (String) JOptionPane.showInputDialog(this, "Trusted Cert:", "New Trusted Cert", 213 | JOptionPane.PLAIN_MESSAGE, null, null, ""); 214 | if (path != null && trustedCertPathsModel.indexOf(path) < 0) { 215 | trustedCertPathsModel.addElement(path); 216 | trustedCertPathsList.setSelectedIndex(trustedCertPathsModel.getSize() - 1); 217 | } 218 | } 219 | 220 | private void deleteTrustedCertClicked() { 221 | int index = trustedCertPathsList.getSelectedIndex(); 222 | if (index >= 0) { 223 | trustedCertPathsModel.remove(index); 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TCSSLPluginProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | import org.apache.commons.lang3.builder.EqualsBuilder; 17 | 18 | import com.mirth.connect.donkey.model.channel.ConnectorPluginProperties; 19 | 20 | /* 21 | * Very much inspired by HttpAuthConnectorPluginProperties and BasicHttpAuthProperties from Mirth Connect by NextGen. 22 | */ 23 | 24 | public class TCSSLPluginProperties extends ConnectorPluginProperties { 25 | 26 | public static final String PLUGIN_POINT = "Toby Connects SSL Plugin Properties"; 27 | 28 | private boolean enabled = false; 29 | 30 | private String certPath = ""; 31 | private String keyPath = ""; 32 | 33 | private boolean trustAllCerts = false; 34 | private Set trustedCertPaths = new HashSet<>(); 35 | 36 | public TCSSLPluginProperties() { } 37 | 38 | public TCSSLPluginProperties(TCSSLPluginProperties pluginProperties) { 39 | this.enabled = pluginProperties.isEnabled(); 40 | this.certPath = pluginProperties.getCertPath(); 41 | this.keyPath = pluginProperties.getKeyPath(); 42 | 43 | } 44 | 45 | public boolean isEnabled() { 46 | return enabled; 47 | } 48 | 49 | public String getCertPath() { 50 | return certPath; 51 | } 52 | 53 | public String getKeyPath() { 54 | return keyPath; 55 | } 56 | 57 | public boolean doTrustAllCerts() { 58 | return trustAllCerts; 59 | } 60 | 61 | public Set getTrustedCertPaths() { 62 | return Set.copyOf(trustedCertPaths); 63 | } 64 | 65 | public void setEnabled(boolean enabled) { 66 | this.enabled = enabled; 67 | } 68 | 69 | public void setCertPath(String certPath) { 70 | this.certPath = certPath; 71 | } 72 | 73 | public void setKeyPath(String keyPath) { 74 | this.keyPath = keyPath; 75 | } 76 | 77 | public void setTrustAllCerts(boolean trustAllCerts) { 78 | this.trustAllCerts = trustAllCerts; 79 | } 80 | 81 | public void setTrustedCertPaths(Set trustedCertPaths) { 82 | this.trustedCertPaths = Set.copyOf(trustedCertPaths); 83 | } 84 | 85 | @Override 86 | public Map getPurgedProperties() { 87 | return new HashMap<>(); 88 | } 89 | 90 | @Override 91 | public ConnectorPluginProperties clone() { 92 | return new TCSSLPluginProperties(this); 93 | } 94 | 95 | @Override 96 | public boolean equals(Object obj) { 97 | return EqualsBuilder.reflectionEquals(this, obj); 98 | } 99 | 100 | @Override 101 | public String getName() { 102 | return PLUGIN_POINT; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TCSSLPropertiesPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import com.mirth.connect.client.ui.AbstractConnectorPropertiesPanel; 12 | import com.mirth.connect.connectors.tcp.TcpDispatcherProperties; 13 | import com.mirth.connect.connectors.tcp.TcpReceiverProperties; 14 | import com.mirth.connect.model.converters.ObjectXMLSerializer; 15 | import com.mirth.connect.model.converters.PluginPropertiesConverter; 16 | import com.mirth.connect.plugins.ConnectorPropertiesPlugin; 17 | import com.thoughtworks.xstream.XStream; 18 | 19 | /* 20 | * Very much inspired by HttpAuthConnectorPropertiesPlugin from Mirth Connect by NextGen. 21 | */ 22 | 23 | public class TCSSLPropertiesPlugin extends ConnectorPropertiesPlugin { 24 | 25 | public TCSSLPropertiesPlugin(String pluginName) { 26 | super(pluginName); 27 | 28 | ObjectXMLSerializer serializer = ObjectXMLSerializer.getInstance(); 29 | XStream xstream = serializer.getXStream(); 30 | xstream.registerLocalConverter(TCSSLPluginProperties.class, "connectorPluginProperties", new PluginPropertiesConverter(serializer.getNormalizedVersion(), xstream.getMapper())); 31 | } 32 | 33 | @Override 34 | public AbstractConnectorPropertiesPanel getConnectorPropertiesPanel() { 35 | return new TCSSLPanel(); 36 | } 37 | 38 | @Override 39 | public String getSettingsTitle() { 40 | return "Toby Connects SSL Settings"; 41 | } 42 | 43 | @Override 44 | public boolean isConnectorPropertiesPluginSupported(String pluginPointName) { 45 | return false; 46 | } 47 | 48 | @Override 49 | public boolean isSupported(String transportName) { 50 | return TcpReceiverProperties.NAME.equals(transportName) || TcpDispatcherProperties.NAME.equals(transportName); 51 | } 52 | 53 | @Override 54 | public String getPluginPointName() { 55 | return TCSSLPluginProperties.PLUGIN_POINT; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TCSSLServicePlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.util.Map; 12 | import java.util.Properties; 13 | 14 | import com.mirth.connect.model.ExtensionPermission; 15 | import com.mirth.connect.model.converters.ObjectXMLSerializer; 16 | import com.mirth.connect.model.converters.PluginPropertiesConverter; 17 | import com.mirth.connect.plugins.ServicePlugin; 18 | import com.mirth.connect.server.controllers.ConfigurationController; 19 | import com.mirth.connect.server.controllers.ControllerFactory; 20 | import com.thoughtworks.xstream.XStream; 21 | 22 | public class TCSSLServicePlugin implements ServicePlugin { 23 | 24 | private ConfigurationController configurationController = ControllerFactory.getFactory().createConfigurationController(); 25 | 26 | @Override 27 | public String getPluginPointName() { 28 | return "Toby Connects SSL Service Plugin"; 29 | } 30 | 31 | @Override 32 | public void start() { } 33 | 34 | @Override 35 | public void stop() { } 36 | 37 | @Override 38 | public Properties getDefaultProperties() { 39 | return new Properties(); 40 | } 41 | 42 | @Override 43 | public ExtensionPermission[] getExtensionPermissions() { 44 | return null; 45 | } 46 | 47 | @Override 48 | public Map getObjectsForSwaggerExamples() { 49 | return null; 50 | } 51 | 52 | @Override 53 | public void init(Properties properties) { 54 | configurationController.saveProperty("TCP", "tcpConfigurationClass", TCSSLConfiguration.class.getName()); 55 | 56 | ObjectXMLSerializer serializer = ObjectXMLSerializer.getInstance(); 57 | XStream xstream = serializer.getXStream(); 58 | xstream.registerLocalConverter(TCSSLPluginProperties.class, "connectorPluginProperties", 59 | new PluginPropertiesConverter(serializer.getNormalizedVersion(), xstream.getMapper())); 60 | } 61 | 62 | @Override 63 | public void update(Properties properties) { } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TrustAllManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.security.cert.CertificateException; 12 | import java.security.cert.X509Certificate; 13 | 14 | import javax.net.ssl.X509TrustManager; 15 | 16 | public class TrustAllManager implements X509TrustManager { 17 | 18 | public final static TrustAllManager INSTANCE = new TrustAllManager(); 19 | 20 | @Override 21 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } 22 | 23 | @Override 24 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } 25 | 26 | @Override 27 | public X509Certificate[] getAcceptedIssuers() { 28 | return new X509Certificate[0]; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/de/tobchen/tobyconnects/tcssl/TrustSomeManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Tobias Heukäufer 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | package de.tobchen.tobyconnects.tcssl; 10 | 11 | import java.io.IOException; 12 | import java.security.cert.CertificateException; 13 | import java.security.cert.X509Certificate; 14 | 15 | import javax.net.ssl.X509TrustManager; 16 | 17 | public class TrustSomeManager implements X509TrustManager { 18 | 19 | private final X509Certificate[] CERTS; 20 | 21 | public TrustSomeManager(String[] paths) throws CertificateException, IOException { 22 | CERTS = new X509Certificate[paths.length]; 23 | for (int i = 0; i < paths.length; ++i) { 24 | CERTS[i] = CertAndKeyStore.readCertificate(paths[i]); 25 | } 26 | } 27 | 28 | @Override 29 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 30 | for (X509Certificate clientCert : chain) { 31 | for (X509Certificate trustedCert : CERTS) { 32 | if (clientCert.equals(trustedCert)) { 33 | return; 34 | } 35 | } 36 | } 37 | throw new CertificateException("Client chain has no trusted certificate!"); 38 | } 39 | 40 | @Override 41 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 42 | for (X509Certificate clientCert : chain) { 43 | for (X509Certificate trustedCert : CERTS) { 44 | if (clientCert.equals(trustedCert)) { 45 | return; 46 | } 47 | } 48 | } 49 | throw new CertificateException("Server chain has no trusted certificate!"); 50 | } 51 | 52 | @Override 53 | public X509Certificate[] getAcceptedIssuers() { 54 | return new X509Certificate[0]; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /testhelp/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 34 | ZP2J4jAE8Q== 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /testhelp/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | O3jnr50Jc5oOOV2KxLdQLaOc3XsUeJQ= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /testhelp/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDizCCAnOgAwIBAgIUHFVCciI4rfCWkTAcXXDTy2QH1SIwDQYJKoZIhvcNAQEL 3 | BQAwVTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVy 4 | bGluMRAwDgYDVQQKDAdUb2JjaGVuMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIw 5 | NjE2MTYzMjU1WhcNMjMwNjE2MTYzMjU1WjBVMQswCQYDVQQGEwJERTEPMA0GA1UE 6 | CAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB1RvYmNoZW4xEjAQ 7 | BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 8 | AK+o2hxBVY3rS9IEnePdiKNEkEj7VE8csTEPgt1q7W+uAz4spV2vJjdyddrj33EW 9 | aIdoKLj0CxuCi+4mzfw+32ATRsu+G3DAG0+pwWCVpaXazOdqXy3Gym2fTCy7gtEw 10 | YaFiw7WfQxneQuvjX8Pf8Eh98TL0xhJ4SFJljTi8EJu2d0UFTU3W2FyNFlrSl10m 11 | 0sqgsZuITsa9yJxSI9Pki+UjxguyV1KE1wbNdhA1W5QLmV8kOrlX6AhgFEvMwmb6 12 | B2G16PFKQU4rwKpAhEHAVnltE3T5JJwzI+10OzsoVatUCDfBdeRJxYnSCVr9p8MB 13 | rnePYT4x92afmFeyXWHaZq0CAwEAAaNTMFEwHQYDVR0OBBYEFM0R9/6kSm1x/sSR 14 | b3ffImUAxHANMB8GA1UdIwQYMBaAFM0R9/6kSm1x/sSRb3ffImUAxHANMA8GA1Ud 15 | EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGRhWvomu1sa4x9gNWDf8xUL 16 | YFJ0jmyUc4plWazEtkBI13C2iCn41NLh9DLYd94+x/e08lWeyd97YGsVFacQe/Vg 17 | RZEVETF3tVqb7HujFv4nyqrqof//QVyHukLXFa8noezsUA6hww81IuBoXbzAkF+n 18 | hkBQrFcFK4O419PKosV/F1bPbxjfHQXXK2kUEdZGZXIPCEngjOpJoSJazePO75iV 19 | sQ2Yw/HKJres7qkzraycCf9irGNxKFl9PTJj8BpxLnvvs7IhzHJ6w5+NTzX9wJ0G 20 | GxvYwx4AMvkvbpqJBbf4UFG2UDaMN5+h3/xet82Mu8C6VlviBo7//zeNHz+G2Gc= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /testhelp/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvqNocQVWN60vS 3 | BJ3j3YijRJBI+1RPHLExD4Ldau1vrgM+LKVdryY3cnXa499xFmiHaCi49Asbgovu 4 | Js38Pt9gE0bLvhtwwBtPqcFglaWl2sznal8txsptn0wsu4LRMGGhYsO1n0MZ3kLr 5 | 41/D3/BIffEy9MYSeEhSZY04vBCbtndFBU1N1thcjRZa0pddJtLKoLGbiE7Gvcic 6 | UiPT5IvlI8YLsldShNcGzXYQNVuUC5lfJDq5V+gIYBRLzMJm+gdhtejxSkFOK8Cq 7 | QIRBwFZ5bRN0+SScMyPtdDs7KFWrVAg3wXXkScWJ0gla/afDAa53j2E+Mfdmn5hX 8 | sl1h2matAgMBAAECggEAAWV9PmZCkNtDAgtpWetj77BW2U2uMObQfcxaxPnZo16l 9 | xfPC6MjArAYr666OEy8Ta6gnUrkV8YULKbyDPLMfQXtFv87mFvJgBTUbRXJLG9Nu 10 | d5R/5zRCXba7e0uOUa1pyUeouKPoe6cprwhitYLtPRZLOZV6kaEFKiqGgeH029gH 11 | IJtITAV1vyBoR40Ews819/oneJs/UKbB18wl2+VtdiRHi4H2kGR0HD0mqWHBOqX0 12 | 95oM4k2lhQuOj06E8eL6ixDh7j5RD7IBRdhTkejN7ju94k3caQ4jPQznK0HMPnav 13 | 2xK+qT3jMwdV7abbNdgt4WfCcJq+T9n+ONfWD8kEgQKBgQDeQs3uYXf/2cqJUunz 14 | IPXymny9HO47SfzPdszLzeuzQ/s9IV3rnADrT4fLqf0KPHGqiapzwYS7gDAn5piA 15 | lUK1kdxmJNaxhzu9QBTbc9Dx0EzsaiBRJY67v8Y0jYQJzC2SFM7jd5XXoYOTHqgk 16 | mPGorlfoPnch8eD8y/SOAZHvYQKBgQDKUxb8/i3S0U1oMwCQqkUhcmn/6dt3SKJE 17 | DXLXQ74ywrLDADEB5S+mlbsmc8YFeWn35TDRHOhjt1IFhZhfGfvmLnk0nC0lowkW 18 | ps4YuCGXQKBXLGKMQcwQIdFL4b4XqKSVs5q/ovl8caAMmU6zdwPfqfJEPojBglpV 19 | XIrBUAd2zQKBgGWKlbSDmSQtZwen9rQZ6eNlSvnHHtu9aJ+LwfKWaEllFIIQHW2H 20 | vy/CDOMyDSXBCZkE/feWgMJ8xQNlqWSNTIXRVqTfzaobg2JpxV2hIuNm683SimMo 21 | SnZiHfZidOFZA7TKht6LAs0ZwBfVmLHnofgTdpvm9dxvOf+kWl0KQJbhAoGBAKEl 22 | CLw8Hnc46TRC2wJd+22mWTLRKlGZclRNedhmRkjbtdGGh4IA7rD5tPPtZhzaFUKy 23 | Mu163sT9L8DiJPgqE+3DuhnjAjpl3klCVghuL6LugEauPVeE2GlI5hBQHj1tvpjV 24 | Uj2sKpWpUlZ2jK537ofoLeOZAwdNMXGnoYRSn39JAoGBAJNqnZoCDGTalnuQ06gp 25 | j0YUnuZrKDMkGtM0OD8yY7r1nHSRiHjoyP6DOGUTmtl+e/OnhQaOEJbKg32k+C9y 26 | Vt6i9LkWtObO2NGf81yW7Kpk/kyZkGUNn5sPcPelC7QZiaQJd2WQQ0AEJNSFkenA 27 | 5x36Kxm5IjhrkvG3QRDQfOQl 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /testhelp/ssl-client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Tobias Heukäufer 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import sys 8 | import socket 9 | import ssl 10 | from ssl import SSLSocket 11 | from typing import List 12 | 13 | 14 | def send_msg(socket: SSLSocket, msg: str): 15 | socket.sendall(bytearray([0x0B, *msg.encode("UTF-8"), 0x1C, 0x0D])) 16 | 17 | 18 | def receive_msg(socket: SSLSocket) -> str: 19 | received: List[int] = list() 20 | 21 | read_0x1c = False 22 | read_0x0d = False 23 | while not (read_0x1c and read_0x0d): 24 | data = socket.recv(1024) 25 | 26 | for b in data: 27 | received.append(b) 28 | if b == 0x1C: 29 | read_0x1c = True 30 | elif read_0x1c and b == 0x0D: 31 | read_0x0d = True 32 | else: 33 | read_0x1c = False 34 | 35 | return bytes(received[1:-2]).decode("UTF-8") 36 | 37 | 38 | if len(sys.argv) < 3: 39 | print("Not enough parameters: python3 ssl-client.py ") 40 | exit(0) 41 | 42 | hostname = "localhost" 43 | port = int(sys.argv[1]) 44 | 45 | context = ssl.create_default_context() 46 | context.check_hostname = False 47 | context.verify_mode = ssl.CERT_NONE 48 | if int(sys.argv[2]) == 1: 49 | context.load_cert_chain("client.crt", "client.key") 50 | 51 | with socket.create_connection((hostname, port)) as my_socket: 52 | with context.wrap_socket(my_socket, server_hostname=hostname) as secure_socket: 53 | print("Wrapped socket!") 54 | 55 | send_msg(secure_socket, "Hello!") 56 | print("Sent msg!") 57 | 58 | response = receive_msg(secure_socket) 59 | print("Received msg!") 60 | 61 | print("From server: {}".format(response)) 62 | -------------------------------------------------------------------------------- /testhelp/ssl-server.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Tobias Heukäufer 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import sys 8 | import ssl 9 | import socket 10 | from ssl import SSLSocket 11 | from typing import List 12 | 13 | 14 | def send_msg(socket: SSLSocket, msg: str): 15 | socket.sendall(bytearray([0x0B, *msg.encode("UTF-8"), 0x1C, 0x0D])) 16 | 17 | 18 | def receive_msg(socket: SSLSocket) -> str: 19 | received: List[int] = list() 20 | 21 | read_0x1c = False 22 | read_0x0d = False 23 | while not (read_0x1c and read_0x0d): 24 | data = socket.recv(1024) 25 | 26 | for b in data: 27 | received.append(b) 28 | if b == 0x1C: 29 | read_0x1c = True 30 | elif read_0x1c and b == 0x0D: 31 | read_0x0d = True 32 | else: 33 | read_0x1c = False 34 | 35 | return bytes(received[1:-2]).decode("UTF-8") 36 | 37 | 38 | if len(sys.argv) < 3: 39 | print("Not enough parameters: python3 ssl-server.py ") 40 | exit(0) 41 | 42 | port = int(sys.argv[1]) 43 | 44 | context = ssl.SSLContext(ssl.PROTOCOL_TLS) 45 | context.load_cert_chain("server.crt", "server.key") 46 | if int(sys.argv[2]) == 1: 47 | context.load_verify_locations("client.crt") 48 | context.verify_mode = ssl.CERT_REQUIRED 49 | 50 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock: 51 | sock.bind(("localhost", port)) 52 | sock.listen() 53 | 54 | with context.wrap_socket(sock, server_side=True) as ssl_sock: 55 | conn, addr = ssl_sock.accept() 56 | 57 | msg = receive_msg(conn) 58 | send_msg(conn, "You're welcome!") 59 | 60 | conn.close() 61 | 62 | print("From client: {}".format(msg)) 63 | -------------------------------------------------------------------------------- /testhelp/unsecure-client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Tobias Heukäufer 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import socket 8 | import sys 9 | from typing import List 10 | 11 | 12 | def send_msg(socket: socket.socket, msg: str): 13 | socket.sendall(bytearray([0x0B, *msg.encode("UTF-8"), 0x1C, 0x0D])) 14 | 15 | 16 | def receive_msg(socket: socket.socket) -> str: 17 | received: List[int] = list() 18 | 19 | read_0x1c = False 20 | read_0x0d = False 21 | while not (read_0x1c and read_0x0d): 22 | data = socket.recv(1024) 23 | 24 | for b in data: 25 | received.append(b) 26 | if b == 0x1C: 27 | read_0x1c = True 28 | elif read_0x1c and b == 0x0D: 29 | read_0x0d = True 30 | else: 31 | read_0x1c = False 32 | 33 | return bytes(received[1:-2]).decode("UTF-8") 34 | 35 | 36 | if len(sys.argv) < 2: 37 | print("Not enough parameters: python3 unsecure-client.py ") 38 | exit(0) 39 | 40 | hostname = "localhost" 41 | port = int(sys.argv[1]) 42 | 43 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 44 | my_socket.connect((hostname, port)) 45 | send_msg(my_socket, "Hello!") 46 | response = receive_msg(my_socket) 47 | my_socket.close() 48 | 49 | print("Answer: {}".format(response)) 50 | -------------------------------------------------------------------------------- /testhelp/unsecure-server.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Tobias Heukäufer 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import sys 8 | import socket 9 | from typing import List 10 | 11 | 12 | def send_msg(socket: socket.socket, msg: str): 13 | socket.sendall(bytearray([0x0B, *msg.encode("UTF-8"), 0x1C, 0x0D])) 14 | 15 | 16 | def receive_msg(socket: socket.socket) -> str: 17 | received: List[int] = list() 18 | 19 | read_0x1c = False 20 | read_0x0d = False 21 | while not (read_0x1c and read_0x0d): 22 | data = socket.recv(1024) 23 | 24 | for b in data: 25 | received.append(b) 26 | if b == 0x1C: 27 | read_0x1c = True 28 | elif read_0x1c and b == 0x0D: 29 | read_0x0d = True 30 | else: 31 | read_0x1c = False 32 | 33 | return bytes(received[1:-2]).decode("UTF-8") 34 | 35 | 36 | if len(sys.argv) < 2: 37 | print("Not enough parameters: python3 unsecure-server.py ") 38 | exit(0) 39 | 40 | port = int(sys.argv[1]) 41 | 42 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock: 43 | sock.bind(("localhost", port)) 44 | sock.listen() 45 | 46 | conn, addr = sock.accept() 47 | 48 | msg = receive_msg(conn) 49 | send_msg(conn, "You're welcome!") 50 | 51 | conn.close() 52 | 53 | print("From client: {}".format(msg)) 54 | --------------------------------------------------------------------------------