File indexing completed on 2024-05-05 04:45:26
0001 /* 0002 * Copyright (C) 2005-2007 Justin Karneges <justin@affinix.com> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Lesser General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Lesser General Public 0015 * License along with this library; if not, write to the Free Software 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 0017 * 0018 */ 0019 0020 #include <QtCrypto> 0021 0022 #include <QCoreApplication> 0023 #include <QDebug> 0024 #include <QDir> 0025 #include <QFile> 0026 #include <QFileInfo> 0027 #include <QTextStream> 0028 #include <QTimer> 0029 0030 #ifdef QT_STATICPLUGIN 0031 #include "import_plugins.h" 0032 #endif 0033 0034 const char *const APPNAME = "qcatool"; 0035 const char *const EXENAME = "qcatool"; 0036 const char *const VERSION = QCA_VERSION_STR; 0037 0038 static QStringList wrapstring(const QString &str, int width) 0039 { 0040 QStringList out; 0041 QString simp = str.simplified(); 0042 QString rest = simp; 0043 while (true) { 0044 int lastSpace = -1; 0045 int n; 0046 for (n = 0; n < rest.length(); ++n) { 0047 if (rest[n].isSpace()) 0048 lastSpace = n; 0049 if (n == width) 0050 break; 0051 } 0052 if (n == rest.length()) { 0053 out += rest; 0054 break; 0055 } 0056 0057 QString line; 0058 if (lastSpace != -1) { 0059 line = rest.mid(0, lastSpace); 0060 rest = rest.mid(lastSpace + 1); 0061 } else { 0062 line = rest.mid(0, n); 0063 rest = rest.mid(n); 0064 } 0065 out += line; 0066 } 0067 return out; 0068 } 0069 0070 class StreamLogger : public QCA::AbstractLogDevice 0071 { 0072 Q_OBJECT 0073 public: 0074 StreamLogger(QTextStream &stream) 0075 : QCA::AbstractLogDevice(QStringLiteral("Stream logger")) 0076 , _stream(stream) 0077 { 0078 QCA::logger()->registerLogDevice(this); 0079 } 0080 0081 ~StreamLogger() override 0082 { 0083 QCA::logger()->unregisterLogDevice(name()); 0084 } 0085 0086 void logTextMessage(const QString &message, enum QCA::Logger::Severity severity) override 0087 { 0088 _stream << now() << " " << severityName(severity) << " " << message << Qt::endl; 0089 } 0090 0091 void logBinaryMessage(const QByteArray &blob, enum QCA::Logger::Severity severity) override 0092 { 0093 Q_UNUSED(blob); 0094 _stream << now() << " " << severityName(severity) << " " 0095 << "Binary blob not implemented yet" << Qt::endl; 0096 } 0097 0098 private: 0099 inline const char *severityName(enum QCA::Logger::Severity severity) 0100 { 0101 if (severity <= QCA::Logger::Debug) { 0102 return s_severityNames[severity]; 0103 } else { 0104 return s_severityNames[QCA::Logger::Debug + 1]; 0105 } 0106 } 0107 0108 inline QString now() 0109 { 0110 static QString format = QStringLiteral("yyyy-MM-dd hh:mm:ss"); 0111 return QDateTime::currentDateTime().toString(format); 0112 } 0113 0114 private: 0115 static const char *s_severityNames[]; 0116 QTextStream &_stream; 0117 }; 0118 0119 const char *StreamLogger::s_severityNames[] = {"Q", "M", "A", "C", "E", "W", "N", "I", "D", "U"}; 0120 0121 static void output_plugin_diagnostic_text() 0122 { 0123 QString str = QCA::pluginDiagnosticText(); 0124 QCA::clearPluginDiagnosticText(); 0125 if (str[str.length() - 1] == QLatin1Char('\n')) 0126 str.truncate(str.length() - 1); 0127 const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts); 0128 for (int n = 0; n < lines.count(); ++n) 0129 fprintf(stderr, "plugin: %s\n", qPrintable(lines[n])); 0130 } 0131 0132 static void output_keystore_diagnostic_text() 0133 { 0134 QString str = QCA::KeyStoreManager::diagnosticText(); 0135 QCA::KeyStoreManager::clearDiagnosticText(); 0136 if (str[str.length() - 1] == QLatin1Char('\n')) 0137 str.truncate(str.length() - 1); 0138 const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts); 0139 for (int n = 0; n < lines.count(); ++n) 0140 fprintf(stderr, "keystore: %s\n", qPrintable(lines[n])); 0141 } 0142 0143 static void output_message_diagnostic_text(QCA::SecureMessage *msg) 0144 { 0145 QString str = msg->diagnosticText(); 0146 if (str[str.length() - 1] == QLatin1Char('\n')) 0147 str.truncate(str.length() - 1); 0148 const QStringList lines = str.split(QLatin1Char('\n'), Qt::KeepEmptyParts); 0149 for (int n = 0; n < lines.count(); ++n) 0150 fprintf(stderr, "message: %s\n", qPrintable(lines[n])); 0151 } 0152 0153 class AnimatedKeyGen : public QObject 0154 { 0155 Q_OBJECT 0156 public: 0157 static QCA::PrivateKey makeKey(QCA::PKey::Type type, int bits, QCA::DLGroupSet set) 0158 { 0159 AnimatedKeyGen kg; 0160 kg.type = type; 0161 kg.bits = bits; 0162 kg.set = set; 0163 QEventLoop eventLoop; 0164 kg.eventLoop = &eventLoop; 0165 QTimer::singleShot(0, &kg, &AnimatedKeyGen::start); 0166 eventLoop.exec(); 0167 QCA::PrivateKey key = kg.key; 0168 return key; 0169 } 0170 0171 private: 0172 QCA::PKey::Type type; 0173 int bits; 0174 QCA::DLGroupSet set; 0175 QEventLoop *eventLoop; 0176 QCA::KeyGenerator gen; 0177 QCA::DLGroup group; 0178 QCA::PrivateKey key; 0179 QTimer t; 0180 int x; 0181 0182 AnimatedKeyGen() 0183 { 0184 gen.setBlockingEnabled(false); 0185 connect(&gen, &QCA::KeyGenerator::finished, this, &AnimatedKeyGen::gen_finished); 0186 connect(&t, &QTimer::timeout, this, &AnimatedKeyGen::t_timeout); 0187 } 0188 0189 private Q_SLOTS: 0190 void start() 0191 { 0192 printf("Generating Key ... "); 0193 fflush(stdout); 0194 x = 0; 0195 t.start(125); 0196 0197 if (type == QCA::PKey::RSA) 0198 gen.createRSA(bits); 0199 else 0200 gen.createDLGroup(set); 0201 } 0202 0203 void gen_finished() 0204 { 0205 if (type == QCA::PKey::DSA || type == QCA::PKey::DH) { 0206 if (group.isNull()) { 0207 group = gen.dlGroup(); 0208 0209 if (type == QCA::PKey::DSA) 0210 gen.createDSA(group); 0211 else 0212 gen.createDH(group); 0213 return; 0214 } 0215 } 0216 0217 key = gen.key(); 0218 0219 printf("\b"); 0220 if (!key.isNull()) 0221 printf("Done\n"); 0222 else 0223 printf("Error\n"); 0224 0225 eventLoop->exit(); 0226 } 0227 0228 void t_timeout() 0229 { 0230 if (x == 0) 0231 printf("\b/"); 0232 else if (x == 1) 0233 printf("\b-"); 0234 else if (x == 2) 0235 printf("\b\\"); 0236 else if (x == 3) 0237 printf("\b|"); 0238 fflush(stdout); 0239 0240 ++x; 0241 x %= 4; 0242 } 0243 }; 0244 0245 class KeyStoreMonitor : public QObject 0246 { 0247 Q_OBJECT 0248 public: 0249 static void monitor() 0250 { 0251 KeyStoreMonitor monitor; 0252 QEventLoop eventLoop; 0253 monitor.eventLoop = &eventLoop; 0254 QTimer::singleShot(0, &monitor, &KeyStoreMonitor::start); 0255 eventLoop.exec(); 0256 } 0257 0258 private: 0259 QEventLoop *eventLoop; 0260 QCA::KeyStoreManager *ksm; 0261 QList<QCA::KeyStore *> keyStores; 0262 QCA::ConsolePrompt *prompt; 0263 0264 private Q_SLOTS: 0265 void start() 0266 { 0267 // user can quit the monitoring by pressing enter 0268 printf("Monitoring keystores, press 'q' to quit.\n"); 0269 prompt = new QCA::ConsolePrompt(this); 0270 connect(prompt, &QCA::ConsolePrompt::finished, this, &KeyStoreMonitor::prompt_finished); 0271 prompt->getChar(); 0272 0273 // kick off the subsystem 0274 QCA::KeyStoreManager::start(); 0275 0276 // setup keystore manager for monitoring 0277 ksm = new QCA::KeyStoreManager(this); 0278 connect(ksm, &QCA::KeyStoreManager::keyStoreAvailable, this, &KeyStoreMonitor::ks_available); 0279 foreach (const QString &keyStoreId, ksm->keyStores()) 0280 ks_available(keyStoreId); 0281 } 0282 0283 void ks_available(const QString &keyStoreId) 0284 { 0285 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, ksm); 0286 connect(ks, &QCA::KeyStore::updated, this, &KeyStoreMonitor::ks_updated); 0287 connect(ks, &QCA::KeyStore::unavailable, this, &KeyStoreMonitor::ks_unavailable); 0288 keyStores += ks; 0289 0290 printf(" available: %s\n", qPrintable(ks->name())); 0291 } 0292 0293 void ks_updated() 0294 { 0295 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0296 0297 printf(" updated: %s\n", qPrintable(ks->name())); 0298 } 0299 0300 void ks_unavailable() 0301 { 0302 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0303 0304 printf(" unavailable: %s\n", qPrintable(ks->name())); 0305 keyStores.removeAll(ks); 0306 delete ks; 0307 } 0308 0309 void prompt_finished() 0310 { 0311 QChar c = prompt->resultChar(); 0312 if (c == QLatin1Char('q') || c == QLatin1Char('Q')) { 0313 eventLoop->exit(); 0314 return; 0315 } 0316 prompt->getChar(); 0317 } 0318 }; 0319 0320 class PassphrasePrompt : public QObject 0321 { 0322 Q_OBJECT 0323 public: 0324 class Item 0325 { 0326 public: 0327 QString promptStr; 0328 int id; 0329 QCA::Event event; 0330 }; 0331 0332 QCA::EventHandler handler; 0333 bool allowPrompt; 0334 bool warned; 0335 bool have_pass; 0336 bool used_pass; 0337 QCA::SecureArray pass; 0338 QCA::ConsolePrompt *prompt; 0339 int prompt_id; 0340 QCA::Event prompt_event; 0341 QList<Item> pending; 0342 bool auto_accept; 0343 0344 QCA::KeyStoreManager ksm; 0345 QList<QCA::KeyStore *> keyStores; 0346 0347 PassphrasePrompt() 0348 : handler(this) 0349 , ksm(this) 0350 { 0351 allowPrompt = true; 0352 warned = false; 0353 have_pass = false; 0354 auto_accept = false; 0355 0356 prompt = nullptr; 0357 0358 connect(&handler, &QCA::EventHandler::eventReady, this, &PassphrasePrompt::ph_eventReady); 0359 handler.start(); 0360 0361 connect(&ksm, &QCA::KeyStoreManager::keyStoreAvailable, this, &PassphrasePrompt::ks_available); 0362 foreach (const QString &keyStoreId, ksm.keyStores()) 0363 ks_available(keyStoreId); 0364 } 0365 0366 ~PassphrasePrompt() override 0367 { 0368 qDeleteAll(keyStores); 0369 0370 if (prompt) { 0371 handler.reject(prompt_id); 0372 delete prompt; 0373 } 0374 0375 while (!pending.isEmpty()) 0376 handler.reject(pending.takeFirst().id); 0377 } 0378 0379 void setExplicitPassword(const QCA::SecureArray &_pass) 0380 { 0381 have_pass = true; 0382 used_pass = false; 0383 pass = _pass; 0384 } 0385 0386 private Q_SLOTS: 0387 void ph_eventReady(int id, const QCA::Event &e) 0388 { 0389 if (have_pass) { 0390 // only allow using an explicit passphrase once 0391 if (used_pass) { 0392 handler.reject(id); 0393 return; 0394 } 0395 used_pass = true; 0396 handler.submitPassword(id, pass); 0397 return; 0398 } 0399 0400 if (!allowPrompt) { 0401 if (!have_pass && !warned) { 0402 warned = true; 0403 fprintf(stderr, "Error: no passphrase specified (use '--pass=' for none).\n"); 0404 } 0405 0406 handler.reject(id); 0407 return; 0408 } 0409 0410 if (e.type() == QCA::Event::Password) { 0411 QString type = QStringLiteral("password"); 0412 if (e.passwordStyle() == QCA::Event::StylePassphrase) 0413 type = QStringLiteral("passphrase"); 0414 else if (e.passwordStyle() == QCA::Event::StylePIN) 0415 type = QStringLiteral("PIN"); 0416 0417 QString str; 0418 if (e.source() == QCA::Event::KeyStore) { 0419 QString name; 0420 QCA::KeyStoreEntry entry = e.keyStoreEntry(); 0421 if (!entry.isNull()) { 0422 name = entry.name(); 0423 } else { 0424 if (e.keyStoreInfo().type() == QCA::KeyStore::SmartCard) 0425 name = QStringLiteral("the '") + e.keyStoreInfo().name() + QStringLiteral("' token"); 0426 else 0427 name = e.keyStoreInfo().name(); 0428 } 0429 str = QStringLiteral("Enter %1 for %2").arg(type, name); 0430 } else if (!e.fileName().isEmpty()) 0431 str = QStringLiteral("Enter %1 for %2").arg(type, e.fileName()); 0432 else 0433 str = QStringLiteral("Enter %1").arg(type); 0434 0435 if (!prompt) { 0436 prompt = new QCA::ConsolePrompt(this); 0437 connect(prompt, &QCA::ConsolePrompt::finished, this, &PassphrasePrompt::prompt_finished); 0438 prompt_id = id; 0439 prompt_event = e; 0440 prompt->getHidden(str); 0441 } else { 0442 Item i; 0443 i.promptStr = str; 0444 i.id = id; 0445 i.event = e; 0446 pending += i; 0447 } 0448 } else if (e.type() == QCA::Event::Token) { 0449 // even though we're being prompted for a missing token, 0450 // we should still check if the token is present, due to 0451 // a possible race between insert and token request. 0452 bool found = false; 0453 0454 // token-only 0455 if (e.keyStoreEntry().isNull()) { 0456 foreach (QCA::KeyStore *ks, keyStores) { 0457 if (ks->id() == e.keyStoreInfo().id()) { 0458 found = true; 0459 break; 0460 } 0461 } 0462 } 0463 // token-entry 0464 else { 0465 QCA::KeyStoreEntry kse = e.keyStoreEntry(); 0466 0467 QCA::KeyStore *ks = nullptr; 0468 foreach (QCA::KeyStore *i, keyStores) { 0469 if (i->id() == e.keyStoreInfo().id()) { 0470 ks = i; 0471 break; 0472 } 0473 } 0474 if (ks) { 0475 QList<QCA::KeyStoreEntry> list = ks->entryList(); 0476 foreach (const QCA::KeyStoreEntry &e, list) { 0477 if (e.id() == kse.id() && kse.isAvailable()) { 0478 found = true; 0479 break; 0480 } 0481 } 0482 } 0483 } 0484 if (found) { 0485 // auto-accept 0486 handler.tokenOkay(id); 0487 return; 0488 } 0489 0490 QCA::KeyStoreEntry entry = e.keyStoreEntry(); 0491 QString name; 0492 if (!entry.isNull()) { 0493 name = QStringLiteral("Please make ") + entry.name() + QStringLiteral(" (of ") + entry.storeName() + 0494 QStringLiteral(") available"); 0495 } else { 0496 name = QStringLiteral("Please insert the '") + e.keyStoreInfo().name() + QStringLiteral("' token"); 0497 } 0498 0499 QString str = QStringLiteral("%1 and press Enter (or 'q' to cancel) ...").arg(name); 0500 0501 if (!prompt) { 0502 fprintf(stderr, "%s\n", qPrintable(str)); 0503 prompt = new QCA::ConsolePrompt(this); 0504 connect(prompt, &QCA::ConsolePrompt::finished, this, &PassphrasePrompt::prompt_finished); 0505 prompt_id = id; 0506 prompt_event = e; 0507 prompt->getChar(); 0508 } else { 0509 Item i; 0510 i.promptStr = str; 0511 i.id = id; 0512 i.event = e; 0513 pending += i; 0514 } 0515 } else 0516 handler.reject(id); 0517 } 0518 0519 void prompt_finished() 0520 { 0521 if (prompt_event.type() == QCA::Event::Password) { 0522 handler.submitPassword(prompt_id, prompt->result()); 0523 } else { 0524 if (auto_accept) { 0525 auto_accept = false; 0526 handler.tokenOkay(prompt_id); 0527 } else { 0528 QChar c = prompt->resultChar(); 0529 if (c == QLatin1Char('\r') || c == QLatin1Char('\n')) 0530 handler.tokenOkay(prompt_id); 0531 else if (c == QLatin1Char('q') || c == QLatin1Char('Q')) 0532 handler.reject(prompt_id); 0533 else { 0534 // retry 0535 prompt->getChar(); 0536 return; 0537 } 0538 } 0539 } 0540 0541 if (!pending.isEmpty()) { 0542 Item i = pending.takeFirst(); 0543 prompt_id = i.id; 0544 prompt_event = i.event; 0545 if (i.event.type() == QCA::Event::Password) { 0546 prompt->getHidden(i.promptStr); 0547 } else // Token 0548 { 0549 fprintf(stderr, "%s\n", qPrintable(i.promptStr)); 0550 prompt->getChar(); 0551 } 0552 } else { 0553 delete prompt; 0554 prompt = nullptr; 0555 } 0556 } 0557 0558 void ks_available(const QString &keyStoreId) 0559 { 0560 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm); 0561 connect(ks, &QCA::KeyStore::updated, this, &PassphrasePrompt::ks_updated); 0562 connect(ks, &QCA::KeyStore::unavailable, this, &PassphrasePrompt::ks_unavailable); 0563 keyStores += ks; 0564 ks->startAsynchronousMode(); 0565 0566 // are we currently in a token-only prompt? 0567 if (prompt && prompt_event.type() == QCA::Event::Token && prompt_event.keyStoreEntry().isNull()) { 0568 // was the token we're looking for just inserted? 0569 if (prompt_event.keyStoreInfo().id() == keyStoreId) { 0570 fprintf(stderr, "Token inserted! Continuing...\n"); 0571 0572 // auto-accept 0573 auto_accept = true; 0574 prompt_finished(); 0575 } 0576 } 0577 } 0578 0579 void ks_unavailable() 0580 { 0581 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0582 keyStores.removeAll(ks); 0583 delete ks; 0584 } 0585 0586 void ks_updated() 0587 { 0588 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0589 0590 // are we currently in a token-entry prompt? 0591 if (prompt && prompt_event.type() == QCA::Event::Token && !prompt_event.keyStoreEntry().isNull()) { 0592 QCA::KeyStoreEntry kse = prompt_event.keyStoreEntry(); 0593 0594 // was the token of the entry we're looking for updated? 0595 if (prompt_event.keyStoreInfo().id() == ks->id()) { 0596 // is the entry available? 0597 bool avail = false; 0598 QList<QCA::KeyStoreEntry> list = ks->entryList(); 0599 foreach (const QCA::KeyStoreEntry &e, list) { 0600 if (e.id() == kse.id()) { 0601 avail = kse.isAvailable(); 0602 break; 0603 } 0604 } 0605 if (avail) { 0606 fprintf(stderr, "Entry available! Continuing...\n"); 0607 0608 // auto-accept 0609 auto_accept = true; 0610 prompt_finished(); 0611 } 0612 } 0613 } 0614 } 0615 }; 0616 0617 class PassphrasePromptThread : public QCA::SyncThread 0618 { 0619 Q_OBJECT 0620 public: 0621 PassphrasePrompt *pp; 0622 0623 PassphrasePromptThread() 0624 { 0625 start(); 0626 } 0627 0628 ~PassphrasePromptThread() override 0629 { 0630 stop(); 0631 } 0632 0633 protected: 0634 void atStart() override 0635 { 0636 pp = new PassphrasePrompt; 0637 } 0638 0639 void atEnd() override 0640 { 0641 delete pp; 0642 } 0643 }; 0644 0645 static bool promptForNewPassphrase(QCA::SecureArray *result) 0646 { 0647 QCA::ConsolePrompt prompt; 0648 prompt.getHidden(QStringLiteral("Enter new passphrase")); 0649 prompt.waitForFinished(); 0650 QCA::SecureArray out = prompt.result(); 0651 0652 prompt.getHidden(QStringLiteral("Confirm new passphrase")); 0653 prompt.waitForFinished(); 0654 0655 if (prompt.result() != out) { 0656 fprintf(stderr, "Error: confirmation does not match original entry.\n"); 0657 return false; 0658 } 0659 *result = out; 0660 return true; 0661 } 0662 0663 static void ksm_start_and_wait() 0664 { 0665 // activate the KeyStoreManager and block until ready 0666 QCA::KeyStoreManager::start(); 0667 { 0668 QCA::KeyStoreManager ksm; 0669 ksm.waitForBusyFinished(); 0670 } 0671 } 0672 0673 static QString line_encode(const QString &in) 0674 { 0675 QString out; 0676 for (const QChar &c : in) { 0677 if (c == QLatin1Char('\\')) 0678 out += QStringLiteral("\\\\"); 0679 else if (c == QLatin1Char('\n')) 0680 out += QStringLiteral("\\n"); 0681 else 0682 out += c; 0683 } 0684 return out; 0685 } 0686 0687 static QString line_decode(const QString &in) 0688 { 0689 QString out; 0690 for (int n = 0; n < in.length(); ++n) { 0691 if (in[n] == QLatin1Char('\\')) { 0692 if (n + 1 < in.length()) { 0693 if (in[n + 1] == QLatin1Char('\\')) 0694 out += QLatin1Char('\\'); 0695 else if (in[n + 1] == QLatin1Char('n')) 0696 out += QLatin1Char('\n'); 0697 ++n; 0698 } 0699 } else 0700 out += in[n]; 0701 } 0702 return out; 0703 } 0704 0705 static QString make_ksentry_string(const QString &id) 0706 { 0707 QString out; 0708 out += QStringLiteral("QCATOOL_KEYSTOREENTRY_1\n"); 0709 out += line_encode(id) + QLatin1Char('\n'); 0710 return out; 0711 } 0712 0713 /*static bool write_ksentry_file(const QString &id, const QString &fileName) 0714 { 0715 QFile f(fileName); 0716 if(!f.open(QFile::WriteOnly | QFile::Truncate)) 0717 return false; 0718 f.write(make_ksentry_string(id).toUtf8()); 0719 return true; 0720 }*/ 0721 0722 static QString read_ksentry_file(const QString &fileName) 0723 { 0724 QString out; 0725 0726 QFile f(fileName); 0727 if (!f.open(QFile::ReadOnly)) 0728 return out; 0729 QTextStream ts(&f); 0730 int linenum = 0; 0731 while (!ts.atEnd()) { 0732 QString line = ts.readLine(); 0733 if (linenum == 0) { 0734 if (line != QLatin1String("QCATOOL_KEYSTOREENTRY_1")) 0735 return out; 0736 } else { 0737 out = line_decode(line); 0738 break; 0739 } 0740 ++linenum; 0741 } 0742 return out; 0743 } 0744 0745 static bool is_pem_file(const QString &fileName) 0746 { 0747 QFile f(fileName); 0748 if (!f.open(QFile::ReadOnly)) 0749 return false; 0750 QTextStream ts(&f); 0751 if (!ts.atEnd()) { 0752 QString line = ts.readLine(); 0753 if (line.startsWith(QLatin1String("-----BEGIN"))) 0754 return true; 0755 } 0756 return false; 0757 } 0758 0759 static QByteArray read_der_file(const QString &fileName) 0760 { 0761 QFile f(fileName); 0762 if (!f.open(QFile::ReadOnly)) 0763 return QByteArray(); 0764 return f.readAll(); 0765 } 0766 0767 class InfoType 0768 { 0769 public: 0770 QCA::CertificateInfoType type; 0771 QString varname; 0772 QString shortname; 0773 QString name; 0774 QString desc; 0775 0776 InfoType() 0777 { 0778 } 0779 0780 InfoType(const QCA::CertificateInfoType &_type, 0781 const QString &_varname, 0782 const QString &_shortname, 0783 const QString &_name, 0784 const QString &_desc) 0785 : type(_type) 0786 , varname(_varname) 0787 , shortname(_shortname) 0788 , name(_name) 0789 , desc(_desc) 0790 { 0791 } 0792 }; 0793 0794 static QList<InfoType> makeInfoTypeList(bool legacyEmail = false) 0795 { 0796 QList<InfoType> out; 0797 out += InfoType(QCA::CommonName, 0798 QStringLiteral("CommonName"), 0799 QStringLiteral("CN"), 0800 QStringLiteral("Common Name (CN)"), 0801 QStringLiteral("Full name, domain, anything")); 0802 out += InfoType( 0803 QCA::Email, QStringLiteral("Email"), QLatin1String(""), QStringLiteral("Email Address"), QLatin1String("")); 0804 if (legacyEmail) 0805 out += InfoType(QCA::EmailLegacy, 0806 QStringLiteral("EmailLegacy"), 0807 QLatin1String(""), 0808 QStringLiteral("PKCS#9 Email Address"), 0809 QLatin1String("")); 0810 out += InfoType(QCA::Organization, 0811 QStringLiteral("Organization"), 0812 QStringLiteral("O"), 0813 QStringLiteral("Organization (O)"), 0814 QStringLiteral("Company, group, etc")); 0815 out += InfoType(QCA::OrganizationalUnit, 0816 QStringLiteral("OrganizationalUnit"), 0817 QStringLiteral("OU"), 0818 QStringLiteral("Organizational Unit (OU)"), 0819 QStringLiteral("Division/branch of organization")); 0820 out += InfoType(QCA::Locality, 0821 QStringLiteral("Locality"), 0822 QLatin1String(""), 0823 QStringLiteral("Locality (L)"), 0824 QStringLiteral("City, shire, part of a state")); 0825 out += InfoType(QCA::State, 0826 QStringLiteral("State"), 0827 QLatin1String(""), 0828 QStringLiteral("State (ST)"), 0829 QStringLiteral("State within the country")); 0830 out += InfoType(QCA::Country, 0831 QStringLiteral("Country"), 0832 QStringLiteral("C"), 0833 QStringLiteral("Country Code (C)"), 0834 QStringLiteral("2-letter code")); 0835 out += InfoType(QCA::IncorporationLocality, 0836 QStringLiteral("IncorporationLocality"), 0837 QLatin1String(""), 0838 QStringLiteral("Incorporation Locality"), 0839 QStringLiteral("For EV certificates")); 0840 out += InfoType(QCA::IncorporationState, 0841 QStringLiteral("IncorporationState"), 0842 QLatin1String(""), 0843 QStringLiteral("Incorporation State"), 0844 QStringLiteral("For EV certificates")); 0845 out += InfoType(QCA::IncorporationCountry, 0846 QStringLiteral("IncorporationCountry"), 0847 QLatin1String(""), 0848 QStringLiteral("Incorporation Country"), 0849 QStringLiteral("For EV certificates")); 0850 out += InfoType(QCA::URI, QStringLiteral("URI"), QLatin1String(""), QStringLiteral("URI"), QLatin1String("")); 0851 out += InfoType(QCA::DNS, 0852 QStringLiteral("DNS"), 0853 QLatin1String(""), 0854 QStringLiteral("Domain Name"), 0855 QStringLiteral("Domain (dnsName)")); 0856 out += InfoType(QCA::IPAddress, 0857 QStringLiteral("IPAddress"), 0858 QLatin1String(""), 0859 QStringLiteral("IP Adddress"), 0860 QLatin1String("")); 0861 out += InfoType(QCA::XMPP, 0862 QStringLiteral("XMPP"), 0863 QLatin1String(""), 0864 QStringLiteral("XMPP Address (JID)"), 0865 QStringLiteral("From RFC 3920 (id-on-xmppAddr)")); 0866 return out; 0867 } 0868 0869 class MyConstraintType 0870 { 0871 public: 0872 QCA::ConstraintType type; 0873 QString varname; 0874 QString name; 0875 QString desc; 0876 0877 MyConstraintType() 0878 { 0879 } 0880 0881 MyConstraintType(const QCA::ConstraintType &_type, 0882 const QString &_varname, 0883 const QString &_name, 0884 const QString &_desc) 0885 : type(_type) 0886 , varname(_varname) 0887 , name(_name) 0888 , desc(_desc) 0889 { 0890 } 0891 }; 0892 0893 static QList<MyConstraintType> makeConstraintTypeList() 0894 { 0895 QList<MyConstraintType> out; 0896 out += MyConstraintType(QCA::DigitalSignature, 0897 QStringLiteral("DigitalSignature"), 0898 QStringLiteral("Digital Signature"), 0899 QStringLiteral("Can be used for signing")); 0900 out += MyConstraintType(QCA::NonRepudiation, 0901 QStringLiteral("NonRepudiation"), 0902 QStringLiteral("Non-Repudiation"), 0903 QStringLiteral("Usage is legally binding")); 0904 out += MyConstraintType(QCA::KeyEncipherment, 0905 QStringLiteral("KeyEncipherment"), 0906 QStringLiteral("Key Encipherment"), 0907 QStringLiteral("Can encrypt other keys")); 0908 out += MyConstraintType(QCA::DataEncipherment, 0909 QStringLiteral("DataEncipherment"), 0910 QStringLiteral("Data Encipherment"), 0911 QStringLiteral("Can encrypt arbitrary data")); 0912 out += MyConstraintType(QCA::KeyAgreement, 0913 QStringLiteral("KeyAgreement"), 0914 QStringLiteral("Key Agreement"), 0915 QStringLiteral("Can perform key agreement (DH)")); 0916 out += MyConstraintType(QCA::KeyCertificateSign, 0917 QStringLiteral("KeyCertificateSign"), 0918 QStringLiteral("Certificate Sign"), 0919 QStringLiteral("Can sign other certificates")); 0920 out += MyConstraintType( 0921 QCA::CRLSign, QStringLiteral("CRLSign"), QStringLiteral("CRL Sign"), QStringLiteral("Can sign CRLs")); 0922 out += MyConstraintType(QCA::EncipherOnly, 0923 QStringLiteral("EncipherOnly"), 0924 QStringLiteral("Encipher Only"), 0925 QStringLiteral("Can be used for encrypting")); 0926 out += MyConstraintType(QCA::DecipherOnly, 0927 QStringLiteral("DecipherOnly"), 0928 QStringLiteral("Decipher Only"), 0929 QStringLiteral("Can be used for decrypting")); 0930 out += MyConstraintType(QCA::ServerAuth, 0931 QStringLiteral("ServerAuth"), 0932 QStringLiteral("Server Authentication"), 0933 QStringLiteral("TLS Server")); 0934 out += MyConstraintType(QCA::ClientAuth, 0935 QStringLiteral("ClientAuth"), 0936 QStringLiteral("Client Authentication"), 0937 QStringLiteral("TLS Client")); 0938 out += MyConstraintType( 0939 QCA::CodeSigning, QStringLiteral("CodeSigning"), QStringLiteral("Code Signing"), QLatin1String("")); 0940 out += MyConstraintType(QCA::EmailProtection, 0941 QStringLiteral("EmailProtection"), 0942 QStringLiteral("Email Protection"), 0943 QStringLiteral("S/MIME")); 0944 out += MyConstraintType( 0945 QCA::IPSecEndSystem, QStringLiteral("IPSecEndSystem"), QStringLiteral("IPSec End-System"), QLatin1String("")); 0946 out += MyConstraintType( 0947 QCA::IPSecTunnel, QStringLiteral("IPSecTunnel"), QStringLiteral("IPSec Tunnel"), QLatin1String("")); 0948 out += 0949 MyConstraintType(QCA::IPSecUser, QStringLiteral("IPSecUser"), QStringLiteral("IPSec User"), QLatin1String("")); 0950 out += MyConstraintType( 0951 QCA::TimeStamping, QStringLiteral("TimeStamping"), QStringLiteral("Time Stamping"), QLatin1String("")); 0952 out += MyConstraintType( 0953 QCA::OCSPSigning, QStringLiteral("OCSPSigning"), QStringLiteral("OCSP Signing"), QLatin1String("")); 0954 return out; 0955 } 0956 0957 const char *crlEntryReasonToString(QCA::CRLEntry::Reason r) 0958 { 0959 switch (r) { 0960 case QCA::CRLEntry::Unspecified: 0961 return "Unspecified"; 0962 case QCA::CRLEntry::KeyCompromise: 0963 return "KeyCompromise"; 0964 case QCA::CRLEntry::CACompromise: 0965 return "CACompromise"; 0966 case QCA::CRLEntry::AffiliationChanged: 0967 return "AffiliationChanged"; 0968 case QCA::CRLEntry::Superseded: 0969 return "Superseded"; 0970 case QCA::CRLEntry::CessationOfOperation: 0971 return "CessationOfOperation"; 0972 case QCA::CRLEntry::CertificateHold: 0973 return "CertificateHold"; 0974 case QCA::CRLEntry::RemoveFromCRL: 0975 return "RemoveFromCRL"; 0976 case QCA::CRLEntry::PrivilegeWithdrawn: 0977 return "PrivilegeWithdrawn"; 0978 case QCA::CRLEntry::AACompromise: 0979 return "AACompromise"; 0980 default: 0981 return "Unknown"; 0982 } 0983 } 0984 0985 static bool validOid(const QString &in) 0986 { 0987 for (const QChar &c : in) { 0988 if (!c.isDigit() && c != QLatin1Char('.')) 0989 return false; 0990 } 0991 return true; 0992 } 0993 0994 class ValidityLength 0995 { 0996 public: 0997 int years, months, days; 0998 }; 0999 1000 static int vl_getnext(const QString &in, int offset = 0) 1001 { 1002 if (offset >= in.length()) 1003 return in.length(); 1004 1005 int n = offset; 1006 bool lookForNonDigit; 1007 1008 if (in[n].isDigit()) 1009 lookForNonDigit = true; 1010 else 1011 lookForNonDigit = false; 1012 1013 for (++n; n < in.length(); ++n) { 1014 if (in[n].isDigit() != lookForNonDigit) 1015 break; 1016 } 1017 return n; 1018 } 1019 1020 static QStringList vl_getparts(const QString &in) 1021 { 1022 QStringList out; 1023 int offset = 0; 1024 while (true) { 1025 int n = vl_getnext(in, offset); 1026 if (n == offset) 1027 break; 1028 out += in.mid(offset, n - offset); 1029 offset = n; 1030 } 1031 return out; 1032 } 1033 1034 static bool parseValidityLength(const QString &in, ValidityLength *vl) 1035 { 1036 vl->years = -1; 1037 vl->months = -1; 1038 vl->days = -1; 1039 1040 QStringList parts = vl_getparts(in); 1041 while (true) { 1042 // first part should be a number 1043 if (parts.count() < 1) 1044 break; 1045 QString str = parts.takeFirst(); 1046 bool ok; 1047 int x = str.toInt(&ok); 1048 if (!ok) 1049 return false; 1050 1051 // next part should be 1 letter plus any amount of space 1052 if (parts.count() < 1) 1053 return false; 1054 str = parts.takeFirst(); 1055 if (!str[0].isLetter()) 1056 return false; 1057 str = str.trimmed(); // remove space 1058 1059 if (str == QLatin1String("y")) { 1060 if (vl->years != -1) 1061 return false; 1062 vl->years = x; 1063 } 1064 if (str == QLatin1String("m")) { 1065 if (vl->months != -1) 1066 return false; 1067 vl->months = x; 1068 } 1069 if (str == QLatin1String("d")) { 1070 if (vl->days != -1) 1071 return false; 1072 vl->days = x; 1073 } 1074 } 1075 1076 if (vl->years == -1) 1077 vl->years = 0; 1078 if (vl->months == -1) 1079 vl->months = 0; 1080 if (vl->days == -1) 1081 vl->days = 0; 1082 1083 return true; 1084 } 1085 1086 static QString prompt_for(const QString &prompt) 1087 { 1088 printf("%s: ", prompt.toLatin1().data()); 1089 fflush(stdout); 1090 QByteArray result(256, 0); 1091 if (fgets((char *)result.data(), result.size(), stdin)) 1092 return QString::fromLocal8Bit(result).trimmed(); 1093 else 1094 return QString(); 1095 } 1096 1097 static QCA::CertificateOptions promptForCertAttributes(bool advanced, bool req) 1098 { 1099 QCA::CertificateOptions opts; 1100 1101 if (advanced) { 1102 if (!req) { 1103 while (true) { 1104 QString str = prompt_for( 1105 QStringLiteral("Create an end user ('user') certificate or a CA ('ca') certificate? [user]")); 1106 if (str.isEmpty()) 1107 str = QStringLiteral("user"); 1108 if (str != QLatin1String("user") && str != QLatin1String("ca")) { 1109 printf("'%s' is not a valid entry.\n", qPrintable(str)); 1110 continue; 1111 } 1112 1113 if (str == QLatin1String("ca")) 1114 opts.setAsCA(); 1115 break; 1116 } 1117 printf("\n"); 1118 1119 while (true) { 1120 QString str = prompt_for(QStringLiteral("Serial Number")); 1121 QCA::BigInteger num; 1122 if (str.isEmpty() || !num.fromString(str)) { 1123 printf("'%s' is not a valid entry.\n", qPrintable(str)); 1124 continue; 1125 } 1126 1127 opts.setSerialNumber(num); 1128 break; 1129 } 1130 printf("\n"); 1131 } 1132 1133 { 1134 QCA::CertificateInfoOrdered info; 1135 printf( 1136 "Choose the information attributes to add to the certificate. They will be\n" 1137 "added in the order they are entered.\n\n"); 1138 printf("Available information attributes:\n"); 1139 QList<InfoType> list = makeInfoTypeList(); 1140 for (int n = 0; n < list.count(); ++n) { 1141 const InfoType &i = list[n]; 1142 char c = 'a' + n; 1143 printf(" %c) %-32s %s\n", c, qPrintable(i.name), qPrintable(i.desc)); 1144 } 1145 printf("\n"); 1146 while (true) { 1147 int index; 1148 while (true) { 1149 QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on")); 1150 if (str.isEmpty()) { 1151 index = -1; 1152 break; 1153 } 1154 if (str.length() == 1) { 1155 index = str[0].toLatin1() - 'a'; 1156 if (index >= 0 && index < list.count()) 1157 break; 1158 } 1159 printf("'%s' is not a valid entry.\n", qPrintable(str)); 1160 } 1161 if (index == -1) 1162 break; 1163 1164 QString val = prompt_for(list[index].name); 1165 info += QCA::CertificateInfoPair(list[index].type, val); 1166 printf("Added attribute.\n\n"); 1167 } 1168 opts.setInfoOrdered(info); 1169 } 1170 1171 { 1172 QCA::Constraints constraints; 1173 printf("\n"); 1174 printf("Choose the constraint attributes to add to the certificate.\n\n"); 1175 printf("Available attributes:\n"); 1176 QList<MyConstraintType> list = makeConstraintTypeList(); 1177 for (int n = 0; n < list.count(); ++n) { 1178 const MyConstraintType &i = list[n]; 1179 char c = 'a' + n; 1180 printf(" %c) %-32s %s\n", c, qPrintable(i.name), qPrintable(i.desc)); 1181 } 1182 printf("\n"); 1183 printf("If no constraints are added, then the certificate may be used for any purpose.\n\n"); 1184 while (true) { 1185 int index; 1186 while (true) { 1187 QString str = prompt_for(QStringLiteral("Select an attribute to add, or enter to move on")); 1188 if (str.isEmpty()) { 1189 index = -1; 1190 break; 1191 } 1192 if (str.length() == 1) { 1193 index = str[0].toLatin1() - 'a'; 1194 if (index >= 0 && index < list.count()) 1195 break; 1196 } 1197 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1198 } 1199 if (index == -1) 1200 break; 1201 1202 if (constraints.contains(list[index].type)) { 1203 printf("You have already added '%s'.\n\n", qPrintable(list[index].name)); 1204 continue; 1205 } 1206 1207 constraints += list[index].type; 1208 printf("Added attribute.\n\n"); 1209 } 1210 opts.setConstraints(constraints); 1211 } 1212 1213 { 1214 QStringList policies; 1215 printf("\n"); 1216 printf( 1217 "Are there any policy OID attributes that you wish to add? Use the dotted\n" 1218 "string format.\n\n"); 1219 while (true) { 1220 QString str = prompt_for(QStringLiteral("Enter a policy OID to add, or enter to move on")); 1221 if (str.isEmpty()) 1222 break; 1223 if (!validOid(str)) { 1224 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1225 continue; 1226 } 1227 if (policies.contains(str)) { 1228 printf("You have already added '%s'.\n\n", qPrintable(str)); 1229 continue; 1230 } 1231 1232 policies += str; 1233 printf("Added attribute.\n\n"); 1234 } 1235 opts.setPolicies(policies); 1236 } 1237 1238 printf("\n"); 1239 } else { 1240 QCA::CertificateInfo info; 1241 info.insert(QCA::CommonName, prompt_for(QStringLiteral("Common Name"))); 1242 info.insert(QCA::Country, prompt_for(QStringLiteral("Country Code (2 letters)"))); 1243 info.insert(QCA::Organization, prompt_for(QStringLiteral("Organization"))); 1244 info.insert(QCA::Email, prompt_for(QStringLiteral("Email"))); 1245 opts.setInfo(info); 1246 1247 printf("\n"); 1248 } 1249 1250 if (!req) { 1251 while (true) { 1252 QString str = prompt_for(QStringLiteral("How long should the certificate be valid? (e.g. '1y2m3d')")); 1253 ValidityLength vl; 1254 if (!parseValidityLength(str, &vl)) { 1255 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1256 continue; 1257 } 1258 1259 if (vl.years == 0 && vl.months == 0 && vl.days == 0) { 1260 printf("The certificate must be valid for at least one day.\n\n"); 1261 continue; 1262 } 1263 1264 QDateTime start = QDateTime::currentDateTimeUtc(); 1265 QDateTime end = start; 1266 if (vl.years > 0) 1267 end = end.addYears(vl.years); 1268 if (vl.months > 0) 1269 end = end.addMonths(vl.months); 1270 if (vl.days > 0) 1271 end = end.addDays(vl.days); 1272 opts.setValidityPeriod(start, end); 1273 1274 QStringList parts; 1275 if (vl.years > 0) 1276 parts += QStringLiteral("%1 year(s)").arg(vl.years); 1277 if (vl.months > 0) 1278 parts += QStringLiteral("%1 month(s)").arg(vl.months); 1279 if (vl.days > 0) 1280 parts += QStringLiteral("%1 day(s)").arg(vl.days); 1281 QString out; 1282 if (parts.count() == 1) 1283 out = parts[0]; 1284 else if (parts.count() == 2) 1285 out = parts[0] + QStringLiteral(" and ") + parts[1]; 1286 else if (parts.count() == 3) 1287 out = parts[0] + QStringLiteral(", ") + parts[1] + QStringLiteral(", and ") + parts[2]; 1288 printf("Certificate will be valid for %s.\n", qPrintable(out)); 1289 break; 1290 } 1291 printf("\n"); 1292 } 1293 1294 return opts; 1295 } 1296 1297 // qsettings seems to give us a string type for both bool and int (and 1298 // possibly others, but those are the only two we care about here). 1299 // in order to figure out what is actually a bool or an int, we need 1300 // to examine the string. so for the functions below, we convert 1301 // the variant to a string, and then inspect it to see if it looks 1302 // like a bool or an int. 1303 1304 static bool string_is_bool(const QString &in) 1305 { 1306 QString lc = in.toLower(); 1307 if (lc == QLatin1String("true") || lc == QLatin1String("false")) 1308 return true; 1309 return false; 1310 } 1311 1312 static bool string_is_int(const QString &in) 1313 { 1314 bool ok; 1315 in.toInt(&ok); 1316 return ok; 1317 } 1318 1319 static bool variant_is_bool(const QVariant &in) 1320 { 1321 if (in.canConvert<QString>() && string_is_bool(in.toString())) 1322 return true; 1323 return false; 1324 } 1325 1326 static bool variant_is_int(const QVariant &in) 1327 { 1328 if (in.canConvert<QString>() && string_is_int(in.toString())) 1329 return true; 1330 return false; 1331 } 1332 1333 static QString prompt_for_string(const QString &prompt, const QString &def = QString()) 1334 { 1335 printf("%s", prompt.toLatin1().data()); 1336 fflush(stdout); 1337 QByteArray result(256, 0); 1338 if (!fgets((char *)result.data(), result.size(), stdin)) 1339 return QString(); 1340 if (result[result.length() - 1] == '\n') 1341 result.truncate(result.length() - 1); 1342 // empty input -> use default 1343 if (result.isEmpty()) 1344 return def; 1345 // trimmed input could result in an empty value, but in that case 1346 // it is treated as if the user wishes to submit an empty value. 1347 return QString::fromLocal8Bit(result).trimmed(); 1348 } 1349 1350 static int prompt_for_int(const QString &prompt, int def = 0) 1351 { 1352 while (true) { 1353 QString str = prompt_for_string(prompt); 1354 if (str.isEmpty()) 1355 return def; 1356 bool ok; 1357 int x = str.toInt(&ok); 1358 if (ok) 1359 return x; 1360 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1361 } 1362 } 1363 1364 static bool partial_compare_nocase(const QString &in, const QString &target, int min = 1) 1365 { 1366 if (in.length() >= min && in.length() <= target.length() && target.mid(0, in.length()).toLower() == in.toLower()) 1367 return true; 1368 return false; 1369 } 1370 1371 static bool prompt_for_bool(const QString &prompt, bool def = false) 1372 { 1373 while (true) { 1374 QString str = prompt_for_string(prompt); 1375 if (str.isEmpty()) 1376 return def; 1377 if (partial_compare_nocase(str, QStringLiteral("true"))) 1378 return true; 1379 else if (partial_compare_nocase(str, QStringLiteral("false"))) 1380 return false; 1381 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1382 } 1383 } 1384 1385 static bool prompt_for_yesno(const QString &prompt, bool def = false) 1386 { 1387 while (true) { 1388 QString str = prompt_for_string(prompt); 1389 if (str.isEmpty()) 1390 return def; 1391 if (partial_compare_nocase(str, QStringLiteral("yes"))) 1392 return true; 1393 else if (partial_compare_nocase(str, QStringLiteral("no"))) 1394 return false; 1395 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1396 } 1397 } 1398 1399 static QString prompt_for_slotevent_method(const QString &prompt, const QString &def = QString()) 1400 { 1401 while (true) { 1402 QString str = prompt_for_string(prompt); 1403 if (str.isEmpty()) 1404 return def; 1405 if (partial_compare_nocase(str, QStringLiteral("auto"))) 1406 return QStringLiteral("auto"); 1407 else if (partial_compare_nocase(str, QStringLiteral("trigger"))) 1408 return QStringLiteral("trigger"); 1409 else if (partial_compare_nocase(str, QStringLiteral("poll"))) 1410 return QStringLiteral("poll"); 1411 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1412 } 1413 } 1414 1415 static QVariantMap provider_config_edit_generic(const QVariantMap &in) 1416 { 1417 QVariantMap config = in; 1418 QMutableMapIterator<QString, QVariant> it(config); 1419 while (it.hasNext()) { 1420 it.next(); 1421 QString var = it.key(); 1422 if (var == QLatin1String("formtype")) 1423 continue; 1424 QVariant val = it.value(); 1425 1426 // fields must be bool, int, or string 1427 QVariant newval; 1428 QString prompt = QStringLiteral("%1: [%2] ").arg(var, val.toString()); 1429 if (variant_is_bool(val)) 1430 newval = prompt_for_bool(QStringLiteral("bool ") + prompt, val.toBool()); 1431 else if (variant_is_int(val)) 1432 newval = prompt_for_int(QStringLiteral("int ") + prompt, val.toInt()); 1433 else if (val.canConvert<QString>()) 1434 newval = prompt_for_string(QStringLiteral("string ") + prompt, val.toString()); 1435 else 1436 continue; // skip bogus fields 1437 1438 it.setValue(newval); 1439 } 1440 1441 return config; 1442 } 1443 1444 class Pkcs11ProviderConfig 1445 { 1446 public: 1447 bool allow_protected_authentication; 1448 bool cert_private; 1449 bool enabled; 1450 QString library; 1451 QString name; 1452 int private_mask; 1453 QString slotevent_method; 1454 int slotevent_timeout; 1455 1456 Pkcs11ProviderConfig() 1457 : allow_protected_authentication(true) 1458 , cert_private(false) 1459 , enabled(false) 1460 , private_mask(0) 1461 , slotevent_method(QStringLiteral("auto")) 1462 , slotevent_timeout(0) 1463 { 1464 } 1465 1466 QVariantMap toVariantMap() const 1467 { 1468 QVariantMap out; 1469 out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication; 1470 out[QStringLiteral("cert_private")] = cert_private; 1471 out[QStringLiteral("enabled")] = enabled; 1472 out[QStringLiteral("library")] = library; 1473 out[QStringLiteral("name")] = name; 1474 out[QStringLiteral("private_mask")] = private_mask; 1475 out[QStringLiteral("slotevent_method")] = slotevent_method; 1476 out[QStringLiteral("slotevent_timeout")] = slotevent_timeout; 1477 return out; 1478 } 1479 1480 bool fromVariantMap(const QVariantMap &in) 1481 { 1482 allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool(); 1483 cert_private = in[QStringLiteral("cert_private")].toBool(); 1484 enabled = in[QStringLiteral("enabled")].toBool(); 1485 library = in[QStringLiteral("library")].toString(); 1486 name = in[QStringLiteral("name")].toString(); 1487 private_mask = in[QStringLiteral("private_mask")].toInt(); 1488 slotevent_method = in[QStringLiteral("slotevent_method")].toString(); 1489 slotevent_timeout = in[QStringLiteral("slotevent_timeout")].toInt(); 1490 return true; 1491 } 1492 }; 1493 1494 class Pkcs11Config 1495 { 1496 public: 1497 bool allow_load_rootca; 1498 bool allow_protected_authentication; 1499 int log_level; 1500 int pin_cache; 1501 QList<Pkcs11ProviderConfig> providers; 1502 1503 QVariantMap orig_config; 1504 1505 Pkcs11Config() 1506 : allow_load_rootca(false) 1507 , allow_protected_authentication(true) 1508 , log_level(0) 1509 , pin_cache(-1) 1510 { 1511 } 1512 1513 QVariantMap toVariantMap() const 1514 { 1515 QVariantMap out = orig_config; 1516 1517 // form type 1518 out[QStringLiteral("formtype")] = QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0"); 1519 1520 // base settings 1521 out[QStringLiteral("allow_load_rootca")] = allow_load_rootca; 1522 out[QStringLiteral("allow_protected_authentication")] = allow_protected_authentication; 1523 out[QStringLiteral("log_level")] = log_level; 1524 out[QStringLiteral("pin_cache")] = pin_cache; 1525 1526 // provider settings (always write at least 10 providers) 1527 for (int n = 0; n < 10 || n < providers.count(); ++n) { 1528 QString prefix = QString::asprintf("provider_%02d_", n); 1529 1530 Pkcs11ProviderConfig provider; 1531 if (n < providers.count()) 1532 provider = providers[n]; 1533 1534 QVariantMap subconfig = provider.toVariantMap(); 1535 QMapIterator<QString, QVariant> it(subconfig); 1536 while (it.hasNext()) { 1537 it.next(); 1538 out.insert(prefix + it.key(), it.value()); 1539 } 1540 } 1541 1542 return out; 1543 } 1544 1545 bool fromVariantMap(const QVariantMap &in) 1546 { 1547 if (in[QStringLiteral("formtype")] != QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0")) 1548 return false; 1549 1550 allow_load_rootca = in[QStringLiteral("allow_load_rootca")].toBool(); 1551 allow_protected_authentication = in[QStringLiteral("allow_protected_authentication")].toBool(); 1552 log_level = in[QStringLiteral("log_level")].toInt(); 1553 pin_cache = in[QStringLiteral("pin_cache")].toInt(); 1554 1555 for (int n = 0;; ++n) { 1556 QString prefix = QString::asprintf("provider_%02d_", n); 1557 1558 // collect all key/values with this prefix into a 1559 // a separate container, leaving out the prefix 1560 // from the keys. 1561 QVariantMap subconfig; 1562 QMapIterator<QString, QVariant> it(in); 1563 while (it.hasNext()) { 1564 it.next(); 1565 if (it.key().startsWith(prefix)) 1566 subconfig.insert(it.key().mid(prefix.length()), it.value()); 1567 } 1568 1569 // if there are no config items with this prefix, we're done 1570 if (subconfig.isEmpty()) 1571 break; 1572 1573 Pkcs11ProviderConfig provider; 1574 if (!provider.fromVariantMap(subconfig)) 1575 return false; 1576 1577 // skip unnamed entries 1578 if (provider.name.isEmpty()) 1579 continue; 1580 1581 // skip duplicate entries 1582 bool have_name_already = false; 1583 foreach (const Pkcs11ProviderConfig &i, providers) { 1584 if (i.name == provider.name) { 1585 have_name_already = true; 1586 break; 1587 } 1588 } 1589 if (have_name_already) 1590 continue; 1591 1592 providers += provider; 1593 } 1594 1595 orig_config = in; 1596 return true; 1597 } 1598 }; 1599 1600 static QVariantMap provider_config_edit_pkcs11(const QVariantMap &in) 1601 { 1602 Pkcs11Config config; 1603 if (!config.fromVariantMap(in)) { 1604 fprintf(stderr, "Error: unable to parse PKCS#11 provider configuration.\n"); 1605 return QVariantMap(); 1606 } 1607 1608 while (true) { 1609 printf("\n"); 1610 printf("Global settings:\n"); 1611 printf(" Allow loading of root CAs: %s\n", config.allow_load_rootca ? "Yes" : "No"); 1612 printf(" Allow protected authentication: %s\n", config.allow_protected_authentication ? "Yes" : "No"); 1613 QString str; 1614 if (config.pin_cache == -1) 1615 str = QStringLiteral("No limit"); 1616 else 1617 str = QStringLiteral("%1 seconds").arg(config.pin_cache); 1618 printf(" Maximum PIN cache time: %s\n", qPrintable(str)); 1619 printf(" Log level: %d\n", config.log_level); 1620 printf("\n"); 1621 printf("PKCS#11 modules:\n"); 1622 if (!config.providers.isEmpty()) { 1623 foreach (const Pkcs11ProviderConfig &provider, config.providers) 1624 printf(" %s\n", qPrintable(provider.name)); 1625 } else 1626 printf(" (None)\n"); 1627 printf("\n"); 1628 printf("Actions:\n"); 1629 printf(" a) Edit global settings\n"); 1630 printf(" b) Add PKCS#11 module\n"); 1631 printf(" c) Edit PKCS#11 module\n"); 1632 printf(" d) Remove PKCS#11 module\n"); 1633 printf("\n"); 1634 1635 int index; 1636 while (true) { 1637 QString str = prompt_for(QStringLiteral("Select an action, or enter to quit")); 1638 if (str.isEmpty()) { 1639 index = -1; 1640 break; 1641 } 1642 if (str.length() == 1) { 1643 index = str[0].toLatin1() - 'a'; 1644 if (index >= 0 && index < 4) 1645 break; 1646 } 1647 printf("'%s' is not a valid entry.\n\n", qPrintable(str)); 1648 } 1649 if (index == -1) 1650 break; 1651 1652 if (index == 0) { 1653 printf("\n"); 1654 1655 QString prompt; 1656 prompt = QStringLiteral("Allow loading of root CAs: [%1] ") 1657 .arg(config.allow_load_rootca ? QStringLiteral("Yes") : QStringLiteral("No")); 1658 config.allow_load_rootca = prompt_for_yesno(prompt, config.allow_load_rootca); 1659 prompt = QStringLiteral("Allow protected authentication: [%1] ") 1660 .arg(config.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No")); 1661 config.allow_protected_authentication = prompt_for_yesno(prompt, config.allow_protected_authentication); 1662 prompt = QStringLiteral("Maximum PIN cache time in seconds (-1 for no limit): [%1] ").arg(config.pin_cache); 1663 config.pin_cache = prompt_for_int(prompt, config.pin_cache); 1664 prompt = QStringLiteral("Log level: [%1] ").arg(config.log_level); 1665 config.log_level = prompt_for_int(prompt, config.log_level); 1666 } else // 1, 2, 3 1667 { 1668 int at = -1; 1669 1670 // for edit/remove, need to select provider 1671 if (index == 2 || index == 3) { 1672 printf("\nWhich PKCS#11 module?\n"); 1673 for (int n = 0; n < config.providers.count(); ++n) { 1674 const Pkcs11ProviderConfig &provider = config.providers[n]; 1675 char c = 'a' + n; 1676 printf(" %c) %s\n", c, qPrintable(provider.name)); 1677 } 1678 printf("\n"); 1679 1680 int index; 1681 while (true) { 1682 QString str = prompt_for(QStringLiteral("Select a module, or enter to go back")); 1683 if (str.isEmpty()) { 1684 index = -1; 1685 break; 1686 } 1687 if (str.length() == 1) { 1688 index = str[0].toLatin1() - 'a'; 1689 if (index >= 0 && index < config.providers.count()) 1690 break; 1691 } 1692 printf("'%s' is not a valid entry.\n", qPrintable(str)); 1693 } 1694 1695 // exit? 1696 if (index == -1) 1697 continue; 1698 1699 at = index; 1700 } 1701 1702 // edit the entry 1703 if (index == 1 || index == 2) { 1704 Pkcs11ProviderConfig provider; 1705 if (index == 2) // edit 1706 provider = config.providers[at]; 1707 provider.enabled = true; 1708 printf("\n"); 1709 1710 QString prompt; 1711 1712 // prompt for unique name 1713 while (true) { 1714 if (index == 1) 1715 prompt = QStringLiteral("Unique friendly name: "); 1716 else 1717 prompt = QStringLiteral("Unique friendly name: [%1] ").arg(provider.name); 1718 provider.name = prompt_for_string(prompt, provider.name); 1719 1720 if (provider.name.isEmpty()) { 1721 printf("The friendly name cannot be blank.\n\n"); 1722 continue; 1723 } 1724 1725 bool have_name_already = false; 1726 for (int n = 0; n < config.providers.count(); ++n) { 1727 const Pkcs11ProviderConfig &i = config.providers[n]; 1728 1729 // skip checking against the entry we are editing 1730 if (at != -1 && n == at) 1731 continue; 1732 1733 if (i.name == provider.name) { 1734 have_name_already = true; 1735 break; 1736 } 1737 } 1738 if (have_name_already) { 1739 printf("This name is already used by another module.\n\n"); 1740 continue; 1741 } 1742 1743 break; 1744 } 1745 1746 // prompt for library file 1747 QString last; 1748 while (true) { 1749 if (index == 1) 1750 prompt = QStringLiteral("Library filename: "); 1751 else 1752 prompt = QStringLiteral("Library filename: [%1] ").arg(provider.library); 1753 provider.library = prompt_for_string(prompt, provider.library); 1754 1755 if (provider.library.isEmpty()) { 1756 printf("The library filename cannot be blank.\n\n"); 1757 continue; 1758 } 1759 1760 if (last != provider.library && !QFile::exists(provider.library)) { 1761 last = provider.library; 1762 printf("'%s' does not exist.\nPress enter again if you really want this.\n\n", 1763 qPrintable(provider.library)); 1764 continue; 1765 } 1766 1767 break; 1768 } 1769 1770 prompt = 1771 QStringLiteral("Allow protected authentication: [%1] ") 1772 .arg(provider.allow_protected_authentication ? QStringLiteral("Yes") : QStringLiteral("No")); 1773 provider.allow_protected_authentication = 1774 prompt_for_yesno(prompt, provider.allow_protected_authentication); 1775 prompt = QStringLiteral("Provider stores certificates as private objects: [%1] ") 1776 .arg(provider.cert_private ? QStringLiteral("Yes") : QStringLiteral("No")); 1777 provider.cert_private = prompt_for_yesno(prompt, provider.cert_private); 1778 printf("\n"); 1779 printf("Provider private key mask:\n"); 1780 printf(" 0 Determine automatically.\n"); 1781 printf(" 1 Use sign.\n"); 1782 printf(" 2 Use sign recover.\n"); 1783 printf(" 4 Use decrypt.\n"); 1784 printf(" 8 Use unwrap.\n"); 1785 prompt = QStringLiteral("Mask value: [%1] ").arg(provider.private_mask); 1786 provider.private_mask = prompt_for_int(prompt, provider.private_mask); 1787 printf("\n"); 1788 printf("Slot event method:\n"); 1789 printf(" auto Determine automatically.\n"); 1790 printf(" trigger Use trigger.\n"); 1791 printf(" poll Use poll.\n"); 1792 prompt = QStringLiteral("Method value: [%1] ").arg(provider.slotevent_method); 1793 provider.slotevent_method = prompt_for_slotevent_method(prompt, provider.slotevent_method); 1794 if (provider.slotevent_method == QLatin1String("poll")) { 1795 prompt = 1796 QStringLiteral("Poll timeout (0 for no preference): [%1] ").arg(provider.slotevent_timeout); 1797 provider.slotevent_timeout = prompt_for_int(prompt, provider.slotevent_timeout); 1798 } else 1799 provider.slotevent_timeout = 0; 1800 1801 if (index == 1) 1802 config.providers += provider; 1803 else // 2 1804 config.providers[at] = provider; 1805 } 1806 // remove the entry 1807 else // 3 1808 { 1809 config.providers.removeAt(at); 1810 } 1811 } 1812 } 1813 1814 return config.toVariantMap(); 1815 } 1816 1817 static QVariantMap provider_config_edit(const QVariantMap &in) 1818 { 1819 // see if we have a configurator for a known form type 1820 if (in[QStringLiteral("formtype")] == QLatin1String("http://affinix.com/qca/forms/qca-pkcs11#1.0")) 1821 return provider_config_edit_pkcs11(in); 1822 1823 // otherwise, use the generic configurator 1824 return provider_config_edit_generic(in); 1825 } 1826 1827 static QString get_fingerprint(const QCA::Certificate &cert, const QString &hashType) 1828 { 1829 QString hex = QCA::Hash(hashType).hashToString(cert.toDER()); 1830 QString out; 1831 for (int n = 0; n < hex.count(); ++n) { 1832 if (n != 0 && n % 2 == 0) 1833 out += QLatin1Char(':'); 1834 out += hex[n]; 1835 } 1836 return out; 1837 } 1838 1839 static QString kstype_to_string(QCA::KeyStore::Type _type) 1840 { 1841 QString type; 1842 switch (_type) { 1843 case QCA::KeyStore::System: 1844 type = QStringLiteral("Sys "); 1845 break; 1846 case QCA::KeyStore::User: 1847 type = QStringLiteral("User"); 1848 break; 1849 case QCA::KeyStore::Application: 1850 type = QStringLiteral("App "); 1851 break; 1852 case QCA::KeyStore::SmartCard: 1853 type = QStringLiteral("Card"); 1854 break; 1855 case QCA::KeyStore::PGPKeyring: 1856 type = QStringLiteral("PGP "); 1857 break; 1858 default: 1859 type = QStringLiteral("XXXX"); 1860 break; 1861 } 1862 return type; 1863 } 1864 1865 static QString ksentrytype_to_string(QCA::KeyStoreEntry::Type _type) 1866 { 1867 QString type; 1868 switch (_type) { 1869 case QCA::KeyStoreEntry::TypeKeyBundle: 1870 type = QStringLiteral("Key "); 1871 break; 1872 case QCA::KeyStoreEntry::TypeCertificate: 1873 type = QStringLiteral("Cert"); 1874 break; 1875 case QCA::KeyStoreEntry::TypeCRL: 1876 type = QStringLiteral("CRL "); 1877 break; 1878 case QCA::KeyStoreEntry::TypePGPSecretKey: 1879 type = QStringLiteral("PSec"); 1880 break; 1881 case QCA::KeyStoreEntry::TypePGPPublicKey: 1882 type = QStringLiteral("PPub"); 1883 break; 1884 default: 1885 type = QStringLiteral("XXXX"); 1886 break; 1887 } 1888 return type; 1889 } 1890 1891 static void try_print_info(const char *name, const QStringList &values) 1892 { 1893 if (!values.isEmpty()) { 1894 QString value = values.join(QStringLiteral(", ")); 1895 printf(" %s: %s\n", name, value.toUtf8().data()); 1896 } 1897 } 1898 1899 static void print_info(const char *title, const QCA::CertificateInfo &info) 1900 { 1901 QList<InfoType> list = makeInfoTypeList(); 1902 printf("%s\n", title); 1903 foreach (const InfoType &t, list) 1904 try_print_info(qPrintable(t.name), info.values(t.type)); 1905 } 1906 1907 static void print_info_ordered(const char *title, const QCA::CertificateInfoOrdered &info) 1908 { 1909 QList<InfoType> list = makeInfoTypeList(true); 1910 printf("%s\n", title); 1911 foreach (const QCA::CertificateInfoPair &pair, info) { 1912 QCA::CertificateInfoType type = pair.type(); 1913 QString name; 1914 int at = -1; 1915 for (int n = 0; n < list.count(); ++n) { 1916 if (list[n].type == type) { 1917 at = n; 1918 break; 1919 } 1920 } 1921 1922 // known type? 1923 if (at != -1) { 1924 name = list[at].name; 1925 } else { 1926 if (pair.type().section() == QCA::CertificateInfoType::DN) 1927 name = QStringLiteral("DN:") + pair.type().id(); 1928 else 1929 name = QStringLiteral("AN:") + pair.type().id(); 1930 } 1931 1932 printf(" %s: %s\n", qPrintable(name), pair.value().toUtf8().data()); 1933 } 1934 } 1935 1936 static QString constraint_to_string(const QCA::ConstraintType &t) 1937 { 1938 QList<MyConstraintType> list = makeConstraintTypeList(); 1939 for (int n = 0; n < list.count(); ++n) { 1940 if (list[n].type == t) 1941 return list[n].name; 1942 } 1943 return t.id(); 1944 } 1945 1946 static QString sigalgo_to_string(QCA::SignatureAlgorithm algo) 1947 { 1948 QString str; 1949 switch (algo) { 1950 case QCA::EMSA1_SHA1: 1951 str = QStringLiteral("EMSA1(SHA1)"); 1952 break; 1953 case QCA::EMSA3_SHA1: 1954 str = QStringLiteral("EMSA3(SHA1)"); 1955 break; 1956 case QCA::EMSA3_MD5: 1957 str = QStringLiteral("EMSA3(MD5)"); 1958 break; 1959 case QCA::EMSA3_MD2: 1960 str = QStringLiteral("EMSA3(MD2)"); 1961 break; 1962 case QCA::EMSA3_RIPEMD160: 1963 str = QStringLiteral("EMSA3(RIPEMD160)"); 1964 break; 1965 case QCA::EMSA3_Raw: 1966 str = QStringLiteral("EMSA3(raw)"); 1967 break; 1968 default: 1969 str = QStringLiteral("Unknown"); 1970 break; 1971 } 1972 return str; 1973 } 1974 1975 static void print_cert(const QCA::Certificate &cert, bool ordered = false) 1976 { 1977 printf("Serial Number: %s\n", qPrintable(cert.serialNumber().toString())); 1978 1979 if (ordered) { 1980 print_info_ordered("Subject", cert.subjectInfoOrdered()); 1981 print_info_ordered("Issuer", cert.issuerInfoOrdered()); 1982 } else { 1983 print_info("Subject", cert.subjectInfo()); 1984 print_info("Issuer", cert.issuerInfo()); 1985 } 1986 1987 printf("Validity\n"); 1988 printf(" Not before: %s\n", qPrintable(cert.notValidBefore().toString())); 1989 printf(" Not after: %s\n", qPrintable(cert.notValidAfter().toString())); 1990 1991 printf("Constraints\n"); 1992 QCA::Constraints constraints = cert.constraints(); 1993 int n; 1994 if (!constraints.isEmpty()) { 1995 for (n = 0; n < constraints.count(); ++n) 1996 printf(" %s\n", qPrintable(constraint_to_string(constraints[n]))); 1997 } else 1998 printf(" No constraints\n"); 1999 2000 printf("Policies\n"); 2001 QStringList policies = cert.policies(); 2002 if (!policies.isEmpty()) { 2003 for (n = 0; n < policies.count(); ++n) 2004 printf(" %s\n", qPrintable(policies[n])); 2005 } else 2006 printf(" No policies\n"); 2007 2008 QByteArray id; 2009 printf("Issuer Key ID: "); 2010 id = cert.issuerKeyId(); 2011 if (!id.isEmpty()) 2012 printf("%s\n", qPrintable(QCA::arrayToHex(id))); 2013 else 2014 printf("None\n"); 2015 2016 printf("Subject Key ID: "); 2017 id = cert.subjectKeyId(); 2018 if (!id.isEmpty()) 2019 printf("%s\n", qPrintable(QCA::arrayToHex(id))); 2020 else 2021 printf("None\n"); 2022 2023 printf("CA: %s\n", cert.isCA() ? "Yes" : "No"); 2024 printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm()))); 2025 2026 QCA::PublicKey key = cert.subjectPublicKey(); 2027 printf("Public Key:\n%s", key.toPEM().toLatin1().data()); 2028 2029 printf("SHA1 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("sha1")))); 2030 printf("MD5 Fingerprint: %s\n", qPrintable(get_fingerprint(cert, QStringLiteral("md5")))); 2031 } 2032 2033 static void print_certreq(const QCA::CertificateRequest &cert, bool ordered = false) 2034 { 2035 if (ordered) 2036 print_info_ordered("Subject", cert.subjectInfoOrdered()); 2037 else 2038 print_info("Subject", cert.subjectInfo()); 2039 2040 printf("Constraints\n"); 2041 QCA::Constraints constraints = cert.constraints(); 2042 int n; 2043 if (!constraints.isEmpty()) { 2044 for (n = 0; n < constraints.count(); ++n) 2045 printf(" %s\n", qPrintable(constraint_to_string(constraints[n]))); 2046 } else 2047 printf(" No constraints\n"); 2048 2049 printf("Policies\n"); 2050 QStringList policies = cert.policies(); 2051 if (!policies.isEmpty()) { 2052 for (n = 0; n < policies.count(); ++n) 2053 printf(" %s\n", qPrintable(policies[n])); 2054 } else 2055 printf(" No policies\n"); 2056 2057 printf("CA: %s\n", cert.isCA() ? "Yes" : "No"); 2058 printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(cert.signatureAlgorithm()))); 2059 2060 QCA::PublicKey key = cert.subjectPublicKey(); 2061 printf("Public Key:\n%s", key.toPEM().toLatin1().data()); 2062 } 2063 2064 static void print_crl(const QCA::CRL &crl, bool ordered = false) 2065 { 2066 if (ordered) 2067 print_info_ordered("Issuer", crl.issuerInfoOrdered()); 2068 else 2069 print_info("Issuer", crl.issuerInfo()); 2070 2071 int num = crl.number(); 2072 if (num != -1) 2073 printf("Number: %d\n", num); 2074 2075 printf("Validity\n"); 2076 printf(" This update: %s\n", qPrintable(crl.thisUpdate().toString())); 2077 printf(" Next update: %s\n", qPrintable(crl.nextUpdate().toString())); 2078 2079 QByteArray id; 2080 printf("Issuer Key ID: "); 2081 id = crl.issuerKeyId(); 2082 if (!id.isEmpty()) 2083 printf("%s\n", qPrintable(QCA::arrayToHex(id))); 2084 else 2085 printf("None\n"); 2086 2087 printf("Signature Algorithm: %s\n", qPrintable(sigalgo_to_string(crl.signatureAlgorithm()))); 2088 2089 QList<QCA::CRLEntry> revokedList = crl.revoked(); 2090 foreach (const QCA::CRLEntry &entry, revokedList) { 2091 printf(" %s: %s, %s\n", 2092 qPrintable(entry.serialNumber().toString()), 2093 crlEntryReasonToString(entry.reason()), 2094 qPrintable(entry.time().toString())); 2095 } 2096 } 2097 2098 static QString format_pgp_fingerprint(const QString &in) 2099 { 2100 QString out; 2101 bool first = true; 2102 for (int n = 0; n + 3 < in.length(); n += 4) { 2103 if (!first) 2104 out += QLatin1Char(' '); 2105 else 2106 first = false; 2107 out += in.mid(n, 4).toUpper(); 2108 } 2109 return out; 2110 } 2111 2112 static void print_pgp(const QCA::PGPKey &key) 2113 { 2114 printf("Key ID: %s\n", qPrintable(key.keyId())); 2115 printf("User IDs:\n"); 2116 foreach (const QString &s, key.userIds()) 2117 printf(" %s\n", qPrintable(s)); 2118 printf("Validity\n"); 2119 printf(" Not before: %s\n", qPrintable(key.creationDate().toString())); 2120 if (!key.expirationDate().isNull()) 2121 printf(" Not after: %s\n", qPrintable(key.expirationDate().toString())); 2122 else 2123 printf(" Not after: (no expiration)\n"); 2124 printf("In Keyring: %s\n", key.inKeyring() ? "Yes" : "No"); 2125 printf("Secret Key: %s\n", key.isSecret() ? "Yes" : "No"); 2126 printf("Trusted: %s\n", key.isTrusted() ? "Yes" : "No"); 2127 printf("Fingerprint: %s\n", qPrintable(format_pgp_fingerprint(key.fingerprint()))); 2128 } 2129 2130 static QString validityToString(QCA::Validity v) 2131 { 2132 QString s; 2133 switch (v) { 2134 case QCA::ValidityGood: 2135 s = QStringLiteral("Validated"); 2136 break; 2137 case QCA::ErrorRejected: 2138 s = QStringLiteral("Root CA is marked to reject the specified purpose"); 2139 break; 2140 case QCA::ErrorUntrusted: 2141 s = QStringLiteral("Certificate not trusted for the required purpose"); 2142 break; 2143 case QCA::ErrorSignatureFailed: 2144 s = QStringLiteral("Invalid signature"); 2145 break; 2146 case QCA::ErrorInvalidCA: 2147 s = QStringLiteral("Invalid CA certificate"); 2148 break; 2149 case QCA::ErrorInvalidPurpose: 2150 s = QStringLiteral("Invalid certificate purpose"); 2151 break; 2152 case QCA::ErrorSelfSigned: 2153 s = QStringLiteral("Certificate is self-signed"); 2154 break; 2155 case QCA::ErrorRevoked: 2156 s = QStringLiteral("Certificate has been revoked"); 2157 break; 2158 case QCA::ErrorPathLengthExceeded: 2159 s = QStringLiteral("Maximum certificate chain length exceeded"); 2160 break; 2161 case QCA::ErrorExpired: 2162 s = QStringLiteral("Certificate has expired"); 2163 break; 2164 case QCA::ErrorExpiredCA: 2165 s = QStringLiteral("CA has expired"); 2166 break; 2167 case QCA::ErrorValidityUnknown: 2168 default: 2169 s = QStringLiteral("General certificate validation error"); 2170 break; 2171 } 2172 return s; 2173 } 2174 2175 static QString smIdentityResultToString(QCA::SecureMessageSignature::IdentityResult r) 2176 { 2177 QString str; 2178 switch (r) { 2179 case QCA::SecureMessageSignature::Valid: 2180 str = QStringLiteral("Valid"); 2181 break; 2182 case QCA::SecureMessageSignature::InvalidSignature: 2183 str = QStringLiteral("InvalidSignature"); 2184 break; 2185 case QCA::SecureMessageSignature::InvalidKey: 2186 str = QStringLiteral("InvalidKey"); 2187 break; 2188 case QCA::SecureMessageSignature::NoKey: 2189 str = QStringLiteral("NoKey"); 2190 break; 2191 default: 2192 str = QStringLiteral("Unknown"); 2193 } 2194 return str; 2195 } 2196 2197 static QString smErrorToString(QCA::SecureMessage::Error e) 2198 { 2199 QMap<QCA::SecureMessage::Error, QString> map; 2200 map[QCA::SecureMessage::ErrorPassphrase] = QStringLiteral("ErrorPassphrase"); 2201 map[QCA::SecureMessage::ErrorFormat] = QStringLiteral("ErrorFormat"); 2202 map[QCA::SecureMessage::ErrorSignerExpired] = QStringLiteral("ErrorSignerExpired"); 2203 map[QCA::SecureMessage::ErrorSignerInvalid] = QStringLiteral("ErrorSignerInvalid"); 2204 map[QCA::SecureMessage::ErrorEncryptExpired] = QStringLiteral("ErrorEncryptExpired"); 2205 map[QCA::SecureMessage::ErrorEncryptUntrusted] = QStringLiteral("ErrorEncryptUntrusted"); 2206 map[QCA::SecureMessage::ErrorEncryptInvalid] = QStringLiteral("ErrorEncryptInvalid"); 2207 map[QCA::SecureMessage::ErrorNeedCard] = QStringLiteral("ErrorNeedCard"); 2208 map[QCA::SecureMessage::ErrorCertKeyMismatch] = QStringLiteral("ErrorCertKeyMismatch"); 2209 map[QCA::SecureMessage::ErrorUnknown] = QStringLiteral("ErrorUnknown"); 2210 return map[e]; 2211 } 2212 2213 static void smDisplaySignatures(const QList<QCA::SecureMessageSignature> &signers) 2214 { 2215 foreach (const QCA::SecureMessageSignature &signer, signers) { 2216 QCA::SecureMessageSignature::IdentityResult r = signer.identityResult(); 2217 fprintf(stderr, "IdentityResult: %s\n", qPrintable(smIdentityResultToString(r))); 2218 2219 QCA::SecureMessageKey key = signer.key(); 2220 if (!key.isNull()) { 2221 if (key.type() == QCA::SecureMessageKey::PGP) { 2222 QCA::PGPKey pub = key.pgpPublicKey(); 2223 fprintf(stderr, "From: %s (%s)\n", qPrintable(pub.primaryUserId()), qPrintable(pub.keyId())); 2224 } else { 2225 QCA::Certificate cert = key.x509CertificateChain().primary(); 2226 QString emailStr; 2227 QCA::CertificateInfo info = cert.subjectInfo(); 2228 if (info.contains(QCA::Email)) 2229 emailStr = QStringLiteral(" (%1)").arg(info.value(QCA::Email)); 2230 fprintf(stderr, "From: %s%s\n", qPrintable(cert.commonName()), qPrintable(emailStr)); 2231 } 2232 } 2233 } 2234 } 2235 2236 static const char *mime_signpart = 2237 "Content-Type: text/plain; charset=UTF-8\r\n" 2238 "Content-Transfer-Encoding: 8bit\r\n" 2239 "\r\n" 2240 "%1"; 2241 2242 static const char *mime_signed = 2243 "Content-Type: multipart/signed;\r\n" 2244 " micalg=%1;\r\n" 2245 " boundary=QCATOOL-0001;\r\n" 2246 " protocol=\"application/pkcs7-signature\"\r\n" 2247 "\r\n" 2248 "\r\n" 2249 "--QCATOOL-0001\r\n" 2250 "%2\r\n" 2251 "--QCATOOL-0001\r\n" 2252 "Content-Transfer-Encoding: base64\r\n" 2253 "Content-Type: application/pkcs7-signature;\r\n" 2254 " name=smime.p7s\r\n" 2255 "Content-Disposition: attachment;\r\n" 2256 " filename=smime.p7s\r\n" 2257 "\r\n" 2258 "%3\r\n" 2259 "\r\n" 2260 "--QCATOOL-0001--\r\n"; 2261 2262 static const char *mime_enveloped = 2263 "Mime-Version: 1.0\r\n" 2264 "Content-Transfer-Encoding: base64\r\n" 2265 "Content-Type: application/pkcs7-mime;\r\n" 2266 " name=smime.p7m;\r\n" 2267 " smime-type=enveloped-data\r\n" 2268 "Content-Disposition: attachment;\r\n" 2269 " filename=smime.p7m\r\n" 2270 "\r\n" 2271 "%1\r\n"; 2272 2273 static QString add_cr(const QString &in) 2274 { 2275 QString out = in; 2276 int at = 0; 2277 while (true) { 2278 at = out.indexOf(QLatin1Char('\n'), at); 2279 if (at == -1) 2280 break; 2281 if (at - 1 >= 0 && out[at - 1] != QLatin1Char('\r')) { 2282 out.insert(at, QLatin1Char('\r')); 2283 ++at; 2284 } 2285 ++at; 2286 } 2287 return out; 2288 } 2289 2290 static QString rem_cr(const QString &in) 2291 { 2292 QString out = in; 2293 out.replace(QLatin1String("\r\n"), QLatin1String("\n")); 2294 return out; 2295 } 2296 2297 static int indexOf_newline(const QString &in, int offset = 0) 2298 { 2299 for (int n = offset; n < in.length(); ++n) { 2300 if (n + 1 < in.length() && in[n] == QLatin1Char('\r') && in[n + 1] == QLatin1Char('\n')) 2301 return n; 2302 if (in[n] == QLatin1Char('\n')) 2303 return n; 2304 } 2305 return -1; 2306 } 2307 2308 static int indexOf_doublenewline(const QString &in, int offset = 0) 2309 { 2310 int at = -1; 2311 while (true) { 2312 int n = indexOf_newline(in, offset); 2313 if (n == -1) 2314 return -1; 2315 2316 if (at != -1) { 2317 if (n == offset) 2318 break; 2319 } 2320 2321 at = n; 2322 if (in[n] == QLatin1Char('\n')) 2323 offset = n + 1; 2324 else 2325 offset = n + 2; 2326 } 2327 return at; 2328 } 2329 2330 // this is so gross 2331 static int newline_len(const QString &in, int offset = 0) 2332 { 2333 if (in[offset] == QLatin1Char('\r')) 2334 return 2; 2335 else 2336 return 1; 2337 } 2338 2339 // all of this mime stuff is a total hack 2340 static QString open_mime_envelope(const QString &in) 2341 { 2342 int n = indexOf_doublenewline(in); 2343 if (n == -1) 2344 return QString(); 2345 return in.mid(n + (newline_len(in, n) * 2)); // good lord 2346 } 2347 2348 static bool open_mime_data_sig(const QString &in, QString *data, QString *sig) 2349 { 2350 int n = in.indexOf(QLatin1String("boundary=")); 2351 if (n == -1) 2352 return false; 2353 n += 9; 2354 int i = indexOf_newline(in, n); 2355 if (i == -1) 2356 return false; 2357 QString boundary; 2358 QString bregion = in.mid(n, i - n); 2359 n = bregion.indexOf(QLatin1Char(';')); 2360 if (n != -1) 2361 boundary = bregion.mid(0, n); 2362 else 2363 boundary = bregion; 2364 2365 if (boundary[0] == QLatin1Char('\"')) 2366 boundary.remove(0, 1); 2367 if (boundary[boundary.length() - 1] == QLatin1Char('\"')) 2368 boundary.remove(boundary.length() - 1, 1); 2369 // printf("boundary: [%s]\n", qPrintable(boundary)); 2370 QString boundary_end = QStringLiteral("--") + boundary; 2371 boundary = QStringLiteral("--") + boundary; 2372 2373 QString work = open_mime_envelope(in); 2374 // printf("work: [%s]\n", qPrintable(work)); 2375 2376 n = work.indexOf(boundary); 2377 if (n == -1) 2378 return false; 2379 n += boundary.length(); 2380 i = indexOf_newline(work, n); 2381 if (i == -1) 2382 return false; 2383 n += newline_len(work, i); 2384 int data_start = n; 2385 2386 n = work.indexOf(boundary, data_start); 2387 if (n == -1) 2388 return false; 2389 int data_end = n; 2390 2391 n = data_end + boundary.length(); 2392 i = indexOf_newline(work, n); 2393 if (i == -1) 2394 return false; 2395 n += newline_len(work, i); 2396 int next = n; 2397 2398 QString tmp_data = work.mid(data_start, data_end - data_start); 2399 n = work.indexOf(boundary_end, next); 2400 if (n == -1) 2401 return false; 2402 QString tmp_sig = work.mid(next, n - next); 2403 2404 // nuke some newlines 2405 if (tmp_data.right(2) == QLatin1String("\r\n")) 2406 tmp_data.truncate(tmp_data.length() - 2); 2407 else if (tmp_data.right(1) == QLatin1String("\n")) 2408 tmp_data.truncate(tmp_data.length() - 1); 2409 if (tmp_sig.right(2) == QLatin1String("\r\n")) 2410 tmp_sig.truncate(tmp_sig.length() - 2); 2411 else if (tmp_sig.right(1) == QLatin1String("\n")) 2412 tmp_sig.truncate(tmp_sig.length() - 1); 2413 2414 tmp_sig = open_mime_envelope(tmp_sig); 2415 2416 *data = tmp_data; 2417 *sig = tmp_sig; 2418 return true; 2419 } 2420 2421 static QString idHash(const QString &id) 2422 { 2423 // hash the id and take the rightmost 4 hex characters 2424 return QCA::Hash(QStringLiteral("md5")).hashToString(id.toUtf8()).right(4); 2425 } 2426 2427 // first = ids, second = names 2428 static QPair<QStringList, QStringList> getKeyStoreStrings(const QStringList &list, QCA::KeyStoreManager *ksm) 2429 { 2430 QPair<QStringList, QStringList> out; 2431 for (int n = 0; n < list.count(); ++n) { 2432 QCA::KeyStore ks(list[n], ksm); 2433 out.first.append(idHash(ks.id())); 2434 out.second.append(ks.name()); 2435 } 2436 return out; 2437 } 2438 2439 static QPair<QStringList, QStringList> getKeyStoreEntryStrings(const QList<QCA::KeyStoreEntry> &list) 2440 { 2441 QPair<QStringList, QStringList> out; 2442 for (int n = 0; n < list.count(); ++n) { 2443 out.first.append(idHash(list[n].id())); 2444 out.second.append(list[n].name()); 2445 } 2446 return out; 2447 } 2448 2449 static QList<int> getPartialMatches(const QStringList &list, const QString &str) 2450 { 2451 QList<int> out; 2452 for (int n = 0; n < list.count(); ++n) { 2453 if (list[n].contains(str, Qt::CaseInsensitive)) 2454 out += n; 2455 } 2456 return out; 2457 } 2458 2459 static int findByString(const QPair<QStringList, QStringList> &in, const QString &str) 2460 { 2461 // exact id match 2462 int n = in.first.indexOf(str); 2463 if (n != -1) 2464 return n; 2465 2466 // partial id match 2467 QList<int> ret = getPartialMatches(in.first, str); 2468 if (!ret.isEmpty()) 2469 return ret.first(); 2470 2471 // partial name match 2472 ret = getPartialMatches(in.second, str); 2473 if (!ret.isEmpty()) 2474 return ret.first(); 2475 2476 return -1; 2477 } 2478 2479 static QString getKeyStore(const QString &name) 2480 { 2481 QCA::KeyStoreManager ksm; 2482 QStringList storeList = ksm.keyStores(); 2483 int n = findByString(getKeyStoreStrings(storeList, &ksm), name); 2484 if (n != -1) 2485 return storeList[n]; 2486 return QString(); 2487 } 2488 2489 static QCA::KeyStoreEntry getKeyStoreEntry(QCA::KeyStore *store, const QString &name) 2490 { 2491 QList<QCA::KeyStoreEntry> list = store->entryList(); 2492 int n = findByString(getKeyStoreEntryStrings(list), name); 2493 if (n != -1) 2494 return list[n]; 2495 return QCA::KeyStoreEntry(); 2496 } 2497 2498 // here are a bunch of get_Foo functions for the various types 2499 2500 // E - generic entry 2501 // K - private key 2502 // C - cert 2503 // X - keybundle 2504 // P - pgp public key 2505 // S - pgp secret key 2506 2507 // in all cases but K, the store:obj notation can be used. if there 2508 // is no colon present, then we treat the input as a filename. we 2509 // try the file as an exported passive entry id, and if the type 2510 // is C or X, we'll fall back to regular files if necessary. 2511 2512 static QCA::KeyStoreEntry get_E(const QString &name, bool nopassiveerror = false) 2513 { 2514 QCA::KeyStoreEntry entry; 2515 2516 QCA::KeyStoreManager::start(); 2517 2518 int n = name.indexOf(QLatin1Char(':')); 2519 if (n != -1) { 2520 ksm_start_and_wait(); 2521 2522 // store:obj lookup 2523 QString storeName = name.mid(0, n); 2524 QString objectName = name.mid(n + 1); 2525 2526 QCA::KeyStoreManager ksm; 2527 QCA::KeyStore store(getKeyStore(storeName), &ksm); 2528 if (!store.isValid()) { 2529 fprintf(stderr, "Error: no such store [%s].\n", qPrintable(storeName)); 2530 return entry; 2531 } 2532 2533 entry = getKeyStoreEntry(&store, objectName); 2534 if (entry.isNull()) { 2535 fprintf(stderr, "Error: no such object [%s].\n", qPrintable(objectName)); 2536 return entry; 2537 } 2538 } else { 2539 // exported id 2540 QString serialized = read_ksentry_file(name); 2541 entry = QCA::KeyStoreEntry(serialized); 2542 if (entry.isNull()) { 2543 if (!nopassiveerror) 2544 fprintf(stderr, "Error: invalid/unknown entry [%s].\n", qPrintable(name)); 2545 return entry; 2546 } 2547 } 2548 2549 return entry; 2550 } 2551 2552 static QCA::PrivateKey get_K(const QString &name) 2553 { 2554 QCA::PrivateKey key; 2555 2556 int n = name.indexOf(QLatin1Char(':')); 2557 if (n != -1) { 2558 fprintf(stderr, "Error: cannot use store:obj notation for raw private keys.\n"); 2559 return key; 2560 } 2561 2562 if (is_pem_file(name)) 2563 key = QCA::PrivateKey::fromPEMFile(name); 2564 else 2565 key = QCA::PrivateKey::fromDER(read_der_file(name)); 2566 if (key.isNull()) { 2567 fprintf(stderr, "Error: unable to read/process private key file.\n"); 2568 return key; 2569 } 2570 2571 return key; 2572 } 2573 2574 static QCA::Certificate get_C(const QString &name) 2575 { 2576 QCA::KeyStoreEntry entry = get_E(name, true); 2577 if (!entry.isNull()) { 2578 if (entry.type() != QCA::KeyStoreEntry::TypeCertificate) { 2579 fprintf(stderr, "Error: entry is not a certificate.\n"); 2580 return QCA::Certificate(); 2581 } 2582 return entry.certificate(); 2583 } 2584 2585 if (!QCA::isSupported("cert")) { 2586 fprintf(stderr, "Error: need 'cert' feature.\n"); 2587 return QCA::Certificate(); 2588 } 2589 2590 // try file 2591 QCA::Certificate cert; 2592 if (is_pem_file(name)) 2593 cert = QCA::Certificate::fromPEMFile(name); 2594 else 2595 cert = QCA::Certificate::fromDER(read_der_file(name)); 2596 if (cert.isNull()) { 2597 fprintf(stderr, "Error: unable to read/process certificate file.\n"); 2598 return cert; 2599 } 2600 2601 return cert; 2602 } 2603 2604 static QCA::KeyBundle get_X(const QString &name) 2605 { 2606 QCA::KeyStoreEntry entry = get_E(name, true); 2607 if (!entry.isNull()) { 2608 if (entry.type() != QCA::KeyStoreEntry::TypeKeyBundle) { 2609 fprintf(stderr, "Error: entry is not a keybundle.\n"); 2610 return QCA::KeyBundle(); 2611 } 2612 return entry.keyBundle(); 2613 } 2614 2615 if (!QCA::isSupported("pkcs12")) { 2616 fprintf(stderr, "Error: need 'pkcs12' feature.\n"); 2617 return QCA::KeyBundle(); 2618 } 2619 2620 // try file 2621 QCA::KeyBundle key = QCA::KeyBundle::fromFile(name); 2622 if (key.isNull()) { 2623 fprintf(stderr, "Error: unable to read/process keybundle file.\n"); 2624 return key; 2625 } 2626 2627 return key; 2628 } 2629 2630 static QCA::PGPKey get_P(const QString &name) 2631 { 2632 QCA::KeyStoreEntry entry = get_E(name, true); 2633 if (!entry.isNull()) { 2634 if (entry.type() != QCA::KeyStoreEntry::TypePGPPublicKey && 2635 entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) { 2636 fprintf(stderr, "Error: entry is not a pgp public key.\n"); 2637 return QCA::PGPKey(); 2638 } 2639 return entry.pgpPublicKey(); 2640 } 2641 2642 // try file 2643 QCA::PGPKey key = QCA::PGPKey::fromFile(name); 2644 if (key.isNull()) { 2645 fprintf(stderr, "Error: unable to read/process pgp key file.\n"); 2646 return key; 2647 } 2648 2649 return key; 2650 } 2651 2652 static QPair<QCA::PGPKey, QCA::PGPKey> get_S(const QString &name, bool noerror = false) 2653 { 2654 QPair<QCA::PGPKey, QCA::PGPKey> key; 2655 QCA::KeyStoreEntry entry = get_E(name, true); 2656 if (!entry.isNull()) { 2657 if (entry.type() != QCA::KeyStoreEntry::TypePGPSecretKey) { 2658 if (!noerror) 2659 fprintf(stderr, "Error: entry is not a pgp secret key.\n"); 2660 return key; 2661 } 2662 2663 key.first = entry.pgpSecretKey(); 2664 key.second = entry.pgpPublicKey(); 2665 return key; 2666 } 2667 return key; 2668 } 2669 2670 static void usage() 2671 { 2672 printf("%s: simple qca utility\n", APPNAME); 2673 printf("usage: %s (options) [command]\n", EXENAME); 2674 printf(" options: --pass=x, --newpass=x, --nonroots=x, --roots=x, --nosys,\n"); 2675 printf(" --noprompt, --ordered, --debug, --log-file=x, --log-level=n,\n"); 2676 printf(" --nobundle\n"); 2677 printf("\n"); 2678 printf(" help|--help|-h This help text\n"); 2679 printf(" version|--version|-v Print version information\n"); 2680 printf(" plugins List available plugins\n"); 2681 printf(" config [command]\n"); 2682 printf(" save [provider] Save default provider config\n"); 2683 printf(" edit [provider] Edit provider config\n"); 2684 printf(" key [command]\n"); 2685 printf(" make rsa|dsa [bits] Create a key pair\n"); 2686 printf(" changepass [K] Add/change/remove passphrase of a key\n"); 2687 printf(" cert [command]\n"); 2688 printf(" makereq [K] Create certificate request (CSR)\n"); 2689 printf(" makeself [K] Create self-signed certificate\n"); 2690 printf(" makereqadv [K] Advanced version of 'makereq'\n"); 2691 printf(" makeselfadv [K] Advanced version of 'makeself'\n"); 2692 printf(" validate [C] Validate certificate\n"); 2693 printf(" keybundle [command]\n"); 2694 printf(" make [K] [C] Create a keybundle\n"); 2695 printf(" extract [X] Extract certificate(s) and key\n"); 2696 printf(" changepass [X] Change passphrase of a keybundle\n"); 2697 printf(" keystore [command]\n"); 2698 printf(" list-stores List all available keystores\n"); 2699 printf(" list [storeName] List content of a keystore\n"); 2700 printf(" monitor Monitor for keystore availability\n"); 2701 printf(" export [E] Export a keystore entry's content\n"); 2702 printf(" exportref [E] Export a keystore entry reference\n"); 2703 printf(" addkb [storeName] [cert.p12] Add a keybundle into a keystore\n"); 2704 printf(" addpgp [storeName] [key.asc] Add a PGP key into a keystore\n"); 2705 printf(" remove [E] Remove an object from a keystore\n"); 2706 printf(" show [command]\n"); 2707 printf(" cert [C] Examine a certificate\n"); 2708 printf(" req [req.pem] Examine a certificate request (CSR)\n"); 2709 printf(" crl [crl.pem] Examine a certificate revocation list\n"); 2710 printf(" kb [X] Examine a keybundle\n"); 2711 printf(" pgp [P|S] Examine a PGP key\n"); 2712 printf(" message [command]\n"); 2713 printf(" sign pgp|pgpdetach|smime [X|S] Sign a message\n"); 2714 printf(" encrypt pgp|smime [C|P] Encrypt a message\n"); 2715 printf(" signencrypt [S] [P] PGP sign & encrypt a message\n"); 2716 printf(" verify pgp|smime Verify a message\n"); 2717 printf(" decrypt pgp|smime ((X) ...) Decrypt a message (S/MIME needs X)\n"); 2718 printf(" exportcerts Export certs from S/MIME message\n"); 2719 printf("\n"); 2720 printf("Object types: K = private key, C = certificate, X = key bundle,\n"); 2721 printf(" P = PGP public key, S = PGP secret key, E = generic entry\n"); 2722 printf("\n"); 2723 printf("An object must be either a filename or a keystore reference (\"store:obj\").\n"); 2724 printf("\n"); 2725 printf("Log level is from 0 (quiet) to 8 (debug)\n"); 2726 printf("\n"); 2727 } 2728 2729 int main(int argc, char **argv) 2730 { 2731 QCA::Initializer qcaInit; 2732 QCoreApplication app(argc, argv); 2733 QFile logFile; 2734 QTextStream logStream(stderr); 2735 StreamLogger streamLogger(logStream); 2736 2737 QStringList args; 2738 for (int n = 1; n < argc; ++n) 2739 args.append(QString::fromLocal8Bit(argv[n])); 2740 2741 if (args.count() < 1) { 2742 usage(); 2743 return 1; 2744 } 2745 2746 bool have_pass = false; 2747 bool have_newpass = false; 2748 QCA::SecureArray pass, newpass; 2749 bool allowprompt = true; 2750 bool ordered = false; 2751 bool debug = false; 2752 bool nosys = false; 2753 bool nobundle = false; 2754 QString rootsFile, nonRootsFile; 2755 2756 for (int n = 0; n < args.count(); ++n) { 2757 QString s = args[n]; 2758 if (!s.startsWith(QLatin1String("--"))) 2759 continue; 2760 QString var; 2761 QString val; 2762 int x = s.indexOf(QLatin1Char('=')); 2763 if (x != -1) { 2764 var = s.mid(2, x - 2); 2765 val = s.mid(x + 1); 2766 } else { 2767 var = s.mid(2); 2768 } 2769 2770 bool known = true; 2771 2772 if (var == QLatin1String("pass")) { 2773 have_pass = true; 2774 pass = val.toUtf8(); 2775 } else if (var == QLatin1String("newpass")) { 2776 have_newpass = true; 2777 newpass = val.toUtf8(); 2778 } else if (var == QLatin1String("log-file")) { 2779 logFile.setFileName(val); 2780 logFile.open(QIODevice::Append | QIODevice::Text | QIODevice::Unbuffered); 2781 logStream.setDevice(&logFile); 2782 } else if (var == QLatin1String("log-level")) { 2783 QCA::logger()->setLevel((QCA::Logger::Severity)val.toInt()); 2784 } else if (var == QLatin1String("noprompt")) 2785 allowprompt = false; 2786 else if (var == QLatin1String("ordered")) 2787 ordered = true; 2788 else if (var == QLatin1String("debug")) 2789 debug = true; 2790 else if (var == QLatin1String("roots")) 2791 rootsFile = val; 2792 else if (var == QLatin1String("nonroots")) 2793 nonRootsFile = val; 2794 else if (var == QLatin1String("nosys")) 2795 nosys = true; 2796 else if (var == QLatin1String("nobundle")) 2797 nobundle = true; 2798 else 2799 known = false; 2800 2801 if (known) { 2802 args.removeAt(n); 2803 --n; // adjust position 2804 } 2805 } 2806 2807 // help 2808 if (args.isEmpty() || args[0] == QLatin1String("help") || args[0] == QLatin1String("--help") || 2809 args[0] == QLatin1String("-h")) { 2810 usage(); 2811 return 0; 2812 } 2813 2814 // version 2815 if (args[0] == QLatin1String("version") || args[0] == QLatin1String("--version") || 2816 args[0] == QLatin1String("-v")) { 2817 int ver = qcaVersion(); 2818 int maj = (ver >> 16) & 0xff; 2819 int min = (ver >> 8) & 0xff; 2820 int bug = ver & 0xff; 2821 printf("%s version %s by Justin Karneges\n", APPNAME, VERSION); 2822 printf("Using QCA version %d.%d.%d\n", maj, min, bug); 2823 return 0; 2824 } 2825 2826 // show plugins 2827 if (args[0] == QLatin1String("plugins")) { 2828 QStringList paths = QCA::pluginPaths(); 2829 if (!paths.isEmpty()) { 2830 for (int n = 0; n < paths.count(); ++n) { 2831 printf(" %s\n", qPrintable(QDir::toNativeSeparators(paths[n]))); 2832 } 2833 } else 2834 printf(" (none)\n"); 2835 2836 QCA::ProviderList list = QCA::providers(); 2837 2838 if (debug) 2839 output_plugin_diagnostic_text(); 2840 2841 printf("Available Providers:\n"); 2842 if (!list.isEmpty()) { 2843 for (int n = 0; n < list.count(); ++n) { 2844 printf(" %s\n", qPrintable(list[n]->name())); 2845 QString credit = list[n]->credit(); 2846 if (!credit.isEmpty()) { 2847 QStringList lines = wrapstring(credit, 74); 2848 foreach (const QString &s, lines) 2849 printf(" %s\n", qPrintable(s)); 2850 } 2851 if (debug) { 2852 QStringList capabilities = list[n]->features(); 2853 foreach (const QString &capability, capabilities) { 2854 printf(" *%s", qPrintable(capability)); 2855 if (!QCA::isSupported(qPrintable(capability), list[n]->name())) { 2856 printf("(NOT supported) - bug"); 2857 } 2858 printf("\n"); 2859 } 2860 } 2861 } 2862 } else 2863 printf(" (none)\n"); 2864 2865 QCA::unloadAllPlugins(); 2866 2867 if (debug) 2868 output_plugin_diagnostic_text(); 2869 2870 return 0; 2871 } 2872 2873 // config stuff 2874 if (args[0] == QLatin1String("config")) { 2875 if (args.count() < 2) { 2876 usage(); 2877 return 1; 2878 } 2879 2880 if (args[1] == QLatin1String("save")) { 2881 if (args.count() < 3) { 2882 usage(); 2883 return 1; 2884 } 2885 2886 QString name = args[2]; 2887 QCA::Provider *p = QCA::findProvider(name); 2888 if (!p) { 2889 fprintf(stderr, "Error: no such provider '%s'.\n", qPrintable(name)); 2890 return 1; 2891 } 2892 2893 QVariantMap map1 = p->defaultConfig(); 2894 if (map1.isEmpty()) { 2895 fprintf(stderr, "Error: provider does not support configuration.\n"); 2896 return 1; 2897 } 2898 2899 // set and save 2900 QCA::setProviderConfig(name, map1); 2901 QCA::saveProviderConfig(name); 2902 printf("Done.\n"); 2903 return 0; 2904 } else if (args[1] == QLatin1String("edit")) { 2905 if (args.count() < 3) { 2906 usage(); 2907 return 1; 2908 } 2909 2910 QString name = args[2]; 2911 if (!QCA::findProvider(name)) { 2912 fprintf(stderr, "Error: no such provider '%s'.\n", qPrintable(name)); 2913 return 1; 2914 } 2915 2916 QVariantMap map1 = QCA::getProviderConfig(name); 2917 if (map1.isEmpty()) { 2918 fprintf(stderr, "Error: provider does not support configuration.\n"); 2919 return 1; 2920 } 2921 2922 printf("Editing configuration for %s ...\n", qPrintable(name)); 2923 printf("Note: to clear a string entry, type whitespace and press enter.\n"); 2924 2925 map1 = provider_config_edit(map1); 2926 if (map1.isEmpty()) 2927 return 1; 2928 2929 // set and save 2930 QCA::setProviderConfig(name, map1); 2931 QCA::saveProviderConfig(name); 2932 printf("Done.\n"); 2933 return 0; 2934 } else { 2935 usage(); 2936 return 1; 2937 } 2938 } 2939 2940 // enable console passphrase prompt 2941 PassphrasePromptThread passphrasePrompt; 2942 if (!allowprompt) 2943 passphrasePrompt.pp->allowPrompt = false; 2944 if (have_pass) 2945 passphrasePrompt.pp->setExplicitPassword(pass); 2946 2947 if (args[0] == QLatin1String("key")) { 2948 if (args.count() < 2) { 2949 usage(); 2950 return 1; 2951 } 2952 2953 if (args[1] == QLatin1String("make")) { 2954 if (args.count() < 4) { 2955 usage(); 2956 return 1; 2957 } 2958 2959 bool genrsa; 2960 int bits; 2961 2962 if (args[2] == QLatin1String("rsa")) { 2963 if (!QCA::isSupported("rsa")) { 2964 fprintf(stderr, "Error: need 'rsa' feature.\n"); 2965 return 1; 2966 } 2967 2968 genrsa = true; 2969 bits = args[3].toInt(); 2970 if (bits < 512) { 2971 fprintf(stderr, "Error: RSA bits must be at least 512.\n"); 2972 return 1; 2973 } 2974 } else if (args[2] == QLatin1String("dsa")) { 2975 if (!QCA::isSupported("dsa")) { 2976 fprintf(stderr, "Error: need 'dsa' feature.\n"); 2977 return 1; 2978 } 2979 2980 if (!QCA::isSupported("dlgroup")) { 2981 fprintf(stderr, "Error: need 'dlgroup' feature.\n"); 2982 return 1; 2983 } 2984 2985 genrsa = false; 2986 bits = args[3].toInt(); 2987 if (bits != 512 && bits != 768 && bits != 1024) { 2988 fprintf(stderr, "Error: DSA bits must be 512, 768, or 1024.\n"); 2989 return 1; 2990 } 2991 } else { 2992 usage(); 2993 return 1; 2994 } 2995 2996 if (!allowprompt && !have_newpass) { 2997 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n"); 2998 return 1; 2999 } 3000 3001 QCA::PrivateKey priv; 3002 QString pubFileName, privFileName; 3003 3004 if (genrsa) { 3005 // note: third arg is bogus, doesn't apply to RSA 3006 priv = AnimatedKeyGen::makeKey(QCA::PKey::RSA, bits, QCA::DSA_512); 3007 pubFileName = QStringLiteral("rsapub.pem"); 3008 privFileName = QStringLiteral("rsapriv.pem"); 3009 } else // dsa 3010 { 3011 QCA::DLGroupSet set; 3012 if (bits == 512) 3013 set = QCA::DSA_512; 3014 else if (bits == 768) 3015 set = QCA::DSA_768; 3016 else // 1024 3017 set = QCA::DSA_1024; 3018 3019 // note: second arg is bogus, doesn't apply to DSA 3020 priv = AnimatedKeyGen::makeKey(QCA::PKey::DSA, 0, set); 3021 pubFileName = QStringLiteral("dsapub.pem"); 3022 privFileName = QStringLiteral("dsapriv.pem"); 3023 } 3024 3025 if (priv.isNull()) { 3026 fprintf(stderr, "Error: unable to generate key.\n"); 3027 return 1; 3028 } 3029 3030 QCA::PublicKey pub = priv.toPublicKey(); 3031 3032 // prompt for new passphrase if necessary 3033 if (!have_newpass) { 3034 while (!promptForNewPassphrase(&newpass)) { } 3035 have_newpass = true; 3036 } 3037 3038 if (pub.toPEMFile(pubFileName)) 3039 printf("Public key saved to %s\n", qPrintable(pubFileName)); 3040 else { 3041 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(pubFileName)); 3042 return 1; 3043 } 3044 3045 bool ok; 3046 if (!newpass.isEmpty()) 3047 ok = priv.toPEMFile(privFileName, newpass); 3048 else 3049 ok = priv.toPEMFile(privFileName); 3050 if (ok) 3051 printf("Private key saved to %s\n", qPrintable(privFileName)); 3052 else { 3053 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(privFileName)); 3054 return 1; 3055 } 3056 } else if (args[1] == QLatin1String("changepass")) { 3057 if (args.count() < 3) { 3058 usage(); 3059 return 1; 3060 } 3061 3062 QCA::PrivateKey priv = get_K(args[2]); 3063 if (priv.isNull()) 3064 return 1; 3065 3066 if (!allowprompt && !have_newpass) { 3067 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n"); 3068 return 1; 3069 } 3070 3071 // prompt for new passphrase if necessary 3072 if (!have_newpass) { 3073 while (!promptForNewPassphrase(&newpass)) { } 3074 have_newpass = true; 3075 } 3076 3077 QString out; 3078 if (!newpass.isEmpty()) 3079 out = priv.toPEM(newpass); 3080 else 3081 out = priv.toPEM(); 3082 if (!out.isEmpty()) 3083 printf("%s", qPrintable(out)); 3084 else { 3085 fprintf(stderr, "Error: can't encode key.\n"); 3086 return 1; 3087 } 3088 } else { 3089 usage(); 3090 return 1; 3091 } 3092 } else if (args[0] == QLatin1String("cert")) { 3093 if (args.count() < 2) { 3094 usage(); 3095 return 1; 3096 } 3097 3098 if (args[1] == QLatin1String("makereq") || args[1] == QLatin1String("makereqadv")) { 3099 if (args.count() < 3) { 3100 usage(); 3101 return 1; 3102 } 3103 3104 if (!QCA::isSupported("csr")) { 3105 fprintf(stderr, "Error: need 'csr' feature.\n"); 3106 return 1; 3107 } 3108 3109 QCA::PrivateKey priv = get_K(args[2]); 3110 if (priv.isNull()) 3111 return 1; 3112 3113 printf("\n"); 3114 3115 bool advanced = (args[1] == QLatin1String("makereqadv")) ? true : false; 3116 3117 QCA::CertificateOptions opts = promptForCertAttributes(advanced, true); 3118 QCA::CertificateRequest req(opts, priv); 3119 3120 QString reqname = QStringLiteral("certreq.pem"); 3121 if (req.toPEMFile(reqname)) 3122 printf("Certificate request saved to %s\n", qPrintable(reqname)); 3123 else { 3124 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(reqname)); 3125 return 1; 3126 } 3127 } else if (args[1] == QLatin1String("makeself") || args[1] == QLatin1String("makeselfadv")) { 3128 if (args.count() < 3) { 3129 usage(); 3130 return 1; 3131 } 3132 3133 if (!QCA::isSupported("cert")) { 3134 fprintf(stderr, "Error: need 'cert' feature.\n"); 3135 return 1; 3136 } 3137 3138 QCA::PrivateKey priv = get_K(args[2]); 3139 if (priv.isNull()) 3140 return 1; 3141 3142 printf("\n"); 3143 3144 bool advanced = (args[1] == QLatin1String("makeselfadv")) ? true : false; 3145 3146 QCA::CertificateOptions opts = promptForCertAttributes(advanced, false); 3147 QCA::Certificate cert(opts, priv); 3148 3149 QString certname = QStringLiteral("cert.pem"); 3150 if (cert.toPEMFile(certname)) 3151 printf("Certificate saved to %s\n", qPrintable(certname)); 3152 else { 3153 fprintf(stderr, "Error: can't encode/write %s\n", qPrintable(certname)); 3154 return 1; 3155 } 3156 } else if (args[1] == QLatin1String("validate")) { 3157 if (args.count() < 3) { 3158 usage(); 3159 return 1; 3160 } 3161 3162 QCA::Certificate target = get_C(args[2]); 3163 if (target.isNull()) 3164 return 1; 3165 3166 // get roots 3167 QCA::CertificateCollection roots; 3168 if (!nosys) 3169 roots += QCA::systemStore(); 3170 if (!rootsFile.isEmpty()) 3171 roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile); 3172 3173 // get nonroots 3174 QCA::CertificateCollection nonroots; 3175 if (!nonRootsFile.isEmpty()) 3176 nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile); 3177 3178 QCA::Validity v = target.validate(roots, nonroots); 3179 if (v == QCA::ValidityGood) 3180 printf("Certificate is valid\n"); 3181 else { 3182 printf("Certificate is NOT valid: %s\n", qPrintable(validityToString(v))); 3183 return 1; 3184 } 3185 } else { 3186 usage(); 3187 return 1; 3188 } 3189 } else if (args[0] == QLatin1String("keybundle")) { 3190 if (args.count() < 2) { 3191 usage(); 3192 return 1; 3193 } 3194 3195 if (args[1] == QLatin1String("make")) { 3196 if (args.count() < 4) { 3197 usage(); 3198 return 1; 3199 } 3200 3201 if (!QCA::isSupported("pkcs12")) { 3202 fprintf(stderr, "Error: need 'pkcs12' feature.\n"); 3203 return 1; 3204 } 3205 3206 QCA::PrivateKey priv = get_K(args[2]); 3207 if (priv.isNull()) 3208 return 1; 3209 3210 QCA::Certificate cert = get_C(args[3]); 3211 if (cert.isNull()) 3212 return 1; 3213 3214 // get roots 3215 QCA::CertificateCollection roots; 3216 if (!nosys) 3217 roots += QCA::systemStore(); 3218 if (!rootsFile.isEmpty()) 3219 roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile); 3220 3221 // get nonroots 3222 QCA::CertificateCollection nonroots; 3223 if (!nonRootsFile.isEmpty()) 3224 nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile); 3225 3226 QList<QCA::Certificate> issuer_pool = roots.certificates() + nonroots.certificates(); 3227 3228 QCA::CertificateChain chain; 3229 chain += cert; 3230 chain = chain.complete(issuer_pool); 3231 3232 QCA::KeyBundle key; 3233 key.setName(chain.primary().commonName()); 3234 key.setCertificateChainAndKey(chain, priv); 3235 3236 if (!allowprompt && !have_newpass) { 3237 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n"); 3238 return 1; 3239 } 3240 3241 // prompt for new passphrase if necessary 3242 if (!have_newpass) { 3243 while (!promptForNewPassphrase(&newpass)) { } 3244 have_newpass = true; 3245 } 3246 3247 if (newpass.isEmpty()) { 3248 fprintf(stderr, "Error: keybundles cannot have empty passphrases.\n"); 3249 return 1; 3250 } 3251 3252 QString newFileName = QStringLiteral("cert.p12"); 3253 3254 if (key.toFile(newFileName, newpass)) 3255 printf("Keybundle saved to %s\n", qPrintable(newFileName)); 3256 else { 3257 fprintf(stderr, "Error: can't encode keybundle.\n"); 3258 return 1; 3259 } 3260 } else if (args[1] == QLatin1String("extract")) { 3261 if (args.count() < 3) { 3262 usage(); 3263 return 1; 3264 } 3265 3266 QCA::KeyBundle key = get_X(args[2]); 3267 if (key.isNull()) 3268 return 1; 3269 3270 QCA::PrivateKey priv = key.privateKey(); 3271 bool export_priv = priv.canExport(); 3272 3273 if (export_priv) { 3274 fprintf(stderr, "You will need to create a passphrase for the extracted private key.\n"); 3275 3276 if (!allowprompt && !have_newpass) { 3277 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n"); 3278 return 1; 3279 } 3280 3281 // prompt for new passphrase if necessary 3282 if (!have_newpass) { 3283 while (!promptForNewPassphrase(&newpass)) { } 3284 have_newpass = true; 3285 } 3286 } 3287 3288 printf("Certs: (first is primary)\n"); 3289 QCA::CertificateChain chain = key.certificateChain(); 3290 for (int n = 0; n < chain.count(); ++n) 3291 printf("%s", qPrintable(chain[n].toPEM())); 3292 printf("Private Key:\n"); 3293 if (export_priv) { 3294 QString out; 3295 if (!newpass.isEmpty()) 3296 out = priv.toPEM(newpass); 3297 else 3298 out = priv.toPEM(); 3299 printf("%s", qPrintable(out)); 3300 } else { 3301 printf("(Key is not exportable)\n"); 3302 } 3303 } else if (args[1] == QLatin1String("changepass")) { 3304 if (args.count() < 3) { 3305 usage(); 3306 return 1; 3307 } 3308 3309 QCA::KeyBundle key = get_X(args[2]); 3310 if (key.isNull()) 3311 return 1; 3312 3313 if (!key.privateKey().canExport()) { 3314 fprintf(stderr, "Error: private key not exportable.\n"); 3315 return 1; 3316 } 3317 3318 if (!allowprompt && !have_newpass) { 3319 fprintf(stderr, "Error: no passphrase specified (use '--newpass=' for none).\n"); 3320 return 1; 3321 } 3322 3323 // prompt for new passphrase if necessary 3324 if (!have_newpass) { 3325 while (!promptForNewPassphrase(&newpass)) { } 3326 have_newpass = true; 3327 } 3328 3329 if (newpass.isEmpty()) { 3330 fprintf(stderr, "Error: keybundles cannot have empty passphrases.\n"); 3331 return 1; 3332 } 3333 3334 QFileInfo fi(args[2]); 3335 QString newFileName = fi.baseName() + QStringLiteral("_new.p12"); 3336 3337 if (key.toFile(newFileName, newpass)) 3338 printf("Keybundle saved to %s\n", qPrintable(newFileName)); 3339 else { 3340 fprintf(stderr, "Error: can't encode keybundle.\n"); 3341 return 1; 3342 } 3343 } else { 3344 usage(); 3345 return 1; 3346 } 3347 } else if (args[0] == QLatin1String("keystore")) { 3348 if (args.count() < 2) { 3349 usage(); 3350 return 1; 3351 } 3352 3353 if (args[1] == QLatin1String("list-stores")) { 3354 ksm_start_and_wait(); 3355 3356 QCA::KeyStoreManager ksm; 3357 QStringList storeList = ksm.keyStores(); 3358 3359 for (int n = 0; n < storeList.count(); ++n) { 3360 QCA::KeyStore ks(storeList[n], &ksm); 3361 QString type = kstype_to_string(ks.type()); 3362 printf("%s %s [%s]\n", qPrintable(type), qPrintable(idHash(ks.id())), qPrintable(ks.name())); 3363 } 3364 3365 if (debug) 3366 output_keystore_diagnostic_text(); 3367 } else if (args[1] == QLatin1String("list")) { 3368 if (args.count() < 3) { 3369 usage(); 3370 return 1; 3371 } 3372 3373 ksm_start_and_wait(); 3374 3375 QCA::KeyStoreManager ksm; 3376 QCA::KeyStore store(getKeyStore(args[2]), &ksm); 3377 if (!store.isValid()) { 3378 if (debug) 3379 output_keystore_diagnostic_text(); 3380 3381 fprintf(stderr, "Error: no such store\n"); 3382 return 1; 3383 } 3384 3385 QList<QCA::KeyStoreEntry> list = store.entryList(); 3386 for (int n = 0; n < list.count(); ++n) { 3387 QCA::KeyStoreEntry i = list[n]; 3388 QString type = ksentrytype_to_string(i.type()); 3389 printf("%s %s [%s]\n", qPrintable(type), qPrintable(idHash(i.id())), qPrintable(i.name())); 3390 } 3391 3392 if (debug) 3393 output_keystore_diagnostic_text(); 3394 } else if (args[1] == QLatin1String("monitor")) { 3395 KeyStoreMonitor::monitor(); 3396 3397 if (debug) 3398 output_keystore_diagnostic_text(); 3399 } else if (args[1] == QLatin1String("export")) { 3400 if (args.count() < 3) { 3401 usage(); 3402 return 1; 3403 } 3404 3405 QCA::KeyStoreEntry entry = get_E(args[2]); 3406 if (entry.isNull()) 3407 return 1; 3408 3409 if (entry.type() == QCA::KeyStoreEntry::TypeCertificate) 3410 printf("%s", qPrintable(entry.certificate().toPEM())); 3411 else if (entry.type() == QCA::KeyStoreEntry::TypeCRL) 3412 printf("%s", qPrintable(entry.crl().toPEM())); 3413 else if (entry.type() == QCA::KeyStoreEntry::TypePGPPublicKey || 3414 entry.type() == QCA::KeyStoreEntry::TypePGPSecretKey) 3415 printf("%s", qPrintable(entry.pgpPublicKey().toString())); 3416 else if (entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) { 3417 fprintf(stderr, "Error: use 'keybundle extract' command instead.\n"); 3418 return 1; 3419 } else { 3420 fprintf(stderr, "Error: cannot export type '%d'.\n", entry.type()); 3421 return 1; 3422 } 3423 } else if (args[1] == QLatin1String("exportref")) { 3424 if (args.count() < 3) { 3425 usage(); 3426 return 1; 3427 } 3428 3429 QCA::KeyStoreEntry entry = get_E(args[2]); 3430 if (entry.isNull()) 3431 return 1; 3432 printf("%s", make_ksentry_string(entry.toString()).toUtf8().data()); 3433 } else if (args[1] == QLatin1String("addkb")) { 3434 if (args.count() < 4) { 3435 usage(); 3436 return 1; 3437 } 3438 3439 ksm_start_and_wait(); 3440 3441 QCA::KeyStoreManager ksm; 3442 QCA::KeyStore store(getKeyStore(args[2]), &ksm); 3443 if (!store.isValid()) { 3444 fprintf(stderr, "Error: no such store\n"); 3445 return 1; 3446 } 3447 3448 QCA::KeyBundle key = get_X(args[3]); 3449 if (key.isNull()) 3450 return 1; 3451 3452 if (!store.writeEntry(key).isEmpty()) 3453 printf("Entry written.\n"); 3454 else { 3455 fprintf(stderr, "Error: unable to write entry.\n"); 3456 return 1; 3457 } 3458 } else if (args[1] == QLatin1String("addpgp")) { 3459 if (args.count() < 4) { 3460 usage(); 3461 return 1; 3462 } 3463 3464 if (!QCA::isSupported("openpgp")) { 3465 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3466 return 1; 3467 } 3468 3469 ksm_start_and_wait(); 3470 3471 QCA::KeyStoreManager ksm; 3472 QCA::KeyStore store(getKeyStore(args[2]), &ksm); 3473 if (!store.isValid()) { 3474 fprintf(stderr, "Error: no such store\n"); 3475 return 1; 3476 } 3477 3478 QCA::PGPKey pub = QCA::PGPKey::fromFile(args[3]); 3479 if (pub.isNull()) 3480 return 1; 3481 3482 if (!store.writeEntry(pub).isEmpty()) 3483 printf("Entry written.\n"); 3484 else { 3485 fprintf(stderr, "Error: unable to write entry.\n"); 3486 return 1; 3487 } 3488 } else if (args[1] == QLatin1String("remove")) { 3489 if (args.count() < 3) { 3490 usage(); 3491 return 1; 3492 } 3493 3494 QCA::KeyStoreEntry entry = get_E(args[2]); 3495 if (entry.isNull()) 3496 return 1; 3497 3498 QCA::KeyStoreManager ksm; 3499 QCA::KeyStore store(entry.storeId(), &ksm); 3500 if (!store.isValid()) { 3501 fprintf(stderr, "Error: no such store\n"); 3502 return 1; 3503 } 3504 3505 if (store.removeEntry(entry.id())) 3506 printf("Entry removed.\n"); 3507 else { 3508 fprintf(stderr, "Error: unable to remove entry.\n"); 3509 return 1; 3510 } 3511 } else { 3512 usage(); 3513 return 1; 3514 } 3515 } else if (args[0] == QLatin1String("show")) { 3516 if (args.count() < 2) { 3517 usage(); 3518 return 1; 3519 } 3520 3521 if (args[1] == QLatin1String("cert")) { 3522 if (args.count() < 3) { 3523 usage(); 3524 return 1; 3525 } 3526 3527 QCA::Certificate cert = get_C(args[2]); 3528 if (cert.isNull()) 3529 return 1; 3530 3531 print_cert(cert, ordered); 3532 } else if (args[1] == QLatin1String("req")) { 3533 if (args.count() < 3) { 3534 usage(); 3535 return 1; 3536 } 3537 3538 if (!QCA::isSupported("csr")) { 3539 fprintf(stderr, "Error: need 'csr' feature.\n"); 3540 return 1; 3541 } 3542 3543 QCA::CertificateRequest req(args[2]); 3544 if (req.isNull()) { 3545 fprintf(stderr, "Error: can't read/process certificate request file.\n"); 3546 return 1; 3547 } 3548 3549 print_certreq(req, ordered); 3550 } else if (args[1] == QLatin1String("crl")) { 3551 if (args.count() < 3) { 3552 usage(); 3553 return 1; 3554 } 3555 3556 if (!QCA::isSupported("crl")) { 3557 fprintf(stderr, "Error: need 'crl' feature.\n"); 3558 return 1; 3559 } 3560 3561 QCA::CRL crl; 3562 if (is_pem_file(args[2])) 3563 crl = QCA::CRL::fromPEMFile(args[2]); 3564 else 3565 crl = QCA::CRL::fromDER(read_der_file(args[2])); 3566 if (crl.isNull()) { 3567 fprintf(stderr, "Error: unable to read/process CRL file.\n"); 3568 return 1; 3569 } 3570 3571 print_crl(crl, ordered); 3572 } else if (args[1] == QLatin1String("kb")) { 3573 if (args.count() < 3) { 3574 usage(); 3575 return 1; 3576 } 3577 3578 QCA::KeyBundle key = get_X(args[2]); 3579 if (key.isNull()) 3580 return 1; 3581 3582 printf("Keybundle contains %d certificates. Displaying primary:\n", int(key.certificateChain().count())); 3583 print_cert(key.certificateChain().primary(), ordered); 3584 } else if (args[1] == QLatin1String("pgp")) { 3585 if (args.count() < 3) { 3586 usage(); 3587 return 1; 3588 } 3589 3590 // try for secret key, then try public key 3591 QCA::PGPKey key = get_S(args[2], true).first; 3592 if (key.isNull()) { 3593 key = get_P(args[2]); 3594 if (key.isNull()) 3595 return 1; 3596 } 3597 3598 print_pgp(key); 3599 } else { 3600 usage(); 3601 return 1; 3602 } 3603 } else if (args[0] == QLatin1String("message")) { 3604 if (args.count() < 2) { 3605 usage(); 3606 return 1; 3607 } 3608 3609 if (args[1] == QLatin1String("sign")) { 3610 if (args.count() < 4) { 3611 usage(); 3612 return 1; 3613 } 3614 3615 QCA::SecureMessageSystem *sms; 3616 QCA::SecureMessageKey skey; 3617 QCA::SecureMessage::SignMode mode; 3618 bool pgp = false; 3619 3620 if (args[2] == QLatin1String("pgp")) { 3621 if (!QCA::isSupported("openpgp")) { 3622 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3623 return 1; 3624 } 3625 3626 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(args[3]); 3627 if (key.first.isNull()) 3628 return 1; 3629 3630 sms = new QCA::OpenPGP; 3631 skey.setPGPSecretKey(key.first); 3632 mode = QCA::SecureMessage::Clearsign; 3633 pgp = true; 3634 } else if (args[2] == QLatin1String("pgpdetach")) { 3635 if (!QCA::isSupported("openpgp")) { 3636 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3637 return 1; 3638 } 3639 3640 QPair<QCA::PGPKey, QCA::PGPKey> key = get_S(args[3]); 3641 if (key.first.isNull()) 3642 return 1; 3643 3644 sms = new QCA::OpenPGP; 3645 skey.setPGPSecretKey(key.first); 3646 mode = QCA::SecureMessage::Detached; 3647 pgp = true; 3648 } else if (args[2] == QLatin1String("smime")) { 3649 if (!QCA::isSupported("cms")) { 3650 fprintf(stderr, "Error: need 'cms' feature.\n"); 3651 return 1; 3652 } 3653 3654 QCA::KeyBundle key = get_X(args[3]); 3655 if (key.isNull()) 3656 return 1; 3657 3658 // get nonroots 3659 QCA::CertificateCollection nonroots; 3660 if (!nonRootsFile.isEmpty()) 3661 nonroots = QCA::CertificateCollection::fromFlatTextFile(nonRootsFile); 3662 3663 QList<QCA::Certificate> issuer_pool = nonroots.certificates(); 3664 3665 QCA::CertificateChain chain = key.certificateChain(); 3666 chain = chain.complete(issuer_pool); 3667 3668 sms = new QCA::CMS; 3669 skey.setX509CertificateChain(chain); 3670 skey.setX509PrivateKey(key.privateKey()); 3671 mode = QCA::SecureMessage::Detached; 3672 } else { 3673 usage(); 3674 return 1; 3675 } 3676 3677 // read input data from stdin all at once 3678 QByteArray plain; 3679 while (!feof(stdin)) { 3680 QByteArray block(1024, 0); 3681 int n = fread(block.data(), 1, 1024, stdin); 3682 if (n < 0) 3683 break; 3684 block.resize(n); 3685 plain += block; 3686 } 3687 3688 // smime envelope 3689 if (!pgp) { 3690 QString text = add_cr(QString::fromUtf8(plain)); 3691 plain = QString::fromLatin1(mime_signpart).arg(text).toUtf8(); 3692 } 3693 3694 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 3695 msg->setSigner(skey); 3696 // pgp should always be ascii 3697 if (pgp) 3698 msg->setFormat(QCA::SecureMessage::Ascii); 3699 msg->setBundleSignerEnabled(!nobundle); 3700 msg->startSign(mode); 3701 msg->update(plain); 3702 msg->end(); 3703 msg->waitForFinished(-1); 3704 3705 if (debug) { 3706 output_keystore_diagnostic_text(); 3707 output_message_diagnostic_text(msg); 3708 } 3709 3710 if (!msg->success()) { 3711 QString errstr = smErrorToString(msg->errorCode()); 3712 delete msg; 3713 delete sms; 3714 3715 fprintf(stderr, "Error: unable to sign: %s\n", qPrintable(errstr)); 3716 return 1; 3717 } 3718 3719 QString hashName = msg->hashName(); 3720 3721 QByteArray output; 3722 if (mode == QCA::SecureMessage::Detached) 3723 output = msg->signature(); 3724 else 3725 output = msg->read(); 3726 3727 delete msg; 3728 delete sms; 3729 3730 // smime envelope 3731 if (!pgp) { 3732 QCA::Base64 enc; 3733 enc.setLineBreaksEnabled(true); 3734 enc.setLineBreaksColumn(76); 3735 QString sigtext = add_cr(enc.arrayToString(output)); 3736 QString str = QString::fromLatin1(mime_signed).arg(hashName, QString::fromUtf8(plain), sigtext); 3737 output = str.toUtf8(); 3738 } 3739 3740 printf("%s", output.data()); 3741 } else if (args[1] == QLatin1String("encrypt")) { 3742 if (args.count() < 4) { 3743 usage(); 3744 return 1; 3745 } 3746 3747 QCA::SecureMessageSystem *sms; 3748 QCA::SecureMessageKey skey; 3749 bool pgp = false; 3750 3751 if (args[2] == QLatin1String("pgp")) { 3752 if (!QCA::isSupported("openpgp")) { 3753 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3754 return 1; 3755 } 3756 3757 QCA::PGPKey key = get_P(args[3]); 3758 if (key.isNull()) 3759 return 1; 3760 3761 sms = new QCA::OpenPGP; 3762 skey.setPGPPublicKey(key); 3763 pgp = true; 3764 } else if (args[2] == QLatin1String("smime")) { 3765 if (!QCA::isSupported("cms")) { 3766 fprintf(stderr, "Error: need 'cms' feature.\n"); 3767 return 1; 3768 } 3769 3770 QCA::Certificate cert = get_C(args[3]); 3771 if (cert.isNull()) 3772 return 1; 3773 3774 sms = new QCA::CMS; 3775 skey.setX509CertificateChain(cert); 3776 } else { 3777 usage(); 3778 return 1; 3779 } 3780 3781 // read input data from stdin all at once 3782 QByteArray plain; 3783 while (!feof(stdin)) { 3784 QByteArray block(1024, 0); 3785 int n = fread(block.data(), 1, 1024, stdin); 3786 if (n < 0) 3787 break; 3788 block.resize(n); 3789 plain += block; 3790 } 3791 3792 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 3793 msg->setRecipient(skey); 3794 // pgp should always be ascii 3795 if (pgp) 3796 msg->setFormat(QCA::SecureMessage::Ascii); 3797 msg->startEncrypt(); 3798 msg->update(plain); 3799 msg->end(); 3800 msg->waitForFinished(-1); 3801 3802 if (debug) { 3803 output_keystore_diagnostic_text(); 3804 output_message_diagnostic_text(msg); 3805 } 3806 3807 if (!msg->success()) { 3808 QString errstr = smErrorToString(msg->errorCode()); 3809 delete msg; 3810 delete sms; 3811 fprintf(stderr, "Error: unable to encrypt: %s\n", qPrintable(errstr)); 3812 return 1; 3813 } 3814 3815 QByteArray output = msg->read(); 3816 delete msg; 3817 delete sms; 3818 3819 // smime envelope 3820 if (!pgp) { 3821 QCA::Base64 enc; 3822 enc.setLineBreaksEnabled(true); 3823 enc.setLineBreaksColumn(76); 3824 QString enctext = add_cr(enc.arrayToString(output)); 3825 QString str = QString::fromLatin1(mime_enveloped).arg(enctext); 3826 output = str.toUtf8(); 3827 } 3828 3829 printf("%s", output.data()); 3830 } else if (args[1] == QLatin1String("signencrypt")) { 3831 if (args.count() < 4) { 3832 usage(); 3833 return 1; 3834 } 3835 3836 if (!QCA::isSupported("openpgp")) { 3837 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3838 return 1; 3839 } 3840 3841 QCA::SecureMessageSystem *sms; 3842 QCA::SecureMessageKey skey; 3843 QCA::SecureMessageKey rkey; 3844 3845 { 3846 QPair<QCA::PGPKey, QCA::PGPKey> sec = get_S(args[2]); 3847 if (sec.first.isNull()) 3848 return 1; 3849 3850 QCA::PGPKey pub = get_P(args[3]); 3851 if (pub.isNull()) 3852 return 1; 3853 3854 sms = new QCA::OpenPGP; 3855 skey.setPGPSecretKey(sec.first); 3856 rkey.setPGPPublicKey(pub); 3857 } 3858 3859 // read input data from stdin all at once 3860 QByteArray plain; 3861 while (!feof(stdin)) { 3862 QByteArray block(1024, 0); 3863 int n = fread(block.data(), 1, 1024, stdin); 3864 if (n < 0) 3865 break; 3866 block.resize(n); 3867 plain += block; 3868 } 3869 3870 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 3871 if (!msg->canSignAndEncrypt()) { 3872 delete msg; 3873 delete sms; 3874 fprintf(stderr, "Error: cannot perform integrated sign and encrypt.\n"); 3875 return 1; 3876 } 3877 3878 msg->setSigner(skey); 3879 msg->setRecipient(rkey); 3880 msg->setFormat(QCA::SecureMessage::Ascii); 3881 msg->startSignAndEncrypt(); 3882 msg->update(plain); 3883 msg->end(); 3884 msg->waitForFinished(-1); 3885 3886 if (debug) { 3887 output_keystore_diagnostic_text(); 3888 output_message_diagnostic_text(msg); 3889 } 3890 3891 if (!msg->success()) { 3892 QString errstr = smErrorToString(msg->errorCode()); 3893 delete msg; 3894 delete sms; 3895 fprintf(stderr, "Error: unable to sign and encrypt: %s\n", qPrintable(errstr)); 3896 return 1; 3897 } 3898 3899 QByteArray output = msg->read(); 3900 delete msg; 3901 delete sms; 3902 3903 printf("%s", output.data()); 3904 } else if (args[1] == QLatin1String("verify")) { 3905 if (args.count() < 3) { 3906 usage(); 3907 return 1; 3908 } 3909 3910 QCA::SecureMessageSystem *sms; 3911 bool pgp = false; 3912 3913 if (args[2] == QLatin1String("pgp")) { 3914 if (!QCA::isSupported("openpgp")) { 3915 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 3916 return 1; 3917 } 3918 3919 sms = new QCA::OpenPGP; 3920 pgp = true; 3921 } else if (args[2] == QLatin1String("smime")) { 3922 if (!QCA::isSupported("cms")) { 3923 fprintf(stderr, "Error: need 'cms' feature.\n"); 3924 return 1; 3925 } 3926 3927 // get roots 3928 QCA::CertificateCollection roots; 3929 if (!nosys) 3930 roots += QCA::systemStore(); 3931 if (!rootsFile.isEmpty()) 3932 roots += QCA::CertificateCollection::fromFlatTextFile(rootsFile); 3933 3934 // get intermediates and possible signers, in case 3935 // the message does not have them. 3936 QCA::CertificateCollection nonroots; 3937 if (!nonRootsFile.isEmpty()) 3938 nonroots += QCA::CertificateCollection::fromFlatTextFile(nonRootsFile); 3939 3940 sms = new QCA::CMS; 3941 ((QCA::CMS *)sms)->setTrustedCertificates(roots); 3942 ((QCA::CMS *)sms)->setUntrustedCertificates(nonroots); 3943 } else { 3944 usage(); 3945 return 1; 3946 } 3947 3948 QByteArray data, sig; 3949 QString smime_text; 3950 { 3951 // read input data from stdin all at once 3952 QByteArray plain; 3953 while (!feof(stdin)) { 3954 QByteArray block(1024, 0); 3955 int n = fread(block.data(), 1, 1024, stdin); 3956 if (n < 0) 3957 break; 3958 block.resize(n); 3959 plain += block; 3960 } 3961 3962 if (pgp) { 3963 // pgp can be either a detached signature followed 3964 // by data, or an integrated message. 3965 3966 // detached signature? 3967 if (plain.startsWith("-----BEGIN PGP SIGNATURE-----")) { 3968 QByteArray footer = "-----END PGP SIGNATURE-----\n"; 3969 int n = plain.indexOf(footer); 3970 if (n == -1) { 3971 delete sms; 3972 fprintf(stderr, "Error: pgp signature header, but no footer.\n"); 3973 return 1; 3974 } 3975 3976 n += footer.length(); 3977 sig = plain.mid(0, n); 3978 data = plain.mid(n); 3979 } else { 3980 data = plain; 3981 } 3982 } else { 3983 // smime envelope 3984 QString in = QString::fromUtf8(plain); 3985 in = add_cr(in); // change the line endings?! 3986 QString str, sigtext; 3987 if (!open_mime_data_sig(in, &str, &sigtext)) { 3988 fprintf(stderr, "Error: can't parse message file.\n"); 3989 return 1; 3990 } 3991 3992 data = str.toUtf8(); 3993 smime_text = str; 3994 3995 QCA::Base64 dec; 3996 dec.setLineBreaksEnabled(true); 3997 sig = dec.stringToArray(rem_cr(sigtext)).toByteArray(); 3998 } 3999 } 4000 4001 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 4002 if (pgp) 4003 msg->setFormat(QCA::SecureMessage::Ascii); 4004 msg->startVerify(sig); 4005 msg->update(data); 4006 msg->end(); 4007 msg->waitForFinished(-1); 4008 4009 if (debug) { 4010 output_keystore_diagnostic_text(); 4011 output_message_diagnostic_text(msg); 4012 } 4013 4014 if (!msg->success()) { 4015 QString errstr = smErrorToString(msg->errorCode()); 4016 delete msg; 4017 delete sms; 4018 fprintf(stderr, "Error: verify failed: %s\n", qPrintable(errstr)); 4019 return 1; 4020 } 4021 4022 QByteArray output; 4023 if (pgp && sig.isEmpty()) 4024 output = msg->read(); 4025 4026 QList<QCA::SecureMessageSignature> signers = msg->signers(); 4027 delete msg; 4028 delete sms; 4029 4030 // for pgp clearsign, pgp signed (non-detached), and smime, 4031 // the signed content was inside of the message. we need 4032 // to print that content now 4033 if (pgp) { 4034 printf("%s", output.data()); 4035 } else { 4036 QString str = open_mime_envelope(smime_text); 4037 printf("%s", str.toUtf8().data()); 4038 } 4039 4040 smDisplaySignatures(signers); 4041 4042 bool allgood = true; 4043 foreach (const QCA::SecureMessageSignature &signer, signers) { 4044 if (signer.identityResult() != QCA::SecureMessageSignature::Valid) { 4045 allgood = false; 4046 break; 4047 } 4048 } 4049 4050 if (!allgood) 4051 return 1; 4052 } else if (args[1] == QLatin1String("decrypt")) { 4053 if (args.count() < 3) { 4054 usage(); 4055 return 1; 4056 } 4057 4058 QCA::SecureMessageSystem *sms; 4059 bool pgp = false; 4060 4061 if (args[2] == QLatin1String("pgp")) { 4062 if (!QCA::isSupported("openpgp")) { 4063 fprintf(stderr, "Error: need 'openpgp' feature.\n"); 4064 return 1; 4065 } 4066 4067 ksm_start_and_wait(); 4068 4069 sms = new QCA::OpenPGP; 4070 pgp = true; 4071 } else if (args[2] == QLatin1String("smime")) { 4072 if (args.count() < 4) { 4073 usage(); 4074 return 1; 4075 } 4076 4077 if (!QCA::isSupported("cms")) { 4078 fprintf(stderr, "Error: need 'cms' feature.\n"); 4079 return 1; 4080 } 4081 4082 // user can provide many possible decrypt keys 4083 QList<QCA::KeyBundle> keys; 4084 for (int n = 3; n < args.count(); ++n) { 4085 QCA::KeyBundle key = get_X(args[n]); 4086 if (key.isNull()) 4087 return 1; 4088 keys += key; 4089 } 4090 4091 sms = new QCA::CMS; 4092 4093 QList<QCA::SecureMessageKey> skeys; 4094 foreach (const QCA::KeyBundle &key, keys) { 4095 QCA::SecureMessageKey skey; 4096 skey.setX509CertificateChain(key.certificateChain()); 4097 skey.setX509PrivateKey(key.privateKey()); 4098 skeys += skey; 4099 } 4100 4101 ((QCA::CMS *)sms)->setPrivateKeys(skeys); 4102 } else { 4103 usage(); 4104 return 1; 4105 } 4106 4107 // read input data from stdin all at once 4108 QByteArray plain; 4109 while (!feof(stdin)) { 4110 QByteArray block(1024, 0); 4111 int n = fread(block.data(), 1, 1024, stdin); 4112 if (n < 0) 4113 break; 4114 block.resize(n); 4115 plain += block; 4116 } 4117 4118 // smime envelope 4119 if (!pgp) { 4120 QString in = QString::fromUtf8(plain); 4121 QString str = open_mime_envelope(in); 4122 if (str.isEmpty()) { 4123 delete sms; 4124 fprintf(stderr, "Error: can't parse message file.\n"); 4125 return 1; 4126 } 4127 4128 QCA::Base64 dec; 4129 dec.setLineBreaksEnabled(true); 4130 plain = dec.stringToArray(rem_cr(str)).toByteArray(); 4131 } 4132 4133 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 4134 if (pgp) 4135 msg->setFormat(QCA::SecureMessage::Ascii); 4136 msg->startDecrypt(); 4137 msg->update(plain); 4138 msg->end(); 4139 msg->waitForFinished(-1); 4140 4141 if (debug) { 4142 output_keystore_diagnostic_text(); 4143 output_message_diagnostic_text(msg); 4144 } 4145 4146 if (!msg->success()) { 4147 QString errstr = smErrorToString(msg->errorCode()); 4148 delete msg; 4149 delete sms; 4150 fprintf(stderr, "Error: decrypt failed: %s\n", qPrintable(errstr)); 4151 return 1; 4152 } 4153 4154 QByteArray output = msg->read(); 4155 4156 QList<QCA::SecureMessageSignature> signers; 4157 bool wasSigned = false; 4158 if (msg->wasSigned()) { 4159 signers = msg->signers(); 4160 wasSigned = true; 4161 } 4162 delete msg; 4163 delete sms; 4164 4165 printf("%s", output.data()); 4166 4167 if (wasSigned) { 4168 fprintf(stderr, "Message was also signed:\n"); 4169 4170 smDisplaySignatures(signers); 4171 4172 bool allgood = true; 4173 foreach (const QCA::SecureMessageSignature &signer, signers) { 4174 if (signer.identityResult() != QCA::SecureMessageSignature::Valid) { 4175 allgood = false; 4176 break; 4177 } 4178 } 4179 4180 if (!allgood) 4181 return 1; 4182 } 4183 } else if (args[1] == QLatin1String("exportcerts")) { 4184 if (!QCA::isSupported("cms")) { 4185 fprintf(stderr, "Error: need 'cms' feature.\n"); 4186 return 1; 4187 } 4188 4189 QCA::SecureMessageSystem *sms = new QCA::CMS; 4190 4191 QByteArray data, sig; 4192 QString smime_text; 4193 { 4194 // read input data from stdin all at once 4195 QByteArray plain; 4196 while (!feof(stdin)) { 4197 QByteArray block(1024, 0); 4198 int n = fread(block.data(), 1, 1024, stdin); 4199 if (n < 0) 4200 break; 4201 block.resize(n); 4202 plain += block; 4203 } 4204 4205 // smime envelope 4206 QString in = QString::fromUtf8(plain); 4207 QString str, sigtext; 4208 if (!open_mime_data_sig(in, &str, &sigtext)) { 4209 delete sms; 4210 fprintf(stderr, "Error: can't parse message file.\n"); 4211 return 1; 4212 } 4213 4214 data = str.toUtf8(); 4215 smime_text = str; 4216 4217 QCA::Base64 dec; 4218 dec.setLineBreaksEnabled(true); 4219 sig = dec.stringToArray(rem_cr(sigtext)).toByteArray(); 4220 } 4221 4222 QCA::SecureMessage *msg = new QCA::SecureMessage(sms); 4223 msg->startVerify(sig); 4224 msg->update(data); 4225 msg->end(); 4226 msg->waitForFinished(-1); 4227 4228 if (debug) 4229 output_message_diagnostic_text(msg); 4230 4231 if (!msg->success()) { 4232 QString errstr = smErrorToString(msg->errorCode()); 4233 delete msg; 4234 delete sms; 4235 fprintf(stderr, "Error: export failed: %s\n", qPrintable(errstr)); 4236 return 1; 4237 } 4238 4239 QList<QCA::SecureMessageSignature> signers = msg->signers(); 4240 delete msg; 4241 delete sms; 4242 4243 // print out all certs of all signers 4244 foreach (const QCA::SecureMessageSignature &signer, signers) { 4245 QCA::SecureMessageKey key = signer.key(); 4246 if (!key.isNull()) { 4247 foreach (const QCA::Certificate &c, key.x509CertificateChain()) 4248 printf("%s", qPrintable(c.toPEM())); 4249 } 4250 } 4251 } else { 4252 usage(); 4253 return 1; 4254 } 4255 } else { 4256 usage(); 4257 return 1; 4258 } 4259 4260 return 0; 4261 } 4262 4263 #include "main.moc"