From 22966937de709c0aeff8638fe4c4078480887cd9 Mon Sep 17 00:00:00 2001 From: Christian Hohnstaedt Date: Sat, 19 May 2018 22:01:50 +0200 Subject: [PATCH] Close #36: Support adding CN to X509v3 SAN automatically Translate the special text "DNS:copycn" in the SAN to the final common name. Add checkobox "Copy Common Name" to the SAN Edit box, for a user-friendly editing of the "DNS:copycn" During certificate creation, an empty common name together with "DNS:copycn" in the SAN will raise a warning message and the opportunity to change the settings. Change the SAN in the HTTP-server XCA template from "DNS:your.server.name.here" to "DNS:copycn" to already take advantage of this feature in the default template. --- lib/db_x509.cpp | 8 +------- lib/x509v3ext.cpp | 12 +++++++++++- misc/CA.xca | Bin 68 -> 862 bytes misc/HTTPS_client.xca | Bin 68 -> 860 bytes misc/HTTPS_server.xca | Bin 93 -> 882 bytes ui/v3ext.ui | 33 ++++++++++++++++----------------- widgets/NewX509.cpp | 33 ++++++++++++++++++++++++++------- widgets/v3ext.cpp | 23 +++++++++++++++++------ 8 files changed, 71 insertions(+), 38 deletions(-) diff --git a/lib/db_x509.cpp b/lib/db_x509.cpp index 3e6c4b43..0ec80f26 100644 --- a/lib/db_x509.cpp +++ b/lib/db_x509.cpp @@ -474,7 +474,6 @@ pki_x509 *db_x509::newCert(NewX509 *dlg) pki_x509req *req = NULL; pki_key *signkey = NULL, *clientkey = NULL, *tempkey = NULL; a1int serial; - x509name subject; QString intname; try { @@ -484,7 +483,6 @@ pki_x509 *db_x509::newCert(NewX509 *dlg) clientkey = dlg->getSelectedKey(); if (!clientkey) return NULL; - subject = dlg->getX509name(); intname = dlg->description->text(); } else { // A PKCS#10 Request was selected @@ -496,10 +494,6 @@ pki_x509 *db_x509::newCert(NewX509 *dlg) clientkey = req->getPubKey(); tempkey = clientkey; } - if (dlg->reqSubChange->isChecked()) - subject = dlg->getX509name(); - else - subject = req->getSubject(); intname = req->getIntName(); } TransThrow(); @@ -509,7 +503,7 @@ pki_x509 *db_x509::newCert(NewX509 *dlg) // initially create cert cert = new pki_x509(); cert->setIntName(intname); - cert->setSubject(subject); + cert->setSubject(dlg->getX509name()); cert->setPubKey(clientkey); // Step 2 - select Signing diff --git a/lib/x509v3ext.cpp b/lib/x509v3ext.cpp index 6ed9cb9b..3bf83d32 100644 --- a/lib/x509v3ext.cpp +++ b/lib/x509v3ext.cpp @@ -53,7 +53,17 @@ x509v3ext &x509v3ext::create(int nid, const QString &et, X509V3_CTX *ctx) ext = NULL; } if (!et.isEmpty()) { - QByteArray ba = et.toLocal8Bit(); + QString etext = et; + if (et.contains("DNS:copycn") && ctx && ctx->subject_cert && + nid == NID_subject_alt_name) + { + x509name xn(X509_get_subject_name(ctx->subject_cert)); + QString cn = xn.getEntryByNid(NID_commonName); + if (!cn.isEmpty()) + etext.replace(QString("DNS:copycn"), + QString("DNS:%1").arg(cn)); + } + QByteArray ba = etext.toLocal8Bit(); ext = X509V3_EXT_conf_nid(NULL, ctx, nid, ba.data()); } if (!ext) diff --git a/misc/CA.xca b/misc/CA.xca index a24b48cffac0d8b8c365048c627faeaec58a4701..6cbf40916efe29d0d39e730f0a6aa7a6fbc2a1e4 100644 GIT binary patch literal 862 zcmaiz%}YX25XBEAEkaz#BO*kiRm*GF@%j3b&}0VkMerpf=wH)ns5fXKy*0V%>8r|EA^EOLS2SeW<5<4fW;L z8am3Xbz&@u5Niq^q^cD3k(;20dMBpoZC(!`E6vo0VM#tS-gfa#GOlCiGOv{T={4o? zvf~ZjO+AAY7OASWGoGR?vN4aiA&TW0W$yy|L}hb4r33m@lhu8adBp0#Zk_Dom5+Uv zdDOM5ijd_E*)NEa=QxLBrWdRUr^&D7X|)b?OBHi$))~t!OexYE^Jq}j#Otz}l5Yf$ z3Ybl)>v8`0Y#55aylgkFbp;y1xZrG?=@GpD#T~LAyW*DM2wN2LBP3FyuQuzAtFSgRm&`DZ`vZlkWyNPjw4FrjN~Kx>+5&!o!}ypXSi4I zd3@h_Ne4YiM^{{`agMdtK&hr0>6!PTZgi(x{#64@$JsNqC0S?~)d{oqV>aY5$Fo?TQ4!~@Jt~{yX`QexofX!Bx*%I0V?09Mi^8L+ zeHHi&@5y`tLx^z+My@xsDPNmk%hBo_>46I7=&UO~FPl)L4`$J#qN%IN>PdePY&C2) zp}ynU@^O{<8HZj{Sq*l>kf+2gv}>Lx;Qq&(up9e$D`5By^7%oDROqYCy6xC)awcUr zC!57JuZZ1?w`)dUB6Hj4q3f(osO!2lL2FXswW3RtU~_#B|MZXadQ8PJpBIZ6v5)U$ IyCD|&1yo0b7XSbN literal 68 zcmZQzV6b6eU|?flWB?%s1|}e8%muN4fEPrt0&zuhqC#?NQAuW6W^!UlDo7Xz3?LK$ Dsi6ke diff --git a/misc/HTTPS_server.xca b/misc/HTTPS_server.xca index ebaaa8156621f45c881921f4a956581dccc613d4..78ff5cc63a80b48eb097dd591d165bc6c3cdee99 100644 GIT binary patch literal 882 zcmaiy%S!@L6vjU)T7lHVMrz|!$w8R7fqV|_4PaFj&zI4JKQrf z=kcBIJfzhpNgD?UmAE<0)Y3qGwROY)x{h_K6TS+OrRMe#+7(%7994_7siu0=wK{rt z?IE(Lpe>W-l^iV%v75fiUqk0gG}eTOM3!L(nW9heZ{Ya?50hV!IAvb9SY|{gpuWO! zOl%Y1fw^$D9Lu~$+&8Z@win!9A#UibMxYq>+!1$?mf4WI+>j-3H;$FedI$Fj-`1TY z;RVc7GQ=4BV2pK-cED5RZ5*x6wl1k)g3h`Uc-hp7 zdS*TqD(ZVpR*!Wm%(R5n=GOE~e|hD;d0Fh4Bln3)^>fCUv!&vy5v#sl_ykY-I9l)wZ1ltkzqbQQiC1GYC$ezbTl#XrDtD7y2u@ T-KFM)Cmf4uvyl(5od-UjDVu~M literal 93 zcmZQzUSd%B br2-`@k`on@Q;SM6(=wA2OF$wZU;v>2We*TK diff --git a/ui/v3ext.ui b/ui/v3ext.ui index e699b9f6..ad795cc5 100644 --- a/ui/v3ext.ui +++ b/ui/v3ext.ui @@ -12,11 +12,22 @@ - - - Critical - - + + + + + Critical + + + + + + + Copy Common Name + + + + @@ -39,12 +50,6 @@ - - 6 - - - 0 - @@ -78,12 +83,6 @@ - - 6 - - - 0 - diff --git a/widgets/NewX509.cpp b/widgets/NewX509.cpp index 0854fdf2..31f86ee0 100644 --- a/widgets/NewX509.cpp +++ b/widgets/NewX509.cpp @@ -797,6 +797,9 @@ x509name NewX509::getX509name(int _throw) x509name x; int j, row, nid; + if (fromReqCB->isChecked() && !reqSubChange->isChecked()) + return getSelectedReq()->getSubject(); + try { foreach(nameEdit ne, nameEdits) { x.addEntryByNid(ne.nid, ne.edit->text()); @@ -857,15 +860,14 @@ void NewX509::setupTmpCtx() // initially create temporary ctx cert if (ctx_cert) - delete(ctx_cert); + delete ctx_cert; ctx_cert = new pki_x509(); + ctx_cert->setSubject(getX509name()); if (fromReqCB->isChecked()) { req = getSelectedReq(); - ctx_cert->setSubject(req->getSubject()); if (req) key = req->getRefKey(); } else { - ctx_cert->setSubject(getX509name()); key = getSelectedKey(); } if (key) @@ -1097,10 +1099,7 @@ QString NewX509::mandatoryDnRemain() if (QString(Settings["mandatory_dn"]).isEmpty()) return QString(); - if (fromReqCB->isChecked() && !reqSubChange->isChecked()) - n = getSelectedReq()->getSubject(); - else - n = getX509name(); + n = getX509name(); for (i=0; i< n.entryCount(); i++) { int j = dnl.indexOf(QString(OBJ_nid2sn(n.nid(i)))); @@ -1345,5 +1344,25 @@ void NewX509::accept() break; } } + QString cn = xn.getEntryByNid(NID_commonName); + QStringList san = subAltName->text().split(QRegExp(" *, *")); + if (cn.isEmpty() && san.contains("DNS:copycn") && pt != tmpl) { + gotoTab(2); + xcaWarning msg(this, tr("The subject alternative name shall contain a copy of the common name. However, the common name is empty.")); + msg.addButton(QMessageBox::Ok)->setText(tr("Edit extensions")); + msg.addButton(QMessageBox::Close)->setText(tr("Abort rollout")); + msg.addButton(QMessageBox::Apply)->setText(tr("Continue rollout")); + switch (msg.exec()) + { + case QMessageBox::Ok: + case QMessageBox::Cancel: + return; + case QMessageBox::Close: + reject(); + return; + case QMessageBox::Apply: + break; + } + } QDialog::accept(); } diff --git a/widgets/v3ext.cpp b/widgets/v3ext.cpp index 08a8d193..15e23733 100644 --- a/widgets/v3ext.cpp +++ b/widgets/v3ext.cpp @@ -41,6 +41,8 @@ void v3ext::addInfo(QLineEdit *myle, const QStringList &sl, int n, this, SLOT(setupLineEdit(const QString &, QLineEdit *))); if (le && !le->text().trimmed().isEmpty()) addItem(le->text()); + if (n != NID_subject_alt_name) + copy_cn->hide(); } void v3ext::addItem(QString list) @@ -52,8 +54,12 @@ void v3ext::addItem(QString list) sl.takeFirst(); critical->setChecked(true); } - for (i=0; i< sl.count(); i++) - addEntry(sl[i]); + for (i=0; i< sl.count(); i++) { + if (sl[i] == "DNS:copycn" && nid == NID_subject_alt_name) + copy_cn->setChecked(true); + else + addEntry(sl[i]); + } } void v3ext::setupLineEdit(const QString &s, QLineEdit *l) @@ -67,17 +73,20 @@ void v3ext::setupLineEdit(const QString &s, QLineEdit *l) else tt = tr("An email address"); } else if (s == "RID") { - tt = tr("a registered ID: OBJECT IDENTIFIER"); + tt = tr("A registered ID: OBJECT IDENTIFIER"); QRegExp rx("[a-zA-Z0-9.]+"); v = new QRegExpValidator(rx, this); } else if (s == "URI") { - tt = tr("a uniform resource indicator"); + tt = tr("A uniform resource indicator"); QRegExp rx("[a-z]+://.*"); v = new QRegExpValidator(rx, this); } else if (s == "DNS") { - tt = tr("a DNS domain name"); + if (nid == NID_subject_alt_name) + tt = tr("A DNS domain name or 'copycn'"); + else + tt = tr("A DNS domain name"); } else if (s == "IP") { - tt = tr("an IP address"); + tt = tr("An IP address"); v = new ipValidator(); } else if (s == "otherName") { tt = tr("Syntax: ;TYPE:text like '1.2.3.4:UTF8:name'"); @@ -123,6 +132,8 @@ QString v3ext::toString() if (critical->isChecked()) str << "critical"; + if (copy_cn->isChecked()) + str << "DNS:copycn"; for (i=0; igetRow(i);