Qtギャップを埋める-SSLキー生成

残念ながら、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(); // Use i2d_X509 to convert the X509 to an array. int length = i2d_X509(x509, 0); QByteArray array; array.resize(length); char *data = array.data(); char **dataP = &data; unsigned char **dataPu = (unsigned char **)dataP; if (i2d_X509(x509, dataPu) < 0) return QByteArray(); // Convert to Base64 - wrap at 64 characters. array = array.toBase64(); QByteArray tmp; for (int i = 0; i < array.size() - 64; i += 64) { tmp += QByteArray::fromRawData(array.data() + i, 64); tmp += "\n"; } if (int remainder = array.size() % 64) { tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); tmp += "\n"; } return "-----BEGIN CERTIFICATE-----\n" + tmp + "-----END CERTIFICATE-----\n"; }
      
      





この関数は、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; //parameters int bits=4096; long serial=57; long days=1895; setCursor(Qt::WaitCursor); //create private key pk=EVP_PKEY_new(); rsa=RSA_generate_key(bits,RSA_F4,NULL,NULL); EVP_PKEY_assign_RSA(pk,rsa); { //save it to QSslKey BIO *bio = BIO_new(BIO_s_mem()); PEM_write_bio_RSAPrivateKey(bio, rsa, (const EVP_CIPHER *)0, NULL, 0, 0, 0); QByteArray pem; char *data; long size = BIO_get_mem_data(bio, &data); pem = QByteArray(data, size); BIO_free(bio); pkey=QSslKey(pem,QSsl::Rsa); ok=!pkey.isNull(); } x=X509_new(); X509_set_version(x,2); ASN1_INTEGER_set(X509_get_serialNumber(x),serial); X509_gmtime_adj(X509_get_notBefore(x),(long)60*60*24*(-2)); X509_gmtime_adj(X509_get_notAfter(x),(long)60*60*24*days); X509_set_pubkey(x,pk); name=X509_get_subject_name(x); QVariant fldVal=field("certSubject"); X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, fldVal.toString().toLatin1().constData(), -1, -1, 0); fldVal=field("certOrganization"); if(!fldVal.isNull()) X509_NAME_add_entry_by_txt(name,"O", MBSTRING_ASC, fldVal.toString().toLatin1().constData(), -1, -1, 0); fldVal=field("certEMail"); if(!fldVal.isNull()) { X509_NAME_add_entry_by_txt(name,"emailAddress", MBSTRING_ASC, fldVal.toString().toLatin1().constData(), -1, -1, 0); } X509_set_issuer_name(x,name); X509_sign(x,pk,EVP_md5()); { QByteArray crt=QByteArray_from_X509(x); cert=QSslCertificate(crt); ok=cert.isValid(); } ok=isPrivateKeyCorrespondsToCertificate(cert,pkey); QWizard::accept(); }
      
      





最初に秘密鍵を生成し、次に証明書を生成します。 証明書のフィールド名に注意してください。 多数のフィールドを持つ証明書を生成する場合、ここでそれらを記入する必要があります。 OpenSSLのドキュメントを参照してください。可能なフィールドのリストがあります。

まあ、それだけです。



All Articles