mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-03-11 08:54:48 +00:00
Merge fb0b003aaa into 36a26308d0
This commit is contained in:
commit
60d50cac47
10 changed files with 793 additions and 227 deletions
|
|
@ -177,4 +177,10 @@ If you chose to not autoload the key on database unlock, you can manually make t
|
|||
|
||||
.SSH Agent Load Key from Context Menu
|
||||
image::sshagent_context_menu.png[]
|
||||
|
||||
==== Associate certificate to SSH key
|
||||
If you have an externally generated OpenSSH certificate file associated with your SSH key, you can configure it in the "Certificate" tab.
|
||||
|
||||
When the key is loaded, if "Use certificate" is checked, both the key and certificate are added to the agent.
|
||||
|
||||
// end::content[]
|
||||
|
|
|
|||
|
|
@ -3095,6 +3095,10 @@ Would you like to correct it?</source>
|
|||
<source>Failed to decrypt SSH key, ensure password is correct.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidgetAdvanced</name>
|
||||
|
|
@ -3528,6 +3532,14 @@ Would you like to correct it?</source>
|
|||
<source>Clear agent</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditGroupWidget</name>
|
||||
|
|
@ -5347,6 +5359,22 @@ Line %2, column %3</source>
|
|||
<source>Failed to open private key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Certificate is an attachment but no attachments provided.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Certificate is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File too large to be a certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>KeePass1Reader</name>
|
||||
|
|
@ -6844,6 +6872,18 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
|
|||
<source>Failed to read public key: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid or unsupported certificate file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't write certificate as it is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unexpected EOF when writing certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OpenSSHKeyGenDialog</name>
|
||||
|
|
@ -10043,6 +10083,14 @@ This option is deprecated, use --set-key-file instead.</source>
|
|||
<source>All SSH identities removed from agent.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agent refused this identity certificate. Possible reasons include:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid or empty certificate.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SearchHelpWidget</name>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "core/PasswordGenerator.h"
|
||||
#include "core/TimeDelta.h"
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
#include <QSignalBlocker>
|
||||
#include "sshagent/OpenSSHKey.h"
|
||||
#include "sshagent/OpenSSHKeyGenDialog.h"
|
||||
#include "sshagent/SSHAgent.h"
|
||||
|
|
@ -544,6 +545,12 @@ void EditEntryWidget::setupEntryUpdate()
|
|||
connect(m_sshAgentUi->requireUserConfirmationCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->lifetimeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->lifetimeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->attachmentCertificateRadioButton, SIGNAL(toggled(bool)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->externalCertificateFileRadioButton, SIGNAL(toggled(bool)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->attachmentCertificateComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->attachmentCertificateComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->externalCertificateFileEdit, SIGNAL(textChanged(QString)), this, SLOT(setModified()));
|
||||
connect(m_sshAgentUi->addCertificateToAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -621,6 +628,15 @@ void EditEntryWidget::setupSSHAgent()
|
|||
connect(m_sshAgentUi->decryptButton, &QPushButton::clicked, this, &EditEntryWidget::decryptPrivateKey);
|
||||
connect(m_sshAgentUi->copyToClipboardButton, &QPushButton::clicked, this, &EditEntryWidget::copyPublicKey);
|
||||
connect(m_sshAgentUi->generateButton, &QPushButton::clicked, this, &EditEntryWidget::generatePrivateKey);
|
||||
connect(m_sshAgentUi->attachmentCertificateRadioButton, &QRadioButton::clicked,
|
||||
this, &EditEntryWidget::updateSSHAgentKeyInfo);
|
||||
connect(m_sshAgentUi->attachmentCertificateComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &EditEntryWidget::updateSSHAgentAttachmentCertificate);
|
||||
connect(m_sshAgentUi->externalCertificateFileRadioButton, &QRadioButton::clicked,
|
||||
this, &EditEntryWidget::updateSSHAgentKeyInfo);
|
||||
connect(m_sshAgentUi->externalCertificateFileEdit, &QLineEdit::textChanged,
|
||||
this, &EditEntryWidget::updateSSHAgentKeyInfo);
|
||||
connect(m_sshAgentUi->browseCertificateButton, &QPushButton::clicked, this, &EditEntryWidget::browseCertificate);
|
||||
|
||||
connect(m_attachments.data(), &EntryAttachments::modified,
|
||||
this, &EditEntryWidget::updateSSHAgentAttachments);
|
||||
|
|
@ -636,10 +652,12 @@ void EditEntryWidget::setSSHAgentSettings()
|
|||
m_sshAgentUi->requireUserConfirmationCheckBox->setChecked(m_sshAgentSettings.useConfirmConstraintWhenAdding());
|
||||
m_sshAgentUi->lifetimeCheckBox->setChecked(m_sshAgentSettings.useLifetimeConstraintWhenAdding());
|
||||
m_sshAgentUi->lifetimeSpinBox->setValue(m_sshAgentSettings.lifetimeConstraintDuration());
|
||||
m_sshAgentUi->attachmentComboBox->clear();
|
||||
QSignalBlocker sshAgent_attachmentComboBox_Blocker(m_sshAgentUi->attachmentComboBox);
|
||||
m_sshAgentUi->addToAgentButton->setEnabled(false);
|
||||
m_sshAgentUi->removeFromAgentButton->setEnabled(false);
|
||||
m_sshAgentUi->copyToClipboardButton->setEnabled(false);
|
||||
m_sshAgentUi->addCertificateToAgentCheckBox->setChecked(m_sshAgentSettings.useCertificate());
|
||||
QSignalBlocker sshAgent_attachmentCertificateComboBox_Blocker(m_sshAgentUi->attachmentCertificateComboBox);
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgent()
|
||||
|
|
@ -672,18 +690,25 @@ void EditEntryWidget::updateSSHAgentAttachments()
|
|||
setSSHAgentSettings();
|
||||
}
|
||||
|
||||
QSignalBlocker sshAgent_attachmentComboBox_Blocker(m_sshAgentUi->attachmentComboBox);
|
||||
m_sshAgentUi->attachmentComboBox->clear();
|
||||
m_sshAgentUi->attachmentComboBox->addItem("");
|
||||
|
||||
QSignalBlocker sshAgent_attachmentCertificateComboBox_Blocker(m_sshAgentUi->attachmentCertificateComboBox);
|
||||
m_sshAgentUi->attachmentCertificateComboBox->clear();
|
||||
m_sshAgentUi->attachmentCertificateComboBox->addItem("");
|
||||
|
||||
for (const QString& fileName : m_attachments->keys()) {
|
||||
if (fileName == "KeeAgent.settings") {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_sshAgentUi->attachmentComboBox->addItem(fileName);
|
||||
m_sshAgentUi->attachmentCertificateComboBox->addItem(fileName);
|
||||
}
|
||||
|
||||
m_sshAgentUi->attachmentComboBox->setCurrentText(m_sshAgentSettings.attachmentName());
|
||||
QSignalBlocker sshAgent_externalFileEdit_Blocker(m_sshAgentUi->externalFileEdit);
|
||||
m_sshAgentUi->externalFileEdit->setText(m_sshAgentSettings.fileName());
|
||||
|
||||
if (m_sshAgentSettings.selectedType() == "attachment") {
|
||||
|
|
@ -692,6 +717,16 @@ void EditEntryWidget::updateSSHAgentAttachments()
|
|||
m_sshAgentUi->externalFileRadioButton->setChecked(true);
|
||||
}
|
||||
|
||||
m_sshAgentUi->attachmentCertificateComboBox->setCurrentText(m_sshAgentSettings.attachmentNameCertificate());
|
||||
QSignalBlocker sshAgent_externalCertificateFileEdit_Blocker(m_sshAgentUi->externalCertificateFileEdit);
|
||||
m_sshAgentUi->externalCertificateFileEdit->setText(m_sshAgentSettings.fileNameCertificate());
|
||||
|
||||
if (m_sshAgentSettings.selectedCertificateType() == "attachment") {
|
||||
m_sshAgentUi->attachmentCertificateRadioButton->setChecked(true);
|
||||
} else {
|
||||
m_sshAgentUi->externalCertificateFileRadioButton->setChecked(true);
|
||||
}
|
||||
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
|
||||
|
|
@ -758,6 +793,14 @@ void EditEntryWidget::toKeeAgentSettings(KeeAgentSettings& settings) const
|
|||
|
||||
// we don't use this either but we don't want it to dirty flag the config
|
||||
settings.setSaveAttachmentToTempFile(m_sshAgentSettings.saveAttachmentToTempFile());
|
||||
|
||||
settings.setUseCertificate(m_sshAgentUi->addCertificateToAgentCheckBox->isChecked());
|
||||
settings.setSelectedCertificateType(m_sshAgentUi->attachmentCertificateRadioButton->isChecked() ? "attachment" : "file");
|
||||
settings.setAttachmentCertificateName(m_sshAgentUi->attachmentCertificateComboBox->currentText());
|
||||
settings.setFileNameCertificate(m_sshAgentUi->externalCertificateFileEdit->text());
|
||||
|
||||
// we don't use this either but we don't want it to dirty flag the config
|
||||
settings.setSaveAttachmentCertificateToTempFile(m_sshAgentSettings.saveAttachmentCertificateToTempFile());
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateTotp()
|
||||
|
|
@ -820,6 +863,23 @@ void EditEntryWidget::addKeyToAgent()
|
|||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgentAttachmentCertificate()
|
||||
{
|
||||
m_sshAgentUi->attachmentCertificateRadioButton->setChecked(true);
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
|
||||
void EditEntryWidget::browseCertificate()
|
||||
{
|
||||
auto fileName = fileDialog()->getOpenFileName(this, tr("Select certificate"), FileDialog::getLastDir("sshagent"));
|
||||
if (!fileName.isEmpty()) {
|
||||
FileDialog::saveLastDir("sshagent", fileName);
|
||||
m_sshAgentUi->externalCertificateFileEdit->setText(fileName);
|
||||
m_sshAgentUi->externalCertificateFileRadioButton->setChecked(true);
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::removeKeyFromAgent()
|
||||
{
|
||||
OpenSSHKey key;
|
||||
|
|
@ -966,6 +1026,9 @@ void EditEntryWidget::loadEntry(Entry* entry,
|
|||
|
||||
void EditEntryWidget::setForms(Entry* entry, bool restore)
|
||||
{
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
QSignalBlocker attachmentsBlocker(m_attachments.data());
|
||||
#endif
|
||||
m_attachments->copyDataFrom(entry->attachments());
|
||||
m_customData->copyDataFrom(entry->customData());
|
||||
|
||||
|
|
@ -1244,6 +1307,7 @@ bool EditEntryWidget::commitEntry()
|
|||
void EditEntryWidget::acceptEntry()
|
||||
{
|
||||
if (commitEntry()) {
|
||||
m_sshAgentUi->privateKeyTabWidget->setCurrentIndex(0);
|
||||
clear();
|
||||
emit editFinished(true);
|
||||
}
|
||||
|
|
@ -1363,6 +1427,7 @@ void EditEntryWidget::cancel()
|
|||
}
|
||||
}
|
||||
|
||||
m_sshAgentUi->privateKeyTabWidget->setCurrentIndex(0);
|
||||
clear();
|
||||
emit editFinished(accepted);
|
||||
}
|
||||
|
|
@ -1382,6 +1447,9 @@ void EditEntryWidget::clear()
|
|||
m_mainUi->notesEdit->clear();
|
||||
|
||||
m_entryAttributes->clear();
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
QSignalBlocker attachmentsBlocker(m_attachments.data());
|
||||
#endif
|
||||
m_attachments->clear();
|
||||
m_customData->clear();
|
||||
m_autoTypeAssoc->clear();
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ private slots:
|
|||
void decryptPrivateKey();
|
||||
void copyPublicKey();
|
||||
void generatePrivateKey();
|
||||
void updateSSHAgentAttachmentCertificate();
|
||||
void browseCertificate();
|
||||
#endif
|
||||
#ifdef WITH_XC_BROWSER
|
||||
void updateBrowserModified();
|
||||
|
|
|
|||
|
|
@ -26,79 +26,35 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="1" colspan="4">
|
||||
<widget class="QCheckBox" name="removeKeyFromAgentCheckBox">
|
||||
<item row="6" column="3">
|
||||
<layout class="QHBoxLayout" name="agentActionsLayout" stretch="0,0,0">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addToAgentButton">
|
||||
<property name="text">
|
||||
<string>Add to agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeFromAgentButton">
|
||||
<property name="text">
|
||||
<string>Remove from agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearAgentButton">
|
||||
<property name="text">
|
||||
<string>Clear agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="requireUserConfirmationCheckBox">
|
||||
<property name="text">
|
||||
<string>Remove key from agent when database is closed/locked</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QLabel" name="commentLabel">
|
||||
<property name="text">
|
||||
<string>Comment</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="4">
|
||||
<widget class="QCheckBox" name="addKeyToAgentCheckBox">
|
||||
<property name="text">
|
||||
<string>Add key to agent when database is opened/unlocked</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="3" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="publicKeyEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="4">
|
||||
<widget class="QPushButton" name="decryptButton">
|
||||
<property name="text">
|
||||
<string>Decrypt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QLabel" name="fingerprintLabel">
|
||||
<property name="text">
|
||||
<string>Fingerprint</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="copyToClipboardButton">
|
||||
<property name="text">
|
||||
<string>Copy to clipboard</string>
|
||||
<string>Require user confirmation when this key is used</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -112,108 +68,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="4">
|
||||
<widget class="QGroupBox" name="privateKeyGroupBox">
|
||||
<property name="title">
|
||||
<string>Private key</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="attachmentRadioButton">
|
||||
<property name="text">
|
||||
<string>Attachment</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLineEdit" name="externalFileEdit">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>External key file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<layout class="QHBoxLayout" name="agentActionsLayout" stretch="0,0,0">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addToAgentButton">
|
||||
<property name="text">
|
||||
<string>Add to agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeFromAgentButton">
|
||||
<property name="text">
|
||||
<string>Remove from agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearAgentButton">
|
||||
<property name="text">
|
||||
<string>Clear agent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QRadioButton" name="externalFileRadioButton">
|
||||
<property name="text">
|
||||
<string>External file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QPushButton" name="browseButton">
|
||||
<property name="accessibleName">
|
||||
<string>Browser for key file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string extracomment="Button for opening file dialog">Browse…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="generateButton">
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QComboBox" name="attachmentComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Select attachment file</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="4">
|
||||
<widget class="QCheckBox" name="requireUserConfirmationCheckBox">
|
||||
<property name="text">
|
||||
<string>Require user confirmation when this key is used</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="3">
|
||||
<item row="14" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="commentTextLabel">
|
||||
|
|
@ -245,7 +100,291 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="4">
|
||||
<item row="14" column="1">
|
||||
<widget class="QLabel" name="commentLabel">
|
||||
<property name="text">
|
||||
<string>Comment</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QLabel" name="fingerprintLabel">
|
||||
<property name="text">
|
||||
<string>Fingerprint</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="2" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="publicKeyEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="3">
|
||||
<widget class="QPushButton" name="decryptButton">
|
||||
<property name="text">
|
||||
<string>Decrypt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="13" column="2" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="fingerprintTextLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>n/a</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="addKeyToAgentCheckBox">
|
||||
<property name="text">
|
||||
<string>Add key to agent when database is opened/unlocked</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="removeKeyFromAgentCheckBox">
|
||||
<property name="text">
|
||||
<string>Remove key from agent when database is closed/locked</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="3">
|
||||
<widget class="QTabWidget" name="privateKeyTabWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="privateKeyTabWidgetPage1">
|
||||
<attribute name="title">
|
||||
<string>Private key</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="addCertificateToAgentCheckBox">
|
||||
<property name="text">
|
||||
<string>Use certificate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QRadioButton" name="externalFileRadioButton">
|
||||
<property name="text">
|
||||
<string>External file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QPushButton" name="browseButton">
|
||||
<property name="accessibleName">
|
||||
<string>Browser for key file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string extracomment="Button for opening file dialog">Browse…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QComboBox" name="attachmentComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Select attachment file</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="attachmentRadioButton">
|
||||
<property name="text">
|
||||
<string>Attachment</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLineEdit" name="externalFileEdit">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>External key file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="generateButton">
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="privateKeyTabWidgetPage2">
|
||||
<attribute name="title">
|
||||
<string>Certificate</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="attachmentCertificateRadioButton">
|
||||
<property name="text">
|
||||
<string>Attachment</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="attachmentCertificateComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Select attachment file</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="externalCertificateFileEdit">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>External key file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="externalCertificateFileRadioButton">
|
||||
<property name="text">
|
||||
<string>External file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="browseCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Browse for certificate file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string extracomment="Button for opening file dialog">Browse…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="copyToClipboardButton">
|
||||
<property name="text">
|
||||
<string>Copy to clipboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="3">
|
||||
<layout class="QHBoxLayout" name="removeKeyLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="lifetimeCheckBox">
|
||||
|
|
@ -282,54 +421,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="13" column="3" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="fingerprintTextLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>n/a</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="12" column="3">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,12 @@ bool KeeAgentSettings::operator==(const KeeAgentSettings& other) const
|
|||
&& m_selectedType == other.m_selectedType
|
||||
&& m_attachmentName == other.m_attachmentName
|
||||
&& m_saveAttachmentToTempFile == other.m_saveAttachmentToTempFile
|
||||
&& m_fileName == other.m_fileName);
|
||||
&& m_fileName == other.m_fileName
|
||||
&& m_selectedCertificateType == other.m_selectedCertificateType
|
||||
&& m_attachmentNameCertificate == other.m_attachmentNameCertificate
|
||||
&& m_saveAttachmentCertificateToTempFile == other.m_saveAttachmentCertificateToTempFile
|
||||
&& m_fileNameCertificate == other.m_fileNameCertificate
|
||||
&& m_useCertificate == other.m_useCertificate);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +88,11 @@ void KeeAgentSettings::reset()
|
|||
m_saveAttachmentToTempFile = false;
|
||||
m_fileName.clear();
|
||||
m_error.clear();
|
||||
m_selectedCertificateType = QStringLiteral("file");
|
||||
m_attachmentNameCertificate.clear();
|
||||
m_saveAttachmentCertificateToTempFile = false;
|
||||
m_fileNameCertificate.clear();
|
||||
m_useCertificate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -200,6 +210,61 @@ void KeeAgentSettings::setFileName(const QString& fileName)
|
|||
m_fileName = fileName;
|
||||
}
|
||||
|
||||
const QString KeeAgentSettings::fileNameCertificateEnvSubst(QProcessEnvironment environment) const
|
||||
{
|
||||
return Tools::envSubstitute(m_fileNameCertificate, environment);
|
||||
}
|
||||
|
||||
bool KeeAgentSettings::useCertificate() const
|
||||
{
|
||||
return m_useCertificate;
|
||||
}
|
||||
|
||||
void KeeAgentSettings::setUseCertificate(bool useCertificate)
|
||||
{
|
||||
m_useCertificate = useCertificate;
|
||||
}
|
||||
|
||||
const QString KeeAgentSettings::selectedCertificateType() const
|
||||
{
|
||||
return m_selectedCertificateType;
|
||||
}
|
||||
|
||||
const QString KeeAgentSettings::attachmentNameCertificate() const
|
||||
{
|
||||
return m_attachmentNameCertificate;
|
||||
}
|
||||
|
||||
bool KeeAgentSettings::saveAttachmentCertificateToTempFile() const
|
||||
{
|
||||
return m_saveAttachmentCertificateToTempFile;
|
||||
}
|
||||
|
||||
const QString KeeAgentSettings::fileNameCertificate() const
|
||||
{
|
||||
return m_fileNameCertificate;
|
||||
}
|
||||
|
||||
void KeeAgentSettings::setSelectedCertificateType(const QString& selectedCertificateType)
|
||||
{
|
||||
m_selectedCertificateType = selectedCertificateType;
|
||||
}
|
||||
|
||||
void KeeAgentSettings::setAttachmentCertificateName(const QString& attachmentCertificateName)
|
||||
{
|
||||
m_attachmentNameCertificate = attachmentCertificateName;
|
||||
}
|
||||
|
||||
void KeeAgentSettings::setSaveAttachmentCertificateToTempFile(bool saveAttachmentCertificateToTempFile)
|
||||
{
|
||||
m_saveAttachmentCertificateToTempFile = saveAttachmentCertificateToTempFile;
|
||||
}
|
||||
|
||||
void KeeAgentSettings::setFileNameCertificate(const QString& fileNameCertificate)
|
||||
{
|
||||
m_fileNameCertificate = fileNameCertificate;
|
||||
}
|
||||
|
||||
bool KeeAgentSettings::readBool(QXmlStreamReader& reader)
|
||||
{
|
||||
reader.readNext();
|
||||
|
|
@ -273,6 +338,29 @@ bool KeeAgentSettings::fromXml(const QByteArray& ba)
|
|||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == "UseCertificate") {
|
||||
m_useCertificate = readBool(reader);
|
||||
} else if (reader.name() == "LocationCertificate") {
|
||||
while (!reader.error() && reader.readNextStartElement()) {
|
||||
if (reader.name() == "SelectedCertificateType") {
|
||||
reader.readNext();
|
||||
m_selectedCertificateType = reader.text().toString();
|
||||
reader.readNext();
|
||||
} else if (reader.name() == "AttachmentCertificateName") {
|
||||
reader.readNext();
|
||||
m_attachmentNameCertificate = reader.text().toString();
|
||||
reader.readNext();
|
||||
} else if (reader.name() == "SaveAttachmentCertificateToTempFile") {
|
||||
m_saveAttachmentCertificateToTempFile = readBool(reader);
|
||||
} else if (reader.name() == "FileNameCertificate") {
|
||||
reader.readNext();
|
||||
m_fileNameCertificate = reader.text().toString();
|
||||
reader.readNext();
|
||||
} else {
|
||||
qWarning() << "Skipping location certificate element" << reader.name();
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Skipping element" << reader.name();
|
||||
reader.skipCurrentElement();
|
||||
|
|
@ -328,6 +416,27 @@ QByteArray KeeAgentSettings::toXml() const
|
|||
}
|
||||
|
||||
writer.writeEndElement(); // Location
|
||||
|
||||
writer.writeTextElement("UseCertificate", m_useCertificate ? TRUE_STR : FALSE_STR);
|
||||
writer.writeStartElement("LocationCertificate");
|
||||
|
||||
writer.writeTextElement("SelectedCertificateType", m_selectedCertificateType);
|
||||
|
||||
if (!m_attachmentNameCertificate.isEmpty()) {
|
||||
writer.writeTextElement("AttachmentCertificateName", m_attachmentNameCertificate);
|
||||
} else {
|
||||
writer.writeEmptyElement("AttachmentCertificateName");
|
||||
}
|
||||
|
||||
writer.writeTextElement("SaveAttachmentCertificateToTempFile", m_saveAttachmentCertificateToTempFile ? TRUE_STR : FALSE_STR);
|
||||
|
||||
if (!m_fileNameCertificate.isEmpty()) {
|
||||
writer.writeTextElement("FileNameCertificate", m_fileNameCertificate);
|
||||
} else {
|
||||
writer.writeEmptyElement("FileNameCertificate");
|
||||
}
|
||||
|
||||
writer.writeEndElement(); // LocationCertificate
|
||||
writer.writeEndElement(); // EntrySettings
|
||||
writer.writeEndDocument();
|
||||
|
||||
|
|
@ -459,7 +568,7 @@ bool KeeAgentSettings::toOpenSSHKey(const QString& username,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (localFile.size() > 1024 * 1024) {
|
||||
if (localFile.size() > SSH_MAX_LOCAL_KEY_SIZE) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings", "File too large to be a private key");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -493,5 +602,61 @@ bool KeeAgentSettings::toOpenSSHKey(const QString& username,
|
|||
key.setComment(QString("%1@%2").arg(username, fileName));
|
||||
}
|
||||
|
||||
if (m_useCertificate) {
|
||||
QString fileCertificateName;
|
||||
QByteArray certificateData;
|
||||
|
||||
if (m_selectedCertificateType == "attachment") {
|
||||
if (!attachments) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings",
|
||||
"Certificate is an attachment but no attachments provided.");
|
||||
return false;
|
||||
}
|
||||
|
||||
fileCertificateName = m_attachmentNameCertificate;
|
||||
certificateData = attachments->value(fileCertificateName);
|
||||
} else {
|
||||
QString fileNameCertificateSubst = fileNameCertificateEnvSubst();
|
||||
QFileInfo localFileCertificateInfo(fileNameCertificateSubst);
|
||||
|
||||
// resolve relative certificate path from database location
|
||||
if (localFileCertificateInfo.isRelative()) {
|
||||
QFileInfo databaseFileCertificateInfo(databasePath);
|
||||
localFileCertificateInfo = QFileInfo(databaseFileCertificateInfo.absolutePath() + QDir::separator() + fileNameCertificateSubst);
|
||||
}
|
||||
|
||||
fileCertificateName = localFileCertificateInfo.fileName();
|
||||
|
||||
QFile localCertificateFile(localFileCertificateInfo.absoluteFilePath());
|
||||
|
||||
if (localCertificateFile.fileName().isEmpty()) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings", "Certificate is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (localCertificateFile.size() > SSH_MAX_LOCAL_KEY_SIZE) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings", "File too large to be a certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!localCertificateFile.open(QIODevice::ReadOnly)) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings", "Failed to open certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
certificateData = localCertificateFile.readAll();
|
||||
}
|
||||
|
||||
if (certificateData.isEmpty()) {
|
||||
m_error = QCoreApplication::translate("KeeAgentSettings", "Certificate is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!key.parseCertificate(certificateData)) {
|
||||
m_error = key.errorString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#define SSH_MAX_LOCAL_KEY_SIZE (1024 * 1024)
|
||||
|
||||
class Entry;
|
||||
class EntryAttachments;
|
||||
class OpenSSHKey;
|
||||
|
|
@ -77,6 +79,19 @@ public:
|
|||
void setSaveAttachmentToTempFile(bool);
|
||||
void setFileName(const QString& fileName);
|
||||
|
||||
// Certificate
|
||||
const QString fileNameCertificateEnvSubst(QProcessEnvironment environment = QProcessEnvironment::systemEnvironment()) const;
|
||||
bool useCertificate() const;
|
||||
void setUseCertificate(bool UseCertificate);
|
||||
const QString selectedCertificateType() const;
|
||||
const QString attachmentNameCertificate() const;
|
||||
bool saveAttachmentCertificateToTempFile() const;
|
||||
const QString fileNameCertificate() const;
|
||||
void setSelectedCertificateType(const QString& certificateType);
|
||||
void setAttachmentCertificateName(const QString& attachmentCertificateName);
|
||||
void setSaveAttachmentCertificateToTempFile(bool);
|
||||
void setFileNameCertificate(const QString& fileNameCertificate);
|
||||
|
||||
private:
|
||||
bool readBool(QXmlStreamReader& reader);
|
||||
int readInt(QXmlStreamReader& reader);
|
||||
|
|
@ -94,6 +109,13 @@ private:
|
|||
bool m_saveAttachmentToTempFile;
|
||||
QString m_fileName;
|
||||
QString m_error;
|
||||
|
||||
// Certificate
|
||||
bool m_useCertificate;
|
||||
QString m_selectedCertificateType;
|
||||
QString m_attachmentNameCertificate;
|
||||
bool m_saveAttachmentCertificateToTempFile;
|
||||
QString m_fileNameCertificate;
|
||||
};
|
||||
|
||||
#endif // KEEAGENTSETTINGS_H
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ OpenSSHKey::OpenSSHKey(QObject* parent)
|
|||
, m_rawPrivateData(QByteArray())
|
||||
, m_comment(QString())
|
||||
, m_error(QString())
|
||||
, m_certificateType(QString())
|
||||
, m_rawCertificateData(QByteArray())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +65,8 @@ OpenSSHKey::OpenSSHKey(const OpenSSHKey& other)
|
|||
, m_rawPrivateData(other.m_rawPrivateData)
|
||||
, m_comment(other.m_comment)
|
||||
, m_error(other.m_error)
|
||||
, m_certificateType(other.m_certificateType)
|
||||
, m_rawCertificateData(other.m_rawCertificateData)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +86,11 @@ const QString OpenSSHKey::type() const
|
|||
return m_type;
|
||||
}
|
||||
|
||||
const QString OpenSSHKey::certificateType() const
|
||||
{
|
||||
return m_certificateType;
|
||||
}
|
||||
|
||||
const QString OpenSSHKey::fingerprint(QCryptographicHash::Algorithm algo) const
|
||||
{
|
||||
if (m_rawPublicData.isEmpty()) {
|
||||
|
|
@ -660,6 +669,84 @@ bool OpenSSHKey::writePrivate(BinaryStream& stream)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OpenSSHKey::parseCertificate(QByteArray& data)
|
||||
{
|
||||
QString stringData = QString::fromLatin1(data);
|
||||
QStringList elements = stringData.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
|
||||
|
||||
QStringList certificateTypeList = {
|
||||
"ssh-ed25519-cert-v01@openssh.com",
|
||||
"ssh-rsa-cert-v01@openssh.com",
|
||||
"ssh-dss-cert-v01@openssh.com",
|
||||
"sk-ssh-ed25519-cert-v01@openssh.com",
|
||||
"sk-ssh-rsa-cert-v01@openssh.com",
|
||||
"sk-ssh-dss-cert-v01@openssh.com",
|
||||
"rsa-sha2-256-cert-v01@openssh.com",
|
||||
"sk-rsa-sha2-256-cert-v01@openssh.com",
|
||||
"rsa-sha2-512-cert-v01@openssh.com",
|
||||
"sk-rsa-sha2-512-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"sk-ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
||||
"sk-ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
||||
};
|
||||
|
||||
if(elements.isEmpty() || elements.size() < 2 || !certificateTypeList.contains(elements.first())) {
|
||||
m_error = tr("Invalid or unsupported certificate file");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_certificateType = elements.first();
|
||||
m_rawCertificateData = QByteArray::fromBase64(elements[1].toLatin1());
|
||||
|
||||
if (m_rawCertificateData.isEmpty()) {
|
||||
m_error = tr("Base64 decoding failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenSSHKey::writeCertificate(BinaryStream& stream, const bool addCertificate)
|
||||
{
|
||||
if (m_rawCertificateData.isEmpty()) {
|
||||
m_error = tr("Can't write certificate as it is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addCertificate) {
|
||||
if (!stream.writeString(m_rawCertificateData)) {
|
||||
m_error = tr("Unexpected EOF when writing certificate");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stream.writeString(m_certificateType)) {
|
||||
m_error = tr("Unexpected EOF when writing certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stream.writeString(m_rawCertificateData)) {
|
||||
m_error = tr("Unexpected EOF when writing certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stream.write(m_rawPrivateData)) {
|
||||
m_error = tr("Unexpected EOF when writing certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stream.writeString(m_comment)) {
|
||||
m_error = tr("Unexpected EOF when writing certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint qHash(const OpenSSHKey& key)
|
||||
{
|
||||
return qHash(key.fingerprint());
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ public:
|
|||
static const QString TYPE_OPENSSH_PRIVATE;
|
||||
static const QString OPENSSH_CIPHER_SUFFIX;
|
||||
|
||||
bool parseCertificate(QByteArray& data);
|
||||
bool writeCertificate(BinaryStream& stream, const bool addCertificate = true);
|
||||
const QString certificateType() const;
|
||||
|
||||
private:
|
||||
enum KeyPart
|
||||
{
|
||||
|
|
@ -85,6 +89,8 @@ private:
|
|||
QByteArray m_rawPrivateData;
|
||||
QString m_comment;
|
||||
QString m_error;
|
||||
QString m_certificateType;
|
||||
QByteArray m_rawCertificateData;
|
||||
};
|
||||
|
||||
uint qHash(const OpenSSHKey& key);
|
||||
|
|
|
|||
|
|
@ -333,6 +333,61 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, const KeeAgentSettings& settings, co
|
|||
OpenSSHKey keyCopy = key;
|
||||
keyCopy.clearPrivate();
|
||||
m_addedKeys[keyCopy] = qMakePair(databaseUuid, settings.removeAtDatabaseClose());
|
||||
|
||||
if (settings.useCertificate()) {
|
||||
QByteArray requestCertificateData;
|
||||
BinaryStream requestCertificate(&requestCertificateData);
|
||||
bool isSecurityCertificate = key.certificateType().startsWith("sk-");
|
||||
|
||||
requestCertificate.write(
|
||||
(settings.useLifetimeConstraintWhenAdding() || settings.useConfirmConstraintWhenAdding() || isSecurityCertificate)
|
||||
? SSH_AGENTC_ADD_ID_CONSTRAINED
|
||||
: SSH_AGENTC_ADD_IDENTITY);
|
||||
|
||||
key.writeCertificate(requestCertificate);
|
||||
|
||||
if (settings.useLifetimeConstraintWhenAdding()) {
|
||||
requestCertificate.write(SSH_AGENT_CONSTRAIN_LIFETIME);
|
||||
requestCertificate.write(static_cast<quint32>(settings.lifetimeConstraintDuration()));
|
||||
}
|
||||
|
||||
if (settings.useConfirmConstraintWhenAdding()) {
|
||||
requestCertificate.write(SSH_AGENT_CONSTRAIN_CONFIRM);
|
||||
}
|
||||
|
||||
// To be verified if useful with certificates
|
||||
if (isSecurityCertificate) {
|
||||
requestCertificate.write(SSH_AGENT_CONSTRAIN_EXTENSION);
|
||||
requestCertificate.writeString(QString("sk-provider@openssh.com"));
|
||||
requestCertificate.writeString(securityKeyProvider());
|
||||
}
|
||||
|
||||
QByteArray responseCertificateData;
|
||||
if (!sendMessage(requestCertificateData, responseCertificateData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (responseCertificateData.length() < 1 || static_cast<quint8>(responseCertificateData[0]) != SSH_AGENT_SUCCESS) {
|
||||
m_error =
|
||||
tr("Agent refused this identity certificate. Possible reasons include:") + "\n" + tr("Invalid or empty certificate.") + "\n" + tr("The key has already been added.");
|
||||
|
||||
if (settings.useLifetimeConstraintWhenAdding()) {
|
||||
m_error += "\n" + tr("Restricted lifetime is not supported by the agent (check options).");
|
||||
}
|
||||
|
||||
if (settings.useConfirmConstraintWhenAdding()) {
|
||||
m_error += "\n" + tr("A confirmation request is not supported by the agent (check options).");
|
||||
}
|
||||
|
||||
if (isSecurityKey) {
|
||||
m_error +=
|
||||
"\n" + tr("Security keys are not supported by the agent or the security key provider is unavailable.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +415,23 @@ bool SSHAgent::removeIdentity(OpenSSHKey& key)
|
|||
request.writeString(keyData);
|
||||
|
||||
QByteArray responseData;
|
||||
return sendMessage(requestData, responseData);
|
||||
|
||||
// Try to remove certificate
|
||||
QByteArray requestCertificateData;
|
||||
BinaryStream requestCertificate(&requestCertificateData);
|
||||
|
||||
QByteArray certificateData;
|
||||
BinaryStream certificateStream(&certificateData);
|
||||
if (key.writeCertificate(certificateStream, false)) {
|
||||
requestCertificate.write(SSH_AGENTC_REMOVE_IDENTITY);
|
||||
requestCertificate.write(certificateData);
|
||||
QByteArray responseCertificateData;
|
||||
|
||||
return (sendMessage(requestData, responseData) &&
|
||||
sendMessage(requestCertificateData, responseCertificateData));
|
||||
}
|
||||
|
||||
return (sendMessage(requestData, responseData));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue