Commit 21f911b3 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Support transferring X.509 certificates to the remaining card slots

* Write X.509 certificates for the PIV Authentication key and the Card
  Authentication key.
* Add a confirmation dialog.

GnuPG-bug-id: 4794
parent 39d57bdd
Pipeline #34470 passed with stage
in 47 minutes and 54 seconds
......@@ -13,13 +13,15 @@
#include "command_p.h"
#include "smartcard/readerstatus.h"
#include "smartcard/pivcard.h"
#include "commands/authenticatepivcardapplicationcommand.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "utils/writecertassuantransaction.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
......@@ -43,8 +45,7 @@ class CertificateToPIVCardCommand::Private : public Command::Private
return static_cast<CertificateToPIVCardCommand *>(q);
}
public:
explicit Private(CertificateToPIVCardCommand *qq, const GpgME::Subkey &key, const std::string &serialno);
explicit Private(CertificateToPIVCardCommand *qq, const std::string &cardSlot, const std::string &serialno);
explicit Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno);
~Private();
private:
......@@ -56,10 +57,9 @@ private:
void authenticationCanceled();
private:
std::string mSerial;
GpgME::Subkey mSubkey;
std::string serialNumber;
std::string cardSlot;
bool overwriteExistingAlreadyApproved = false;
Key certificate;
bool hasBeenCanceled = false;
};
......@@ -76,19 +76,10 @@ const CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func(
#define d d_func()
CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq,
const GpgME::Subkey &key,
const std::string &serialno)
: Command::Private(qq, nullptr),
mSerial(serialno),
mSubkey(key)
{
}
CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &cardSlot_, const std::string &serialno)
CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno)
: Command::Private(qq, nullptr)
, mSerial(serialno)
, cardSlot(cardSlot_)
, serialNumber(serialno)
, cardSlot(slot)
{
}
......@@ -97,21 +88,23 @@ CertificateToPIVCardCommand::Private::~Private()
}
namespace {
static GpgME::Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card)
static Key getCertificateToWriteToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card)
{
if (!cardSlot.empty()) {
const std::string cardKeygrip = card->keyGrip(cardSlot);
const auto subkey = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip);
if (subkey.isNull() || subkey.parent().protocol() != GpgME::CMS) {
return GpgME::Subkey();
const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip).parent();
if (certificate.isNull() || certificate.protocol() != GpgME::CMS) {
return Key();
}
if ((cardSlot == PIVCard::digitalSignatureKeyRef() && subkey.canSign()) ||
(cardSlot == PIVCard::keyManagementKeyRef() && subkey.canEncrypt())) {
return subkey;
if ((cardSlot == PIVCard::pivAuthenticationKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::cardAuthenticationKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::digitalSignatureKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::keyManagementKeyRef() && certificate.canEncrypt())) {
return certificate;
}
}
return GpgME::Subkey();
return Key();
}
}
......@@ -119,20 +112,40 @@ void CertificateToPIVCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::start()";
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(mSerial);
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber);
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(mSerial)));
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber)));
finished();
return;
}
mSubkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard);
if (mSubkey.isNull()) {
certificate = getCertificateToWriteToPIVCard(cardSlot, pivCard);
if (certificate.isNull()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
finished();
return;
}
const QString certificateInfo = i18nc("X.509 certificate DN (validity, created: date)", "%1 (%2, created: %3)",
DN(certificate.userID(0).id()).prettyDN(),
Formatting::complianceStringShort(certificate),
Formatting::creationDateString(certificate));
const QString message = i18nc(
"@info %1 name of card slot, %2 serial number of card",
"<p>Please confirm that you want to write the following certificate to the %1 slot of card %2:</p>"
"<center>%3</center>",
PIVCard::keyDisplayName(cardSlot), QString::fromStdString(serialNumber), certificateInfo);
auto confirmButton = KStandardGuiItem::yes();
confirmButton.setText(i18nc("@action:button", "Write certificate"));
confirmButton.setToolTip(QString());
const auto choice = KMessageBox::questionYesNo(
parentWidgetOrView(), message, i18nc("@title:window", "Write certificate to card"),
confirmButton, KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::WindowModal);
if (choice != KMessageBox::Yes) {
finished();
return;
}
startCertificateToPIVCard();
}
......@@ -143,7 +156,7 @@ void CertificateToPIVCardCommand::Private::startCertificateToPIVCard()
auto ctx = Context::createForProtocol(GpgME::CMS);
QGpgME::QByteArrayDataProvider dp;
Data data(&dp);
const Error err = ctx->exportPublicKeys(mSubkey.parent().primaryFingerprint(), data);
const Error err = ctx->exportPublicKeys(certificate.primaryFingerprint(), data);
if (err) {
error(i18nc("@info", "Exporting the certificate failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
......@@ -162,7 +175,7 @@ void CertificateToPIVCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(mSerial, parentWidgetOrView());
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber, parentWidgetOrView());
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished,
q, [this]() { authenticationFinished(); });
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled,
......@@ -206,12 +219,11 @@ void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err)
return;
}
d->error(i18nc("@info",
"Writing the certificate to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
d->error(i18nc("@info", "Writing the certificate to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
KMessageBox::information(d->parentWidgetOrView(),
i18n("Successfully copied the certificate to the card."),
i18nc("@info", "Writing the certificate to the card succeeded."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
......
......@@ -62,7 +62,9 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
mDigitalSignatureKey(new QLabel(this)),
mKeyManagementKey(new QLabel(this)),
mGeneratePIVAuthenticationKeyBtn(new QPushButton(this)),
mWritePIVAuthenticationCertificateBtn(new QPushButton(this)),
mGenerateCardAuthenticationKeyBtn(new QPushButton(this)),
mWriteCardAuthenticationCertificateBtn(new QPushButton(this)),
mGenerateDigitalSignatureKeyBtn(new QPushButton(this)),
mWriteDigitalSignatureCertificateBtn(new QPushButton(this)),
mGenerateKeyManagementKeyBtn(new QPushButton(this)),
......@@ -100,30 +102,40 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(new QLabel(i18n("PIV authentication:")), row, 0);
grid->addWidget(mPIVAuthenticationKey, row, 1);
mPIVAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGeneratePIVAuthenticationKeyBtn->setText(i18n("Generate"));
mGeneratePIVAuthenticationKeyBtn->setText(i18nc("@action:button", "Generate"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGeneratePIVAuthenticationKeyBtn, row, 2);
connect(mGeneratePIVAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generatePIVAuthenticationKey);
mWritePIVAuthenticationCertificateBtn->setText(i18nc("@action:button", "Write Certificate"));
mWritePIVAuthenticationCertificateBtn->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
mWritePIVAuthenticationCertificateBtn->setEnabled(false);
grid->addWidget(mWritePIVAuthenticationCertificateBtn, row, 3);
connect(mWritePIVAuthenticationCertificateBtn, &QPushButton::clicked, this, [this] () { writeCertificateToCard(PIVCard::pivAuthenticationKeyRef()); });
row++;
grid->addWidget(new QLabel(i18n("Card authentication:")), row, 0);
grid->addWidget(mCardAuthenticationKey, row, 1);
mCardAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateCardAuthenticationKeyBtn->setText(i18n("Generate"));
mGenerateCardAuthenticationKeyBtn->setText(i18nc("@action:button", "Generate"));
mGenerateCardAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGenerateCardAuthenticationKeyBtn, row, 2);
connect(mGenerateCardAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateCardAuthenticationKey);
mWriteCardAuthenticationCertificateBtn->setText(i18nc("@action:button", "Write Certificate"));
mWriteCardAuthenticationCertificateBtn->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
mWriteCardAuthenticationCertificateBtn->setEnabled(false);
grid->addWidget(mWriteCardAuthenticationCertificateBtn, row, 3);
connect(mWriteCardAuthenticationCertificateBtn, &QPushButton::clicked, this, [this] () { writeCertificateToCard(PIVCard::cardAuthenticationKeyRef()); });
row++;
grid->addWidget(new QLabel(i18n("Digital signature:")), row, 0);
grid->addWidget(mDigitalSignatureKey, row, 1);
mDigitalSignatureKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateDigitalSignatureKeyBtn->setText(i18n("Generate"));
mGenerateDigitalSignatureKeyBtn->setText(i18nc("@action:button", "Generate"));
mGenerateDigitalSignatureKeyBtn->setEnabled(false);
grid->addWidget(mGenerateDigitalSignatureKeyBtn, row, 2);
connect(mGenerateDigitalSignatureKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateDigitalSignatureKey);
mWriteDigitalSignatureCertificateBtn->setText(i18n("Write Certificate"));
mWriteDigitalSignatureCertificateBtn->setToolTip(i18n("Write the certificate corresponding to this key to the card"));
mWriteDigitalSignatureCertificateBtn->setText(i18nc("@action:button", "Write Certificate"));
mWriteDigitalSignatureCertificateBtn->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
mWriteDigitalSignatureCertificateBtn->setEnabled(false);
grid->addWidget(mWriteDigitalSignatureCertificateBtn, row, 3);
connect(mWriteDigitalSignatureCertificateBtn, &QPushButton::clicked, this, [this] () { writeCertificateToCard(PIVCard::digitalSignatureKeyRef()); });
......@@ -132,12 +144,12 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(new QLabel(i18n("Key management:")), row, 0);
grid->addWidget(mKeyManagementKey, row, 1);
mKeyManagementKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateKeyManagementKeyBtn->setText(i18n("Generate"));
mGenerateKeyManagementKeyBtn->setText(i18nc("@action:button", "Generate"));
mGenerateKeyManagementKeyBtn->setEnabled(false);
grid->addWidget(mGenerateKeyManagementKeyBtn, row, 2);
connect(mGenerateKeyManagementKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateKeyManagementKey);
mWriteKeyManagementCertificateBtn->setText(i18n("Write Certificate"));
mWriteKeyManagementCertificateBtn->setToolTip(i18n("Write the certificate corresponding to this key to the card"));
mWriteKeyManagementCertificateBtn->setText(i18nc("@action:button", "Write Certificate"));
mWriteKeyManagementCertificateBtn->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
mWriteKeyManagementCertificateBtn->setEnabled(false);
grid->addWidget(mWriteKeyManagementCertificateBtn, row, 3);
connect(mWriteKeyManagementCertificateBtn, &QPushButton::clicked, this, [this] () { writeCertificateToCard(PIVCard::keyManagementKeyRef()); });
......@@ -150,22 +162,23 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
auto actionLayout = new QHBoxLayout;
{
auto button = new QPushButton(i18n("Change PIN"));
button->setToolTip(i18n("Change the PIV Card Application PIN that activates the PIV Card and enables private key operations using the stored keys."));
auto button = new QPushButton(i18nc("@action:button", "Change PIN"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application PIN that activates the PIV Card "
"and enables private key operations using the stored keys."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pinKeyRef()); });
}
{
auto button = new QPushButton(i18n("Change PUK"));
button->setToolTip(i18n("Change the PIN Unblocking Key that enables a reset of the PIN."));
auto button = new QPushButton(i18nc("@action:button", "Change PUK"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIN Unblocking Key that enables a reset of the PIN."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pukKeyRef()); });
}
{
auto button = new QPushButton(i18n("Change Admin Key"));
button->setToolTip(i18n("Change the PIV Card Application Administration Key that is used by the "
"PIV Card Application to authenticate the PIV Card Application Administrator and by the "
"administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
auto button = new QPushButton(i18nc("@action:button", "Change Admin Key"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application Administration Key that is used by the "
"PIV Card Application to authenticate the PIV Card Application Administrator and by the "
"administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { setAdminKey(); });
}
......@@ -183,7 +196,7 @@ PIVCardWidget::~PIVCardWidget()
void PIVCardWidget::setCard(const PIVCard *card)
{
mCardSerialNumber = card->serialNumber();
mVersionLabel->setText(i18nc("Placeholder is a version number", "PIV v%1 card", formatVersion(card->appVersion())));
mVersionLabel->setText(i18nc("%1 version number", "PIV v%1 card", formatVersion(card->appVersion())));
if (card->displaySerialNumber() != card->serialNumber()) {
mSerialNumber->setText(QStringLiteral("%1 (%2)").arg(QString::fromStdString(card->displaySerialNumber()),
......@@ -192,8 +205,8 @@ void PIVCardWidget::setCard(const PIVCard *card)
mSerialNumber->setText(QString::fromStdString(card->serialNumber()));
}
updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn, nullptr);
updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn, nullptr);
updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn, mWritePIVAuthenticationCertificateBtn);
updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn, mWriteCardAuthenticationCertificateBtn);
updateKey(PIVCard::digitalSignatureKeyRef(), card, mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn, mWriteDigitalSignatureCertificateBtn);
updateKey(PIVCard::keyManagementKeyRef(), card, mKeyManagementKey, mGenerateKeyManagementKeyBtn, mWriteKeyManagementCertificateBtn);
}
......@@ -201,11 +214,11 @@ void PIVCardWidget::setCard(const PIVCard *card)
void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, QLabel *label, QPushButton *generateButton, QPushButton *writeButton)
{
const std::string grip = card->keyGrip(keyRef);
label->setText(grip.empty() ? i18n("Slot empty") : QString::fromStdString(grip));
generateButton->setText(grip.empty() ? i18n("Generate") : i18n("Replace"));
label->setText(grip.empty() ? i18nc("@info", "Slot empty") : QString::fromStdString(grip));
generateButton->setText(grip.empty() ? i18nc("@action:button", "Generate") : i18nc("@action:button", "Replace"));
generateButton->setToolTip(grip.empty() ?
i18nc("Placeholder is the display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)) :
i18nc("Placeholder is the display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)) :
i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
generateButton->setEnabled(true);
if (writeButton) {
writeButton->setEnabled(!grip.empty());
......
......@@ -55,7 +55,9 @@ private:
*mDigitalSignatureKey = nullptr,
*mKeyManagementKey = nullptr;
QPushButton *mGeneratePIVAuthenticationKeyBtn = nullptr,
*mWritePIVAuthenticationCertificateBtn = nullptr,
*mGenerateCardAuthenticationKeyBtn = nullptr,
*mWriteCardAuthenticationCertificateBtn = nullptr,
*mGenerateDigitalSignatureKeyBtn = nullptr,
*mWriteDigitalSignatureCertificateBtn = nullptr,
*mGenerateKeyManagementKeyBtn = nullptr,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment