残念ながら、openSSLを操作するために必要なすべてのコンポーネントを備えたQtライブラリには、キーを生成するためのコードが含まれていません。 したがって、状況を修正しようとします。
まず、ヘッダーファイルを検討します。
#ifndef SSLKEYGEN_H #define SSLKEYGEN_H #include <QtGui/QDialog> #include <QSslKey> #include <QMap> #include <QSslCertificate> #include <QWizard> #include <QWizardPage> #include <QProgressBar> #include <QDir> #include <QFileDialog> #include <QFileInfo> #include <QFile> #include <QLabel> bool isPrivateKeyCorrespondsToCertificate(QSslCertificate cert, QSslKey key); class CertificateGeneratorWizard : public QWizard { Q_OBJECT public: CertificateGeneratorWizard(QWidget * parent=0); QSslKey getPrivateKey(){return pkey;} QSslCertificate getCertificate(){return cert;} void accept(); private: QSslKey pkey; QSslCertificate cert; }; class RandSeeder : public QObject { Q_OBJECT public: void startSeeding(QList<QWidget *> w, int lim); void stop(); bool isCompleted() const{return (counter>=maximum);} protected: QMap<QWidget *, bool> wdgs; int counter; int maximum; bool eventFilter(QObject *obj, QEvent *event); signals: void step(); void stopped(); }; class getCertDataPage : public QWizardPage { Q_OBJECT public: getCertDataPage(QWidget *parent=0); }; class randomizePage : public QWizardPage { Q_OBJECT public: randomizePage(QWidget *parent=0); virtual void cleanupPage(); virtual void initializePage(); virtual bool isComplete() const; private: QProgressBar * progressBar; QLabel * label; RandSeeder seeder; private slots: void seedStep(); void seedStopped(); }; #endif // SSLKEYGEN_H
最初に宣言された関数PrivateKeyCorrespondsToCertificateは、理解しているように、証明書がキーと一致することを検証することを目的としています-Qtライブラリの明らかな省略でもあります。 sslkeygen.cppファイルからの彼女のコードは次のとおりです。
bool isPrivateKeyCorrespondsToCertificate( QSslCertificate cert, QSslKey key ) { X509 *x; EVP_PKEY *k; x=(X509 *)cert.handle(); k=EVP_PKEY_new(); if(key.algorithm() == QSsl::Rsa) EVP_PKEY_assign_RSA(k, (RSA *)key.handle()); else EVP_PKEY_assign_DSA(k, (DSA *)key.handle()); if(X509_check_private_key(x,k)==1) return true; return false; }
OpenSSLライブラリを知っていれば、すべてが非常に単純です(文書化されている、率直に言って、重要ではありません-すべての機能が説明されているように、しかし何とか控えめで説明なし)
次の宣言は、キー生成ウィザードとそのページを定義します。 RandSeederクラスはやや上品ですが、それほど後ではありません。
余分な文字で記事が乱雑にならないように、すぐにコードに進みます。 以下はヘルパー関数です。率直に言って、私はQtのソースから直接盗みました(Trolltech-Nokiaは許してくれます):
QByteArray QByteArray_from_X509(X509 *x509) { if (!x509) return QByteArray();
この関数は、X509証明書ストアを内部OpenSSLビューからQByteArrayに変換し、そこからQSslCertificateを簡単に取得できます。
さて、実際には、キー生成ウィザードについては。 最初に、証明書に記入するデータを要求するページを表示します。 このページのすべてのクラスコードは、コンストラクターのみで構成されています。
getCertDataPage::getCertDataPage( QWidget *parent ) : QWizardPage(parent) { setTitle(" "); QVBoxLayout * verticalLayout = new QVBoxLayout(); QLabel * label = new QLabel(tr(" . .")); label->setWordWrap(true); verticalLayout->addWidget(label); QHBoxLayout * horizontalLayout = new QHBoxLayout(); label = new QLabel(tr("&:")); label->setMinimumSize(QSize(80, 0)); horizontalLayout->addWidget(label); QLineEdit * lineEdit = new QLineEdit(); horizontalLayout->addWidget(lineEdit); label->setBuddy(lineEdit); verticalLayout->addLayout(horizontalLayout); registerField("certSubject*",lineEdit); horizontalLayout = new QHBoxLayout(); label = new QLabel(tr("&:")); label->setMinimumSize(QSize(80, 0)); horizontalLayout->addWidget(label); lineEdit = new QLineEdit(); horizontalLayout->addWidget(lineEdit); label->setBuddy(lineEdit); verticalLayout->addLayout(horizontalLayout); registerField("certOrganization*",lineEdit); horizontalLayout = new QHBoxLayout(); label = new QLabel(tr("&E-Mail:")); label->setMinimumSize(QSize(80, 0)); horizontalLayout->addWidget(label); lineEdit = new QLineEdit(); horizontalLayout->addWidget(lineEdit); label->setBuddy(lineEdit); verticalLayout->addLayout(horizontalLayout); registerField("certEMail*",lineEdit); setLayout(verticalLayout); }
これは、証明書データを入力するための3つのフィールドを作成する単純なページコンストラクターです。 使用するのは3つだけですが、実装には他のフィールドがある場合があります。
さらに、ウィザードは何らかの方法で良好なエントロピーを生成する必要があります。 ここでこれが何を意味するのかは説明しませんが、キーの信頼性は、それがどれだけランダムであるか(ランダム性の統計的指標がどれだけ良いか)に依存します。 これにはマウスの動きを使用し、ユーザーにマウスを左右に動かすように求めます。 ここで、RandSeederクラスを使用します。 その実装は次のとおりです。
bool RandSeeder::eventFilter( QObject *obj, QEvent *event ) { if(event->type() == QEvent::MouseMove) { uint addition=QDateTime::currentDateTime().toTime_t(); QMouseEvent *mEvent = static_cast<QMouseEvent *>(event); addition*=mEvent->globalX(); addition*=mEvent->globalY(); RAND_seed(&addition,sizeof(uint)); counter++; emit step(); if(counter>maximum) stop(); return true; } else return QObject::eventFilter(obj, event); } void RandSeeder::startSeeding( QList<QWidget *> w, int lim ) { counter=0; if(lim<512) lim=512; maximum=lim; int i; for(i=0;i<w.count();i++) { wdgs.insert(w.at(i),w.at(i)->hasMouseTracking()); w.at(i)->installEventFilter(this); w.at(i)->setMouseTracking(true); } } void RandSeeder::stop() { QList<QWidget *> w=wdgs.keys(); int i; bool mt; for(i=0;i<w.count();i++) { mt=wdgs.value(w.at(i)); wdgs.remove(w.at(i)); w.at(i)->setMouseTracking(mt); w.at(i)->removeEventFilter(this); } emit stopped(); }
ウィザードの2ページ目でRandSeederクラスがどのように使用されるかを見てみましょう。
randomizePage::randomizePage( QWidget *parent ) : QWizardPage(parent) { setTitle(" "); setFinalPage(true); QVBoxLayout * verticalLayout = new QVBoxLayout(); label = new QLabel(tr(" .")); label->setWordWrap(true); verticalLayout->addWidget(label); progressBar = new QProgressBar(); progressBar->setMaximum(1024); progressBar->setValue(0); verticalLayout->addWidget(progressBar); setLayout(verticalLayout); connect(&seeder, SIGNAL(step()), this, SLOT(seedStep())); connect(&seeder, SIGNAL(stopped()), this, SLOT(seedStopped())); } void randomizePage::seedStep() { progressBar->setValue(progressBar->value()+1); } void randomizePage::cleanupPage() { seeder.stop(); progressBar->setValue(0); } void randomizePage::initializePage() { progressBar->setValue(0); progressBar->setMaximum(1024); QList<QWidget *> wlst, chwdg; wlst << this << wizard() << progressBar << label; int i; chwdg=wizard()->findChildren<QWidget *>(); for(i=0;i<chwdg.count();i++) { if(!wlst.contains(chwdg.at(i))) wlst.append(chwdg.at(i)); } seeder.startSeeding(wlst ,1024); } void randomizePage::seedStopped() { if(seeder.isCompleted()) emit completeChanged(); } bool randomizePage::isComplete() const { if(seeder.isCompleted()) return true; return false; }
RandSeederクラスは、エントロピー調整の開始、終了、およびすべてのステップを生成します。 これらのイベントをウィザードページでキャッチし、ユーザーの進行状況を表示します。 次のページに移動するためのボタンは、調整の終了時にのみ使用可能になり、調整はinitializePage関数で開始されます。
そして今、私たちは鍵と証明書の生成を実行する準備ができています。 キーの生成は、ウィザードの受け入れ機能で実行されます。 しかし、それを見る前に、ウィザードコンストラクターを見てください。
CertificateGeneratorWizard::CertificateGeneratorWizard(QWidget * parent) : QWizard(parent) { setOption(QWizard::NoCancelButton, false); setOption(QWizard::NoDefaultButton, false); setOption(QWizard::CancelButtonOnLeft, true); addPage(new getCertDataPage); addPage(new randomizePage); setWindowTitle(tr(" ")); }
それは非常に簡単です-いくつかのオプションを(あなたの好みに合わせて、あなたによって異なるかもしれません)設定し、私たちのページを追加し、タイトルを設定します。
そして最後に、待望のジェネレーター:
void CertificateGeneratorWizard::accept() { EVP_PKEY *pk; RSA *rsa; X509 *x; X509_NAME *name=NULL; bool ok;
最初に秘密鍵を生成し、次に証明書を生成します。 証明書のフィールド名に注意してください。 多数のフィールドを持つ証明書を生成する場合、ここでそれらを記入する必要があります。 OpenSSLのドキュメントを参照してください。可能なフィールドのリストがあります。
まあ、それだけです。