Provides data for the VerifyCertificate event.
TLS Secure DICOM communications between an SCP and an SCU has a handshake process where the SCP and SCU verify each others provided certificates.
The VerifyCertificate event is called once for each certificate in a certificate chain, and provides information about the verification process.
This is useful for determining why a TLS DICOM Secure communication failed to be established.
The VerifyCertificateEventArgs.CertificateString provides information about a certificate, including:
For example, if an SCU is providing a certificate that is created without SSL Server and SSL Client purposes, the VerifyCertificateEventArgs.ErrorException property will have a value of DicomSecurityCertificateExceptionCode.InvalidPurpose, and show the problem in the Certificate Purposes section of the VerifyCertificateEventArgs.CertificateString.
An example VerifyCertificateEventArgs.CertificateString corresponding to a DicomSecurityCertificateExceptionCode value of InvalidPurpose error is shown below:
Subject Name: CN = Test Client, C = US, ST = Nebraska, L = Omaha, O = Test Client Organization
Issuer Name: CN = LEAD CA, L = Charlotte, ST = North Carolina, C = US, emailAddress = support@leadtools.com, O = "LEAD Technologies, Inc."
Valid From: Jun 1 21:31:35 2020 GMT
Valid To: May 30 21:31:35 2030 GMT
Serial Number: ae:8a:75:37:56:03:c4:45
Basic Constraints:Not a CA (Certificate Authority)
Certificate purposes:
SSL client : No
SSL client CA : No
SSL server : No
SSL server CA : No
Netscape SSL server : No
Netscape SSL server CA : No
S/MIME signing : No
S/MIME signing CA : No
S/MIME encryption : No
S/MIME encryption CA : No
CRL signing : No
CRL signing CA : No
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : No
Time Stamp signing : No
The VerifyCertificateEventArgs.ErrorException property is a DicomSecurityCertificateException exception that indicates success or an error.
The VerifyCertificateEventArgs.Ok property determines the verification behavior.
Set VerifyCertificateEventArgs.Ok to 0 to stop the verification process with a "verification failed" state. If the DicomConnection(string,dicomnetsecuritymode,dicomopensslcontextcreationsettings) constructor is used and DicomOpenSslVerificationFlags.Peer is passed via the DicomOpenSslContextCreationSettings argument, a verification failure alert is sent to the SCU and the TLS/SSL handshake is terminated.
Set VerifyCertificateEventArgs.Ok to 1 to continue the verification process. If VerifyCertificateEventArgs.Ok is always set to 1, the TLS/SSL handshake will not be terminated with respect to verification failures and the connection will be established.
Leave VerifyCertificateEventArgs.Ok unchanged to get the default verification behavior.
using Leadtools;
using Leadtools.Dicom.Scu;
using Leadtools.Dicom.Scu.Common;
using Leadtools.Dicom;
using Leadtools.Dicom.Common.DataTypes;
using Leadtools.Dicom.Common.DataTypes.Status;
public void MoveSeriesSecure()
{
DicomEngine.Startup();
DicomNet.Startup();
QueryRetrieveScu retrieveSeriesSecure = new QueryRetrieveScu();
FindQuery query = new FindQuery();
DicomScp scp = new DicomScp();
//
// Change these parameters to reflect the calling AETitle.
//
retrieveSeriesSecure.AETitle = "T20_CLIENT64";
retrieveSeriesSecure.HostPort = 1030;
retrieveSeriesSecure.HostAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
retrieveSeriesSecure.UseSecureHost = true; // SCU host is secure
retrieveSeriesSecure.SecureHostSettings = new DicomOpenSslContextCreationSettings(DicomSslMethodType.SslV23, @"C:\Certificates\ca.pem", DicomOpenSslVerificationFlags.None, 9, DicomOpenSslOptionsFlags.AllBugWorkarounds);
//
// Change these parameters to reflect the called AETitle (server).
//
scp.AETitle = "L20_PACS_SCP64";
scp.Port = 534;
scp.Timeout = 60;
scp.PeerAddress = IPAddress.Parse("192.168.5.102");
scp.Secure = false; // SCP is unsecure
retrieveSeriesSecure.BeforeCMove += new BeforeCMoveDelegate(retrieveSeriesSecure_BeforeCMove);
retrieveSeriesSecure.Moved += new MovedDelegate(retrieveSeriesSecure_Moved);
retrieveSeriesSecure.AfterCMove += new AfterCMoveDelegate(retrieveSeriesSecure_AfterCMove);
retrieveSeriesSecure.HostReady += RetrieveSeriesSecure_HostReady;
retrieveSeriesSecure.AfterSecureLinkReady += RetrieveSeriesSecure_AfterSecureLinkReady;
retrieveSeriesSecure.Move(scp, string.Empty, "1.2.840.114257.3.6.5.41964868", "1.2.840.114257.3.6.5.5.4214471");
retrieveSeriesSecure.VerifyCertificate += RetrieveSeriesSecure_VerifyCertificate;
DicomNet.Shutdown();
DicomEngine.Shutdown();
}
private void RetrieveSeriesSecure_VerifyCertificate(object sender, VerifyCertificateEventArgs e)
{
if (e != null)
{
Console.WriteLine("VerifyCertificate\n", e.CertificateString);
if (e.ErrorException.Code != DicomSecurityCertificateExceptionCode.Success)
{
Console.WriteLine(e.ErrorException.Message);
}
}
}
private void RetrieveSeriesSecure_AfterSecureLinkReady(object sender, AfterSecureLinkReadyEventArgs e)
{
DicomNet net = (DicomNet)sender;
if (net != null)
{
if (e.Error != DicomExceptionCode.Success)
{
ClientSecureLinkReadyException exception = new ClientSecureLinkReadyException("Secure handshake (TLS) failed.", e.Error);
Console.WriteLine("Secure handshake (TLS) failed: code{0}", exception.Code);
throw exception;
}
}
}
private void RetrieveSeriesSecure_HostReady(object sender, HostReadyEventArgs e)
{
DicomConnection host = e.ScpHost;
if (host != null)
{
Console.WriteLine("HostReady: Host AETitle:{0} Host Port:{1}", e.ScpHost.AETitle, e.ScpHost.HostPort);
if (host.SecurityMode == DicomNetSecurityMode.Tls)
{
host.PrivateKeyPassword += Host_PrivateKeyPassword;
host.SetTlsClientCertificate(@"C:\Certificates\client.pem", DicomTlsCertificateType.Pem, @"C:\Certificates\client.pem");
host.PrivateKeyPassword -= Host_PrivateKeyPassword;
host.SetTlsCipherSuiteByIndex(0, 0);
host.SetTlsCipherSuiteByIndex(0, DicomTlsCipherSuiteType.DheRsaWithDesCbcSha);
host.SetTlsCipherSuiteByIndex(1, DicomTlsCipherSuiteType.DheRsaWith3DesEdeCbcSha);
host.SetTlsCipherSuiteByIndex(2, DicomTlsCipherSuiteType.DheRsaAes256Sha);
host.SetTlsCipherSuiteByIndex(3, DicomTlsCipherSuiteType.RsaWithAes128CbcSha);
host.SetTlsCipherSuiteByIndex(4, DicomTlsCipherSuiteType.RsaWith3DesEdeCbcSha);
host.SetTlsCipherSuiteByIndex(5, DicomTlsCipherSuiteType.DheRsaWithAes128GcmSha256);
host.SetTlsCipherSuiteByIndex(6, DicomTlsCipherSuiteType.EcdheRsaWithAes128GcmSha256);
host.SetTlsCipherSuiteByIndex(7, DicomTlsCipherSuiteType.DheRsaWithAes256GcmSha384);
host.SetTlsCipherSuiteByIndex(8, DicomTlsCipherSuiteType.EcdheRsaWithAes256GcmSha384);
}
}
}
private void Host_PrivateKeyPassword(object sender, PrivateKeyPasswordEventArgs e)
{
e.PrivateKeyPassword = "test";
}
void retrieveSeriesSecure_BeforeCMove(object sender, BeforeCMoveEventArgs e)
{
Console.WriteLine("Before CMove");
}
void retrieveSeriesSecure_Moved(object sender, MovedEventArgs e)
{
Console.WriteLine(e.Patient.Name.Full);
Console.WriteLine(e.Study.AccessionNumber);
Console.WriteLine(e.Series.Number);
Console.WriteLine(e.Instance.SOPInstanceUID);
Console.WriteLine("==========================================");
}
void retrieveSeriesSecure_AfterCMove(object sender, AfterCMoveEventArgs e)
{
Console.WriteLine("After CMove");
Console.WriteLine("\t{0} Completed", e.Completed);
Console.WriteLine("\t{0} Failed", e.Failed);
Console.WriteLine("\t{0} Warning", e.Warning);
Console.WriteLine("\tStatus: {0}", e.Status);
if (e.Status != DicomCommandStatusType.Success)
{
string statusAllString = e.StatusAll.ToString(StatusFormatFlags.IgnoreStatus, "\n", "\t");
Console.WriteLine(statusAllString);
}
}