Files
UA-.NETStandard/SampleApplications/Samples/GDSCommon/Controls/TrustListControl.cs
T

637 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Windows.Forms;
using System.Security.Cryptography.X509Certificates;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Gds;
namespace Opc.Ua.Gds
{
public partial class CertificateStoreControl : UserControl
{
public CertificateStoreControl()
{
InitializeComponent();
CertificateListGridView.AutoGenerateColumns = false;
ImageList = new ImageListControl().ImageList;
m_dataset = new DataSet();
m_dataset.Tables.Add("Certificates");
m_dataset.Tables[0].Columns.Add("Subject", typeof(string));
m_dataset.Tables[0].Columns.Add("Issuer", typeof(string));
m_dataset.Tables[0].Columns.Add("IsCA", typeof(string));
m_dataset.Tables[0].Columns.Add("HasCrl", typeof(string));
m_dataset.Tables[0].Columns.Add("Status", typeof(Status));
m_dataset.Tables[0].Columns.Add("ValidTo", typeof(string));
m_dataset.Tables[0].Columns.Add("Thumbprint", typeof(string));
m_dataset.Tables[0].Columns.Add("Certificate", typeof(X509Certificate2));
m_dataset.Tables[0].Columns.Add("Icon", typeof(Image));
m_dataset.Tables[0].Columns.Add("Crls", typeof(List<X509CRL>));
CertificateListGridView.DataSource = m_dataset.Tables[0];
}
private DataSet m_dataset;
private FileInfo m_certificateFile;
private string m_trustedStorePath;
private string m_issuerStorePath;
private string m_rejectedStorePath;
private DataTable CertificatesTable { get { return m_dataset.Tables[0]; } }
private enum Status
{
Trusted,
Issuer,
Rejected,
Deleted
}
private ICertificateStore CreateStore(string storePath)
{
ICertificateStore store = CertificateStoreIdentifier.CreateStore(CertificateStoreIdentifier.DetermineStoreType(storePath));
store.Open(storePath);
return store;
}
public async void Initialize(string trustedStorePath, string issuerStorePath, string rejectedStorePath)
{
CertificatesTable.Rows.Clear();
m_trustedStorePath = trustedStorePath;
m_issuerStorePath = issuerStorePath;
m_rejectedStorePath = rejectedStorePath;
if (!String.IsNullOrEmpty(trustedStorePath))
{
using (ICertificateStore store = CreateStore(trustedStorePath))
{
X509CertificateCollection certificates = await store.Enumerate();
foreach (X509Certificate2 certificate in certificates)
{
List<X509CRL> crls = new List<X509CRL>();
if (store.SupportsCRLs)
{
foreach (X509CRL crl in store.EnumerateCRLs(certificate))
{
crls.Add(crl);
}
}
AddCertificate(certificate, Status.Trusted, crls);
}
}
}
string path1 = Utils.GetAbsoluteDirectoryPath(trustedStorePath, true, false, false);
string path2 = Utils.GetAbsoluteDirectoryPath(issuerStorePath, true, false, false);
if (String.Compare(path1, path2, StringComparison.OrdinalIgnoreCase) != 0)
{
if (!String.IsNullOrEmpty(issuerStorePath))
{
using (ICertificateStore store = CreateStore(issuerStorePath))
{
X509Certificate2Collection certificates = await store.Enumerate();
foreach (X509Certificate2 certificate in certificates)
{
List<X509CRL> crls = new List<X509CRL>();
if (store.SupportsCRLs)
{
foreach (X509CRL crl in store.EnumerateCRLs(certificate))
{
crls.Add(crl);
}
}
AddCertificate(certificate, Status.Issuer, crls);
}
}
}
}
if (!String.IsNullOrEmpty(rejectedStorePath))
{
using (ICertificateStore store = CreateStore(rejectedStorePath))
{
X509Certificate2Collection certificates = await store.Enumerate();
foreach (X509Certificate2 certificate in certificates)
{
AddCertificate(certificate, Status.Rejected, null);
}
}
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
public void Initialize(TrustListDataType trustList)
{
CertificatesTable.Rows.Clear();
if (trustList != null)
{
if ((trustList.SpecifiedLists & (uint)TrustListMasks.TrustedCertificates) != 0 && trustList.TrustedCertificates != null)
{
foreach (var certificateBytes in trustList.TrustedCertificates)
{
var certificate = new X509Certificate2(certificateBytes);
List<X509CRL> crls = new List<X509CRL>();
if ((trustList.SpecifiedLists & (uint)TrustListMasks.TrustedCrls) != 0 && trustList.TrustedCrls != null)
{
foreach (var crlBytes in trustList.TrustedCrls)
{
X509CRL crl = new X509CRL(crlBytes);
if (Utils.CompareDistinguishedName(crl.Issuer, certificate.Subject))
{
crls.Add(crl);
}
}
}
AddCertificate(certificate, Status.Trusted, crls);
}
}
if ((trustList.SpecifiedLists & (uint)TrustListMasks.IssuerCertificates) != 0 && trustList.IssuerCertificates != null)
{
foreach (var certificateBytes in trustList.IssuerCertificates)
{
var certificate = new X509Certificate2(certificateBytes);
List<X509CRL> crls = new List<X509CRL>();
if ((trustList.SpecifiedLists & (uint)TrustListMasks.IssuerCrls) != 0 && trustList.IssuerCrls != null)
{
foreach (var crlBytes in trustList.IssuerCrls)
{
X509CRL crl = new X509CRL(crlBytes);
if (Utils.CompareDistinguishedName(crl.Issuer, certificate.Subject))
{
crls.Add(crl);
}
}
}
AddCertificate(certificate, Status.Issuer, crls);
}
}
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
public TrustListDataType GetTrustLists()
{
ByteStringCollection trusted = new ByteStringCollection();
ByteStringCollection trustedCrls = new ByteStringCollection();
ByteStringCollection issuers = new ByteStringCollection();
ByteStringCollection issuersCrls = new ByteStringCollection();
foreach (DataGridViewRow row in CertificateListGridView.Rows)
{
DataRowView source = row.DataBoundItem as DataRowView;
Status status = (Status)source.Row[4];
X509Certificate2 certificate = (X509Certificate2)source.Row[7];
List<X509CRL> crls = (List<X509CRL>)source.Row[9];
if (status == Status.Trusted)
{
trusted.Add(certificate.RawData);
if (crls != null)
{
foreach (var crl in crls)
{
trustedCrls.Add(crl.RawData);
}
}
}
else if (status == Status.Issuer)
{
issuers.Add(certificate.RawData);
if (crls != null)
{
foreach (var crl in crls)
{
issuersCrls.Add(crl.RawData);
}
}
}
}
TrustListDataType trustList = new TrustListDataType()
{
SpecifiedLists = (uint)(TrustListMasks.All),
TrustedCertificates = trusted,
TrustedCrls = trustedCrls,
IssuerCertificates = issuers,
IssuerCrls = issuersCrls
};
return trustList;
}
private void SetIcon(DataRow row, Status status)
{
switch (status)
{
default:
case Status.Rejected: { row[8] = ImageList.Images[ImageIndex.RejectedCertificate]; break; }
case Status.Trusted: { row[8] = ImageList.Images[ImageIndex.TrustedCertificate]; break; }
case Status.Issuer: { row[8] = ImageList.Images[ImageIndex.UntrustedCertificate]; break; }
}
}
private bool IsCA(X509Certificate2 certificate)
{
foreach (X509Extension extension in certificate.Extensions)
{
X509BasicConstraintsExtension basicContraints = extension as X509BasicConstraintsExtension;
if (basicContraints != null)
{
if (basicContraints.CertificateAuthority)
{
return true;
}
break;
}
}
return false;
}
private string GetCommonName(X509Certificate2 certificate)
{
foreach (string element in Utils.ParseDistinguishedName(certificate.Subject))
{
if (element.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
{
return element.Substring(3);
}
}
return "(unknown)";
}
private void AddCertificate(X509Certificate2 certificate, Status status, List<X509CRL> crls)
{
DataRow row = CertificatesTable.NewRow();
row[0] = certificate.Subject;
row[1] = certificate.Issuer;
row[2] = IsCA(certificate);
row[3] = (crls != null && crls.Count > 0).ToString();
row[4] = status;
row[5] = certificate.NotAfter.ToString("yyy-MM-dd");
row[6] = certificate.Thumbprint;
row[7] = certificate;
row[9] = crls;
SetIcon(row, status);
CertificatesTable.Rows.Add(row);
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
private void MoveCertificate(DataRow row, Status status)
{
Status oldStatus = (Status)row[4];
row[4] = status;
SetIcon(row, status);
string targetStorePath = null;
switch (status)
{
case Status.Trusted: { targetStorePath = m_trustedStorePath; break; }
case Status.Issuer: { targetStorePath = m_issuerStorePath; break; }
case Status.Rejected: { targetStorePath = m_rejectedStorePath; break; }
}
string oldStorePath = null;
switch (oldStatus)
{
case Status.Trusted: { oldStorePath = m_trustedStorePath; break; }
case Status.Issuer: { oldStorePath = m_issuerStorePath; break; }
case Status.Rejected: { oldStorePath = m_rejectedStorePath; break; }
}
X509Certificate2 certificate = (X509Certificate2)row[7];
if (oldStorePath != targetStorePath)
{
if (!String.IsNullOrEmpty(targetStorePath))
{
using (ICertificateStore store = CreateStore(targetStorePath))
{
store.Add(certificate);
}
}
if (!String.IsNullOrEmpty(oldStorePath))
{
using (ICertificateStore store = CreateStore(oldStorePath))
{
store.Delete(certificate.Thumbprint);
}
}
}
}
private void ViewMenuItem_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
EditValueDlg dialog = new EditValueDlg();
dialog.Size = new Size(800, 400);
dialog.ShowDialog(null, "", new CertificateWrapper() { Certificate = (X509Certificate2)source.Row[7] }, true, this.Text);
break;
}
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void DeleteMenuItem_Click(object sender, EventArgs e)
{
try
{
List<DataRow> rows = new List<DataRow>();
foreach (DataGridViewRow row in CertificateListGridView.SelectedRows)
{
DataRowView source = row.DataBoundItem as DataRowView;
MoveCertificate(source.Row, Status.Deleted);
rows.Add(source.Row);
}
foreach (var row in rows)
{
row.Delete();
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void TrustMenuItem_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
MoveCertificate(source.Row, Status.Trusted);
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void Reject_MenuItem_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
MoveCertificate(source.Row, Status.Rejected);
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void UntrustMenuItem_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
MoveCertificate(source.Row, Status.Issuer);
}
m_dataset.AcceptChanges();
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void ImportMenuItem_Click(object sender, EventArgs e)
{
try
{
string directory = Environment.CurrentDirectory;
if (m_certificateFile != null)
{
directory = m_certificateFile.DirectoryName;
}
OpenFileDialog dialog = new OpenFileDialog();
dialog.CheckFileExists = true;
dialog.CheckPathExists = true;
dialog.DefaultExt = "*.der";
dialog.Filter = "Certificate Files (*.der)|*.der|All Files (*.*)|*.*";
dialog.Title = "Import Certificate";
dialog.Multiselect = false;
dialog.ValidateNames = true;
dialog.FileName = (m_certificateFile != null) ? m_certificateFile.Name : null;
dialog.InitialDirectory = directory;
dialog.RestoreDirectory = true;
if (dialog.ShowDialog() != DialogResult.OK)
{
return;
}
m_certificateFile = new FileInfo(dialog.FileName);
X509Certificate2 certificate = CertificateFactory.Load(new X509Certificate2(File.ReadAllBytes(dialog.FileName)), false);
if (certificate != null)
{
if (!String.IsNullOrEmpty(m_trustedStorePath))
{
using (ICertificateStore store = CreateStore(m_trustedStorePath))
{
store.Add(certificate);
}
}
}
AddCertificate(certificate, Status.Trusted, null);
NoDataWarningLabel.Visible = CertificatesTable.Rows.Count == 0;
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void ExportMenuItem_Click(object sender, EventArgs e)
{
try
{
if (CertificateListGridView.SelectedCells.Count != 1)
{
return;
}
X509Certificate2 certificate = null;
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
certificate = (X509Certificate2)source.Row[7];
break;
}
string directory = Environment.CurrentDirectory;
if (m_certificateFile != null)
{
directory = m_certificateFile.DirectoryName;
}
SaveFileDialog dialog = new SaveFileDialog();
dialog.CheckFileExists = false;
dialog.CheckPathExists = true;
dialog.DefaultExt = ".der";
dialog.Filter = "Certificate Files (*.der)|*.der|All Files (*.*)|*.*";
dialog.ValidateNames = true;
dialog.Title = "Export Certificate";
dialog.FileName = String.Format("{0} [{1}].der", GetCommonName(certificate), certificate.Thumbprint);
dialog.InitialDirectory = directory;
if (dialog.ShowDialog() != DialogResult.OK)
{
return;
}
m_certificateFile = new FileInfo(dialog.FileName);
File.WriteAllBytes(dialog.FileName, certificate.RawData);
}
catch (Exception exception)
{
MessageBox.Show(Text + ": " + exception.Message);
}
}
private void PopupMenuStrip_Opening(object sender, CancelEventArgs e)
{
if (CertificateListGridView.SelectedCells.Count > 0)
{
Status? status = null;
bool? isCa = null;
foreach (DataGridViewCell cell in CertificateListGridView.SelectedCells)
{
DataRowView source = CertificateListGridView.Rows[cell.RowIndex].DataBoundItem as DataRowView;
if (isCa == null)
{
isCa = IsCA((X509Certificate2)source.Row[7]);
}
else
{
if (!IsCA((X509Certificate2)source.Row[7]))
{
isCa = false;
}
}
if (status == null)
{
status = (Status)source.Row[4];
}
else
{
if (status != (Status)source.Row[4])
{
status = null;
break;
}
}
}
DeleteMenuItem.Visible = true;
TrustMenuItem.Visible = status != null && status.Value != Status.Trusted;
UntrustMenuItem.Visible = status != null && status.Value == Status.Trusted && isCa != null && isCa.Value;
RejectMenuItem.Visible = !String.IsNullOrEmpty(m_rejectedStorePath) && status != null && status.Value != Status.Rejected;
Seperator01MenuItem.Visible = isCa != null && isCa.Value && CertificateListGridView.SelectedCells.Count == 1;
AddCrlMenuItem.Visible = isCa != null && isCa.Value && CertificateListGridView.SelectedCells.Count == 1;
DeleteCrlMenuItem.Visible = isCa != null && isCa.Value && CertificateListGridView.SelectedCells.Count == 1;
ExportMenuItem.Enabled = CertificateListGridView.SelectedCells.Count == 1;
}
else
{
DeleteMenuItem.Visible = false;
TrustMenuItem.Visible = false;
UntrustMenuItem.Visible = false;
RejectMenuItem.Visible = false;
Seperator01MenuItem.Visible = false;
AddCrlMenuItem.Visible = false;
DeleteCrlMenuItem.Visible = false;
ExportMenuItem.Enabled = false;
}
}
private void CertificateListGridView_DoubleClick(object sender, EventArgs e)
{
ViewMenuItem_Click(sender, e);
}
}
}