1 #include "certificatemanagementdialog.hpp"
2 #include "ui_certificatemanagementdialog.h"
3 
4 #include "kristall.hpp"
5 
6 #include "newidentitiydialog.hpp"
7 #include "certificateiodialog.hpp"
8 #include "ioutil.hpp"
9 
10 #include <QCryptographicHash>
11 #include <QMessageBox>
12 
CertificateManagementDialog(QWidget * parent)13 CertificateManagementDialog::CertificateManagementDialog(QWidget *parent) :
14     QDialog(parent),
15     ui(new Ui::CertificateManagementDialog),
16     selected_identity { nullptr }
17 {
18     ui->setupUi(this);
19 
20     connect( // connect with "this" as context, so the connection will die when the window is destroyed
21         kristall::globals().localization.get(), &Localization::translationChanged,
__anon513544430102() 22         this, [this]() { this->ui->retranslateUi(this); },
23         Qt::DirectConnection
24     );
25 
26     this->ui->certificates->setModel(&identity_set);
27     this->ui->certificates->expandAll();
28 
29     connect(
30         this->ui->certificates->selectionModel(),
31         &QItemSelectionModel::currentChanged,
32         this,
33         &CertificateManagementDialog::on_certificates_selected
34     );
35     on_certificates_selected(QModelIndex { }, QModelIndex { });
36 }
37 
~CertificateManagementDialog()38 CertificateManagementDialog::~CertificateManagementDialog()
39 {
40     delete ui;
41 }
42 
identitySet() const43 IdentityCollection CertificateManagementDialog::identitySet() const
44 {
45     return this->identity_set;
46 }
47 
setIdentitySet(const IdentityCollection & src)48 void CertificateManagementDialog::setIdentitySet(const IdentityCollection &src)
49 {
50     this->identity_set = src;
51     this->ui->certificates->expandAll();
52 
53 }
54 
on_certificates_selected(QModelIndex const & index,QModelIndex const & previous)55 void CertificateManagementDialog::on_certificates_selected(QModelIndex const& index, QModelIndex const & previous)
56 {
57     Q_UNUSED(previous);
58 
59     selected_identity = identity_set.getMutableIdentity(index);
60 
61     this->ui->export_cert_button->setEnabled(selected_identity != nullptr);
62 
63     if(selected_identity != nullptr)
64     {
65         auto & cert = *selected_identity;
66         this->ui->groupBox->setEnabled(true);
67         this->ui->cert_display_name->setText(cert.display_name);
68         this->ui->cert_common_name->setText(cert.certificate.subjectInfo(QSslCertificate::CommonName).join(", "));
69         this->ui->cert_expiration_date->setDateTime(cert.certificate.expiryDate());
70         auto days = QDateTime::currentDateTime().daysTo(cert.certificate.expiryDate());
71         this->ui->cert_livetime->setText(tr("%1 day","%1 days", days).arg(days));
72         this->ui->cert_fingerprint->setPlainText(toFingerprintString(cert.certificate));
73         this->ui->cert_notes->setPlainText(cert.user_notes);
74 
75         this->ui->cert_host_filter->setText(cert.host_filter);
76         this->ui->cert_auto_enable->setEnabled(not cert.host_filter.isEmpty());
77         this->ui->cert_auto_enable->setChecked(cert.auto_enable);
78 
79         this->ui->delete_cert_button->setEnabled(true);
80     }
81     else
82     {
83         this->ui->groupBox->setEnabled(false);
84         this->ui->cert_display_name->setText("");
85         this->ui->cert_common_name->setText("");
86         this->ui->cert_expiration_date->setDateTime(QDateTime { });
87         this->ui->cert_livetime->setText("");
88         this->ui->cert_fingerprint->setPlainText("");
89         this->ui->cert_host_filter->setText("");
90         this->ui->cert_auto_enable->setChecked(false);
91 
92         if(auto group_name = identity_set.group(index); not group_name.isEmpty()) {
93             this->ui->delete_cert_button->setEnabled(identity_set.canDeleteGroup(group_name));
94         } else {
95             this->ui->delete_cert_button->setEnabled(false);
96         }
97     }
98 }
99 
on_cert_notes_textChanged()100 void CertificateManagementDialog::on_cert_notes_textChanged()
101 {
102     if(this->selected_identity != nullptr) {
103         this->selected_identity->user_notes = this->ui->cert_notes->toPlainText();
104     }
105 }
106 
on_cert_display_name_textChanged(const QString & arg1)107 void CertificateManagementDialog::on_cert_display_name_textChanged(const QString &arg1)
108 {
109     Q_UNUSED(arg1)
110     if(this->selected_identity != nullptr) {
111         this->selected_identity->display_name = this->ui->cert_display_name->text();
112     }
113 }
114 
on_delete_cert_button_clicked()115 void CertificateManagementDialog::on_delete_cert_button_clicked()
116 {
117     auto index = this->ui->certificates->currentIndex();
118 
119     if(identity_set.getMutableIdentity(index) != nullptr)
120     {
121         auto answer = QMessageBox::question(
122             this,
123             tr("Kristall"),
124             tr("Do you really want to delete this certificate?\r\n\r\nYou will not be able to restore the identity after this!"),
125             QMessageBox::Yes | QMessageBox::No,
126             QMessageBox::No
127         );
128         if(answer != QMessageBox::Yes)
129             return;
130         if(not identity_set.destroyIdentity(index)) {
131             QMessageBox::warning(this, tr("Kristall"), tr("Could not destroy identity!"));
132         }
133     }
134     else if(auto group_name = identity_set.group(index); not group_name.isEmpty()) {
135 
136         auto answer = QMessageBox::question(
137             this,
138             tr("Kristall"),
139             tr("Do you want to delete the group '%1'").arg(group_name)
140         );
141         if(answer != QMessageBox::Yes)
142             return;
143 
144         if(not identity_set.deleteGroup(group_name)) {
145             QMessageBox::warning(this, tr("Kristall"), tr("Could not delete group!"));
146         }
147     }
148 }
149 
on_export_cert_button_clicked()150 void CertificateManagementDialog::on_export_cert_button_clicked()
151 {
152     if(this->selected_identity == nullptr)
153         return;
154     CertificateIoDialog dialog { this };
155 
156     dialog.setKeyAlgorithm(this->selected_identity->private_key.algorithm());
157     dialog.setIoMode(CertificateIoDialog::Export);
158 
159     if(dialog.exec() != QDialog::Accepted)
160         return;
161 
162     {
163         QFile cert_file { dialog.certificateFileName() };
164         if(not cert_file.open(QFile::WriteOnly)) {
165             QMessageBox::warning(
166                 this,
167                 tr("Kristall"),
168                 tr("The file %1 could not be found!").arg(dialog.certificateFileName())
169             );
170             return;
171         }
172 
173         QByteArray cert_blob;
174         if(dialog.certificateFileName().endsWith(".der")) {
175             cert_blob = this->selected_identity->certificate.toDer();
176         } else {
177             cert_blob = this->selected_identity->certificate.toPem();
178         }
179 
180         if(not IoUtil::writeAll(cert_file, cert_blob)) {
181             QMessageBox::warning(
182                 this,
183                 tr("Kristall"),
184                 tr("The file %1 could not be created found!").arg(dialog.certificateFileName())
185             );
186             return;
187         }
188     }
189 
190     {
191         QFile key_file { dialog.keyFileName() };
192         if(not key_file.open(QFile::WriteOnly)) {
193             QMessageBox::warning(
194                 this,
195                 tr("Kristall"),
196                 tr("The file %1 could not be found!").arg(dialog.keyFileName())
197             );
198             return;
199         }
200 
201         QByteArray key_blob;
202         if(dialog.keyFileName().endsWith(".der")) {
203             key_blob = this->selected_identity->private_key.toDer();
204         } else {
205             key_blob = this->selected_identity->private_key.toPem();
206         }
207 
208         if(not IoUtil::writeAll(key_file, key_blob)) {
209             QMessageBox::warning(
210                 this,
211                 tr("Kristall"),
212                 tr("The file %1 could not be created!").arg(dialog.keyFileName())
213             );
214             return;
215         }
216     }
217 }
218 
on_import_cert_button_clicked()219 void CertificateManagementDialog::on_import_cert_button_clicked()
220 {
221     CertificateIoDialog dialog { this };
222 
223     dialog.setIoMode(CertificateIoDialog::Import);
224 
225     if(dialog.exec() != QDialog::Accepted)
226         return;
227 
228     QFile cert_file { dialog.certificateFileName() };
229     if(not cert_file.open(QFile::ReadOnly)) {
230         QMessageBox::warning(
231             this,
232             tr("Kristall"),
233             tr("The file %1 could not be found!").arg(dialog.certificateFileName())
234         );
235         return;
236     }
237 
238     QFile key_file { dialog.keyFileName() };
239     if(not key_file.open(QFile::ReadOnly)) {
240         QMessageBox::warning(
241             this,
242             tr("Kristall"),
243             tr("The file %1 could not be found!").arg(dialog.keyFileName())
244         );
245         return;
246     }
247 
248     CryptoIdentity ident;
249     ident.private_key = QSslKey {
250         &key_file,
251         dialog.keyAlgorithm(),
252         dialog.keyFileName().endsWith(".der") ? QSsl::Der : QSsl::Pem,
253         QSsl::PrivateKey
254     };
255     ident.certificate = QSslCertificate {
256         &cert_file,
257         dialog.keyFileName().endsWith(".der") ? QSsl::Der : QSsl::Pem,
258     };
259     ident.user_notes = tr("Imported from:\r\nkey: %1\r\n:cert: %2").arg(dialog.keyFileName(), dialog.certificateFileName());
260     //: Default name
261     ident.display_name = tr("Imported Certificate");
262     ident.auto_enable = false;
263     ident.host_filter = "";
264     ident.is_persistent = true;
265 
266     if(ident.private_key.isNull()) {
267         QMessageBox::warning(
268             this,
269             tr("Kristall"),
270             tr("The key file %1 could not be loaded. Please verify your key file.").arg(dialog.keyFileName())
271         );
272         return;
273     }
274 
275     if(ident.certificate.isNull()) {
276         QMessageBox::warning(
277             this,
278             tr("Kristall"),
279             tr("The certificate file %1 could not be loaded. Please verify your certificate.").arg(dialog.keyFileName())
280         );
281         return;
282     }
283 
284     if(not identity_set.addCertificate(tr("Imported Certificates"), ident)) {
285         QMessageBox::warning(
286             this,
287             tr("Kristall"),
288             tr("Failed to import the certificate.")
289         );
290     }
291 }
292 
on_create_cert_button_clicked()293 void CertificateManagementDialog::on_create_cert_button_clicked()
294 {
295     NewIdentitiyDialog dialog { this };
296 
297     dialog.setGroupName(identity_set.group(this->ui->certificates->currentIndex()));
298 
299     if(dialog.exec() != QDialog::Accepted)
300         return;
301 
302     auto id = dialog.createIdentity();
303     if(not id.isValid())
304         return;
305     id.is_persistent = true;
306 
307     identity_set.addCertificate(
308         dialog.groupName(),
309         id);
310 }
311 
on_cert_host_filter_textChanged(const QString & host_filter)312 void CertificateManagementDialog::on_cert_host_filter_textChanged(const QString &host_filter)
313 {
314     if(this->selected_identity != nullptr) {
315         this->ui->cert_auto_enable->setEnabled(not host_filter.isEmpty());
316         this->selected_identity->host_filter = host_filter;
317     } else {
318         this->ui->cert_auto_enable->setEnabled(false);
319     }
320 
321 }
322 
on_cert_auto_enable_clicked(bool checked)323 void CertificateManagementDialog::on_cert_auto_enable_clicked(bool checked)
324 {
325     if(this->selected_identity != nullptr) {
326         this->selected_identity->auto_enable = checked;
327     }
328 }
329