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.
This commit is contained in:
Christian Hohnstaedt 2018-05-19 22:01:50 +02:00
parent 4b2375101b
commit 22966937de
8 changed files with 71 additions and 38 deletions

View File

@ -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

View File

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -12,11 +12,22 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="critical">
<property name="text">
<string notr="true">Critical</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="critical">
<property name="text">
<string notr="true">Critical</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="copy_cn">
<property name="text">
<string>Copy Common Name</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
@ -39,12 +50,6 @@
</item>
<item>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="addEntry">
<property name="text">
@ -78,12 +83,6 @@
</item>
<item>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="apply">
<property name="text">

View File

@ -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();
}

View File

@ -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: <OID>;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; i<row; i++) {
QStringList s = tab->getRow(i);