#include "ltdic.h"
L_INT LDicomNet::Initialize(pszPath, nMode, pCtxCreate)
L_TCHAR *pszPath; |
character string |
L_INT32 nMode; |
initialization mode |
pL_SSL_CTX_CREATE *pCtxCreate; |
pointer to a structure |
This function is to be used in conjunction with the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) constructor in order to change security options from the defaults. This function is available in the Medical Imaging Suite toolkits.
| Parameter | Description | |
| pszPath | Character string that contains the location of the temporary files. This should be the same string that was used in the LDicomNet constructor. | |
| nMode | Flag that indicates the security mode to use when initializing the network structure. This should be the same flag that was used in the LDicomNet constructor. Possible values are: | |
| Value | Meaning | |
| DICOM_SECURE_NONE | No security mode. | |
| DICOM_SECURE_ISCL | Integrated Secure Communication Layer security mode. | |
| DICOM_SECURE_TLS | Transport Layer Security security mode. | |
| pCtxCreate | Pointer to the L_SSL_CTX_CREATE structure that is used to modify the security defaults. This structure is used only if the nMode flag is DICOM_SECURE_TLS. Pass NULL to get the default values. | |
SUCCESS |
The function was successful. |
> 0 |
An error occurred. Refer to Return Codes.. |
This function is to be used in conjunction with the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) constructor when changing security options from the defaults. Note that when using the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) version of the constructor, in addition to calling LDicomNet::Startup it is also necessary to call LDicomNet::Initialize in order to prepare the LDicomNet object for use. Use the pCtxCreate parameter when the nMode flag is set to DICOM_SECURE_TLS.
Note that the following uses of the LDicomNet constructors are functionally equivalent:
1.
LDicomNet *pNet = new LDicomNet(pszPath, nMode);
2.
LDicomNet *pNet = new LDicomNet(pszPath, nMode, 0);
If (pNet)
pNet->Initialize(pszPath, nMode, NULL);
Required DLLs and Libraries
LTDIC For a listing of the exact DLLs and Libraries needed, based on the toolkit version, refer to Files To Be Included With Your Application |
Win32, x64
Functions: |
|
Topics: |
|
|
|
|
|
|
|
|
This is a basic, but complete example that shows a DICOM client sending a C-Echo-REQ to a server, and the server processing the request using TLS Security.
#define MAKE_IMAGE_PATH(pFileName) TEXT("C:\\Users\\Public\\Documents\\LEADTOOLS Images\\")pFileNamenamespace LDicomNet_Initialize_Namespace{#ifndef CA_CERT_NAME#define CA_CERT_NAME MAKE_IMAGE_PATH(TEXT("CA.pem"))#endif#ifndef SERVER_CERT_NAME#define SERVER_CERT_NAME MAKE_IMAGE_PATH(TEXT("Server.pem"))#endif#ifndef CLIENT_CERT_NAME#define CLIENT_CERT_NAME MAKE_IMAGE_PATH(TEXT("Client.pem"))#endif// Logs a message// This implementation logs to the console, and the debug windowL_VOID LogMessage(TCHAR *szMsg){wprintf(TEXT("\n"));wprintf(szMsg);OutputDebugStringW(TEXT("\n"));OutputDebugStringW(szMsg);}L_VOID LogMessage(TCHAR *s, L_INT n){TCHAR szLog[200] = {0};wsprintf(szLog, TEXT("%s [%d]"), s, n);LogMessage(szLog);}L_INT PrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag){UNREFERENCED_PARAMETER(nFlag);LPCTSTR pszMyPassword= TEXT("test");L_INT nRet = 0;if ((L_INT)_tcslen(pszMyPassword) < nSize){_tcsncpy_s(pszPassword, nSize, pszMyPassword, nSize);nRet = (L_INT)_tcslen(pszMyPassword);}return nRet;}L_VOID SetupTlsContext(LDicomNet *pNet, L_TCHAR *pszCertName){if (pNet != NULL){L_SSL_CTX_CREATE ctxCreate;memset(&ctxCreate, 0, sizeof(L_SSL_CTX_CREATE));ctxCreate.uStructSize = sizeof(L_SSL_CTX_CREATE);ctxCreate.uFlags = FLAG_SSL_CTX_CREATE_METHOD_TYPE | FLAG_SSL_CTX_CREATE_VERIFY_MODE |FLAG_SSL_CTX_CREATE_VERIFY_DEPTH | FLAG_SSL_CTX_CREATE_OPTIONS | FLAG_SSL_CTX_CREATE_CAFILE;ctxCreate.nMethodTypeSSL= TYPE_SSLV23_METHOD;ctxCreate.pszCAfile = CA_CERT_NAME;ctxCreate.uVerifyMode = L_SSL_VERIFY_PEER | L_SSL_VERIFY_FAIL_IF_NO_PEER_CERT;ctxCreate.nVerifyDepth = 2;ctxCreate.nOptions = L_SSL_OP_NO_SSLv2|L_SSL_OP_ALL;ctxCreate.nReserved1 = 0;ctxCreate.nReserved2 = 0;L_INT nRet = pNet->Initialize(NULL, DICOM_SECURE_TLS, &ctxCreate);if (nRet == DICOM_SUCCESS){L_TCHAR szMsg[200] = {0};// Assign the server the certificate// Note that SERVER_CERT_NAME contains both the password and an encrypted private key// When loading the private key, the OnPrivateKeyPassword virtual function is called// so that the encryption password "test" can be suppliednRet = pNet->SetServerCertificateTLS (pszCertName, L_TLS_FILETYPE_PEM, NULL);if (nRet == DICOM_SUCCESS)wsprintf(szMsg, TEXT("%s loaded successfully"), pszCertName);elsewsprintf(szMsg, TEXT("%s could not be loaded successfully -- error[%d]"), pszCertName, nRet);LogMessage(szMsg);}}}L_VOID DumpTlsInformation(LDicomNet *pNet){L_CIPHERSUITE ciphersuite;ciphersuite = pNet->GetCiphersuiteTLS();switch(ciphersuite){case TLS_DHE_RSA_WITH_DES_CBC_SHA:LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_WITH_DES_CBC_SHA"));break;case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"));break;case TLS_DHE_RSA_AES256_SHA:LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_AES256_SHA"));break;}L_UINT32 nEncryptionAlgorithm = pNet->GetEncryptionAlgorithmTLS(ciphersuite);switch(nEncryptionAlgorithm){case L_CRYPT_NONE:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_NONE"));break;case L_CRYPT_DES:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_DES"));break;case L_CRYPT_3DES:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_3DES"));break;case L_CRYPT_RC4:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_RC4"));break;case L_CRYPT_RC2:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_RC2"));break;case L_CRYPT_IDEA:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_IDEA"));break;case L_CRYPT_FORTEZZA:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_FORTEZZA"));break;case L_CRYPT_AES:LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_AES"));break;}L_UINT32 auth = pNet->GetAuthenticationAlgorithmTLS(ciphersuite);switch(auth){case L_MUTUALAUTH_NONE:LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_NONE"));break;case L_MUTUALAUTH_RSA:LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_RSA"));break;case L_MUTUALAUTH_DSS:LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_DSS"));break;case L_MUTUALAUTH_DH:LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_DH"));break;}L_UINT32 integrity = pNet->GetIntegrityAlgorithmTLS(ciphersuite);switch(integrity){case L_MAC_NONE:LogMessage(TEXT(" Message Authentication code type: L_MAC_NONE"));break;case L_MAC_SHA1:LogMessage(TEXT(" Message Authentication code type: L_MAC_SHA1"));break;case L_MAC_MD5:LogMessage(TEXT(" Message Authentication code type: L_MAC_MD5"));break;}L_UINT32 keyExchange = pNet->GetKeyExchangeAlgorithmTLS(ciphersuite);switch(keyExchange){case L_KEYEXCHANGE_NONE:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_NONE"));break;case L_KEYEXCHANGE_RSA_SIGNED_DHE:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_RSA_SIGNED_DHE"));break;case L_KEYEXCHANGE_RSA:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_RSA"));break;case L_KEYEXCHANGE_DH:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_DH"));break;case L_KEYEXCHANGE_DH_DSS:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_DH_DSS"));break;case L_KEYEXCHANGE_FORTEZZA:LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_FORTEZZA"));break;}L_UINT32 uKeyLength = pNet->GetEncryptKeyLengthTLS(ciphersuite);LogMessage(TEXT(" Encrypt Key Length"), uKeyLength);L_UINT32 uMutualAuthKeyLength = pNet->GetMutualAuthKeyLengthTLS(ciphersuite);LogMessage(TEXT(" Mutual authentication key length"), uMutualAuthKeyLength);}// *******************************************************************************************// Client Class//// Class that is used to connect to the server// *******************************************************************************************class CMyClient : public LDicomNet{public:CMyClient(L_INT32 nMode): LDicomNet(NULL, nMode){m_waitEvent = CreateEvent( NULL, TRUE, TRUE, TEXT("ClientEvent"));ResetEvent(m_waitEvent);this->SetCipherToIndexTLS(0, TLS_DHE_RSA_WITH_DES_CBC_SHA);L_CIPHERSUITE ciphersuite;ciphersuite = this->GetCipherFromIndexTLS(0);assert(ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA);this->SetClientCertificateTLS( MAKE_IMAGE_PATH(TEXT("Client.pem")), L_TLS_FILETYPE_PEM, NULL);}~CMyClient(void){CloseHandle(m_waitEvent);}// ClientL_VOID OnConnect (L_INT nError);L_VOID OnSecureLinkReady (L_UINT32 nError);L_VOID OnReceiveAssociateAccept (LDicomAssociate *pPDU);L_VOID OnReceiveReleaseResponse ();L_VOID OnReceiveCEchoResponse (L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus);L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);L_BOOL Wait(DWORD timeout = 5000);private:HANDLE m_waitEvent;};// Continues dispatching messages until hEvent is signalled, our timeout// Returns TRUE if hEvent is signalled// Returns FALSE if timeoutL_BOOL MessageLoop (HANDLE hEvent, // handles that need to be waited onDWORD timeout // timeout in milliseconds){DWORD dwStart = GetTickCount();MSG msg = {0};volatile L_BOOL bRunForever = TRUE;while (bRunForever){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0){ResetEvent(hEvent);return TRUE;}DWORD dwCurrent = GetTickCount();if ((dwCurrent - dwStart) > timeout){return FALSE;}}return TRUE;}L_VOID CMyClient::OnConnect(L_INT nError){L_TCHAR szMsg[200] = {0};wsprintf(szMsg, TEXT("CMyClient::OnConnect: nError[%d]"), nError);LogMessage(szMsg);}L_VOID CMyClient::OnSecureLinkReady(L_UINT32 nError){UNREFERENCED_PARAMETER(nError);SetEvent(m_waitEvent);L_UINT32 uSecureMode = this->GetSecureMode();switch(uSecureMode){case DICOM_SECURE_NONE:LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_NONE"));break;case DICOM_SECURE_ISCL:LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_ISCL"));break;case DICOM_SECURE_TLS:{LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_TLS"));if (nError == DICOM_SUCCESS){DumpTlsInformation(this);}else{L_TCHAR szMsg[200] = {0};wsprintf(szMsg, TEXT( "ClientTLS: Secure link error %d"), nError);LogMessage(szMsg);}}break;}}L_VOID CMyClient::OnReceiveAssociateAccept (LDicomAssociate *pPDU){UNREFERENCED_PARAMETER(pPDU);SetEvent(m_waitEvent);LogMessage(TEXT("CMyClient::OnReceiveAssociateAccept"));}L_VOID CMyClient::OnReceiveCEchoResponse(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus){SetEvent(m_waitEvent);L_TCHAR szMsg[200] = {0};if (pszClass == NULL){pszClass = TEXT("");}wsprintf(szMsg, TEXT("CMyClient::OnReceiveCEchoResponse: \n\tnPresentationID[%d], \n\tnMessageID[%d], \n\tpszClass[%s], \n\tnStatus[%d]"), nPresentationID, nMessageID, pszClass, nStatus);LogMessage(szMsg);}L_INT CMyClient::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag){return PrivateKeyPassword(pszPassword, nSize, nFlag);}L_VOID CMyClient::OnReceiveReleaseResponse(){SetEvent(m_waitEvent);LogMessage(TEXT("CMyClient::OnReceiveReleaseResponse"));}L_BOOL CMyClient::Wait(DWORD timeout){L_BOOL bRet = MessageLoop(m_waitEvent, timeout);return bRet;}// *******************************************************************************************// Server Connection Class//// When a client connects, CMyServer creates a new instance of the CMyServerConnection class// and accepts the connection.// *******************************************************************************************class CMyServerConnection : public LDicomNet{public:CMyServerConnection(L_INT32 nMode): LDicomNet(NULL, nMode){}~CMyServerConnection(void){}// ServerL_VOID OnReceiveAssociateRequest (LDicomAssociate *pPDU);L_VOID OnReceiveAssociateReject (L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason);L_VOID OnReceiveCEchoRequest (L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass);L_VOID OnReceiveReleaseRequest ();L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);};#define SIZEINWORD(p) sizeof(p)/sizeof(L_TCHAR)L_VOID CMyServerConnection::OnReceiveAssociateRequest(LDicomAssociate *pPDU){LogMessage(TEXT("\tCMyServerConnection::OnReceiveAssociateRequest"));//check the version, if not 1, reject itif (pPDU->GetVersion() != 1){LogMessage(TEXT("\tCMyServerConnection::SendAssociateReject"));SendAssociateReject(PDU_REJECT_RESULT_PERMANENT,PDU_REJECT_SOURCE_USER,PDU_REJECT_REASON_UNKNOWN);}else{LDicomAssociate DicomAssociate(FALSE);L_TCHAR clientAE[20] = {0};pPDU->GetCalling(clientAE, 20);if (lstrcmp(clientAE, TEXT("LEAD_CLIENT")) != 0){LogMessage(TEXT("\tCMyServerConnection::SendAssociateReject"));SendAssociateReject(PDU_REJECT_RESULT_PERMANENT,PDU_REJECT_SOURCE_USER,PDU_REJECT_REASON_UNKNOWN);return;}//Copy presentation objects from received//Reply that we only support the first Transfer Syntax from the received hPDUL_TCHAR szTransfer[PDU_MAX_UID_SIZE+1] = {0};L_TCHAR szAbstract[PDU_MAX_UID_SIZE+1] = {0};L_INT iPresentationCount = pPDU->GetPresentationCount();for (L_UCHAR i = 0; i<iPresentationCount; i++){L_UCHAR nId = pPDU->GetPresentation(i);pPDU->GetTransfer(nId, 0, szTransfer, PDU_MAX_UID_SIZE+1);L_UCHAR nResult = PDU_ACCEPT_RESULT_SUCCESS;pPDU->GetAbstract(nId, szAbstract, PDU_MAX_UID_SIZE+1);DicomAssociate.AddPresentation( nId, nResult, szAbstract);DicomAssociate.AddTransfer( nId, szTransfer);}LogMessage(TEXT("\tCMyServerConnection::SendAssociateAccept"));SendAssociateAccept(&DicomAssociate);}}L_VOID CMyServerConnection::OnReceiveAssociateReject(L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason){L_TCHAR szMsg[200] = {0};wsprintf(szMsg, TEXT("\tOnReceiveAssociateReject\nResult[%d]\nSource[%d]\nReason[%d]"), nResult, nSource, nReason);LogMessage(szMsg);}L_VOID CMyServerConnection::OnReceiveCEchoRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass){LogMessage(TEXT("\tCMyServerConnection::OnReceiveCEchoRequest"));LogMessage(TEXT("\tCMyServerConnection::SendCEchoResponse"));SendCEchoResponse(nPresentationID, nMessageID, pszClass, COMMAND_STATUS_SUCCESS);}L_VOID CMyServerConnection::OnReceiveReleaseRequest(){LogMessage(TEXT("\tCMyServerConnection::OnReceiveReleaseRequest"));LogMessage(TEXT("\tCMyServerConnection::SendReleaseResponse"));SendReleaseResponse();}L_INT CMyServerConnection::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag){return PrivateKeyPassword(pszPassword, nSize, nFlag);}// *******************************************************************************************// Server Class//// Listens for connections// When a client connects, this class creates a CMyServerConnection and accepts the connection// *******************************************************************************************class CMyServer : public LDicomNet{public:CMyServer(L_INT32 nMode): LDicomNet(NULL, nMode){m_pServerConnection = NULL;}~CMyServer(void){if (m_pServerConnection != NULL){delete m_pServerConnection;}}L_VOID OnAccept (L_INT nError);L_VOID OnClose (L_INT nError, LDicomNet *pServerConnection);L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);CMyServerConnection *m_pServerConnection;};L_VOID CMyServer::OnAccept(L_INT nError){LogMessage(TEXT("\tCMyServer::OnAccept"));if (nError != DICOM_SUCCESS){return;}if (m_pServerConnection != NULL){delete m_pServerConnection;m_pServerConnection = NULL;}m_pServerConnection = new CMyServerConnection(DICOM_SECURE_TLS);if (m_pServerConnection == NULL){return;}SetupTlsContext(m_pServerConnection, SERVER_CERT_NAME);nError = LDicomNet::Accept(m_pServerConnection);if (nError != DICOM_SUCCESS){delete m_pServerConnection;return;}}L_VOID CMyServer::OnClose(L_INT nError, LDicomNet *pServerConnection){UNREFERENCED_PARAMETER(nError);LogMessage(TEXT("\tCMyServer::OnClose"));if (m_pServerConnection == pServerConnection){m_pServerConnection = NULL;}delete (CMyServerConnection *)pServerConnection;}L_INT CMyServer::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag){return PrivateKeyPassword(pszPassword, nSize, nFlag);}// *******************************************************************************************// Sample starts here// *******************************************************************************************#define WaitForProcessing() \{ \if (!client.Wait()) \{ \LogMessage(TEXT("Timeout: client.Connect")); \nRet = DICOM_ERROR_NET_TIME_OUT; \goto Cleanup; \} \}L_INT LDicomNet_InitializeExample(){LogMessage(TEXT("\n\n *** Initialize ***"));L_TCHAR *pszServerAddress = TEXT("127.0.0.1");L_UINT uServerPort = 105;L_INT nRet = DICOM_SUCCESS;LDicomNet::StartUp();CMyClient client(DICOM_SECURE_TLS);CMyServer server(DICOM_SECURE_TLS);SetupTlsContext(&client, CLIENT_CERT_NAME);SetupTlsContext(&server, SERVER_CERT_NAME);LogMessage(TEXT("\tCMyServer::Listen"));nRet = server.Listen(pszServerAddress, uServerPort, 5);LogMessage(TEXT("CMyClient::Connect"));client.Connect(NULL, 0, pszServerAddress, uServerPort);if (!client.Wait(2000)){if (!client.IsConnected()){LogMessage(TEXT("Timeout: client.Connect"));nRet = DICOM_ERROR_NET_TIME_OUT;goto Cleanup;}}if (nRet == DICOM_SUCCESS){//create the Associate Class as RequestLDicomAssociate dicomAssociateRequest(TRUE);dicomAssociateRequest.Default();// Send A-Associate-RQ messagedicomAssociateRequest.SetCalled(TEXT("LEAD_SERVER"));dicomAssociateRequest.SetCalling(TEXT("LEAD_CLIENT"));LogMessage(TEXT("CMyClient::SendAssociateRequest"));nRet = client.SendAssociateRequest(&dicomAssociateRequest);if (!client.Wait(5000)){LogMessage(TEXT("Timeout: client.Connect"));nRet = DICOM_ERROR_NET_TIME_OUT;goto Cleanup;}}L_UCHAR nPresentationID = client.GetAssociate()->FindAbstract(UID_VERIFICATION_CLASS);L_UINT16 uUniqueID = 99;LogMessage(TEXT("CMyClient::SendCEchoRequest"));client.SendCEchoRequest( nPresentationID, uUniqueID, UID_VERIFICATION_CLASS);WaitForProcessing();LogMessage(TEXT("CMyClient::SendReleaseRequest"));client.SendReleaseRequest();WaitForProcessing();Cleanup:LogMessage(TEXT("CMyClient::Close"));client.Close();client.Wait(1000);LogMessage(TEXT("\tCMyServer::Close"));server.Close();LDicomNet::ShutDown();return nRet;}}