mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
1150 lines
27 KiB
C++
1150 lines
27 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2011 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "x509v3ext.h"
|
|
#include "x509name.h"
|
|
#include "asn1int.h"
|
|
#include "func.h"
|
|
#include "exception.h"
|
|
#include <openssl/x509v3.h>
|
|
#include <openssl/stack.h>
|
|
#include <QStringList>
|
|
#include <QRegularExpression>
|
|
#include <QDebug>
|
|
#include "base.h"
|
|
#include "BioByteArray.h"
|
|
|
|
x509v3ext::x509v3ext()
|
|
{
|
|
}
|
|
|
|
x509v3ext::x509v3ext(const X509_EXTENSION *n)
|
|
{
|
|
if (n) {
|
|
ext = X509_EXTENSION_dup((X509_EXTENSION *)n);
|
|
Q_CHECK_PTR(ext);
|
|
}
|
|
}
|
|
|
|
x509v3ext::x509v3ext(const x509v3ext &n)
|
|
{
|
|
set(n.ext);
|
|
}
|
|
|
|
x509v3ext::x509v3ext(int nid, const QString &et, X509V3_CTX *ctx)
|
|
{
|
|
create(nid, et, ctx);
|
|
}
|
|
|
|
x509v3ext::~x509v3ext()
|
|
{
|
|
if (ext)
|
|
X509_EXTENSION_free(ext);
|
|
}
|
|
|
|
x509v3ext &x509v3ext::set(const X509_EXTENSION *n)
|
|
{
|
|
if (n) {
|
|
ASN1_OCTET_STRING *str = X509_EXTENSION_get_data((X509_EXTENSION *)n);
|
|
if (!str || !str->length)
|
|
n = nullptr;
|
|
}
|
|
if (ext != nullptr)
|
|
X509_EXTENSION_free(ext);
|
|
ext = n ? X509_EXTENSION_dup((X509_EXTENSION *)n) : nullptr;
|
|
openssl_error();
|
|
return *this;
|
|
}
|
|
|
|
x509v3ext &x509v3ext::create(int nid, const QString &et, X509V3_CTX *ctx)
|
|
{
|
|
if (ext) {
|
|
X509_EXTENSION_free(ext);
|
|
ext = nullptr;
|
|
}
|
|
openssl_error();
|
|
if (!et.isEmpty()) {
|
|
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));
|
|
QStringList new_san;
|
|
int i, max = xn.entryCount();
|
|
for (i=0; i < max; i++) {
|
|
if (xn.nid(i) == NID_commonName)
|
|
new_san << QString("DNS:%1").arg(xn.getEntry(i));
|
|
}
|
|
qDebug() << "COUNT" << new_san.size() << new_san.join(",");
|
|
if (new_san.size() > 0)
|
|
etext.replace(QString("DNS:copycn"), new_san.join(","));
|
|
}
|
|
if (nid == NID_subject_alt_name || nid == NID_issuer_alt_name || nid == NID_name_constraints) {
|
|
QStringList sl = etext.split(",");
|
|
QRegularExpression match("([a-z]+;)*UPN:");
|
|
for (int i=0; i<sl.size(); i++) {
|
|
QString s = sl[i].trimmed();
|
|
sl[i] = s.replace(match, "\\1otherName:msUPN;UTF8:");
|
|
}
|
|
etext = sl.join(",");
|
|
}
|
|
QByteArray ba = etext.toLocal8Bit();
|
|
ext = X509V3_EXT_conf_nid(NULL, ctx, nid, ba.data());
|
|
}
|
|
if (ext) {
|
|
if (ctx && ctx->subject_cert) {
|
|
X509_add_ext(ctx->subject_cert, ext, -1);
|
|
}
|
|
}
|
|
ign_openssl_error();
|
|
return *this;
|
|
}
|
|
|
|
x509v3ext &x509v3ext::create_ia5(int nid, const QString &et, X509V3_CTX *ctx)
|
|
{
|
|
QByteArray ba = et.toLocal8Bit();
|
|
for (int i=0; i<ba.size(); i++) {
|
|
if (ba[i] & 0x80)
|
|
throw errorEx(QObject::tr("String '%1' for '%2' contains invalid characters").arg(et).arg(OBJ_nid2ln(nid)));
|
|
}
|
|
return create(nid, et, ctx);
|
|
}
|
|
|
|
const ASN1_OBJECT *x509v3ext::object() const
|
|
{
|
|
ASN1_OBJECT *obj = nullptr;
|
|
if (ext) {
|
|
obj = X509_EXTENSION_get_object(ext);
|
|
ign_openssl_error();
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
int x509v3ext::nid() const
|
|
{
|
|
const ASN1_OBJECT *obj = object();
|
|
if (!obj)
|
|
return NID_undef;
|
|
int nid = OBJ_obj2nid(obj);
|
|
if (nid == NID_undef) {
|
|
QString o = OBJ_obj2QString(obj, 1);
|
|
if (!o.isEmpty())
|
|
nid = OBJ_create(CCHAR(o), CCHAR(o), CCHAR(o));
|
|
openssl_error();
|
|
}
|
|
return nid;
|
|
}
|
|
|
|
void *x509v3ext::d2i() const
|
|
{
|
|
if (!ext)
|
|
return nullptr;
|
|
|
|
void *r = X509V3_EXT_d2i(ext);
|
|
ign_openssl_error();
|
|
return r;
|
|
}
|
|
|
|
x509v3ext &x509v3ext::operator = (const x509v3ext &x)
|
|
{
|
|
set(x.ext);
|
|
return *this;
|
|
}
|
|
|
|
QString x509v3ext::getObject() const
|
|
{
|
|
const ASN1_OBJECT *obj = object();
|
|
return obj ? OBJ_obj2QString(obj) : QString("INVALID");
|
|
}
|
|
|
|
int x509v3ext::getCritical() const
|
|
{
|
|
return ext ? X509_EXTENSION_get_critical(ext) : 0;
|
|
}
|
|
|
|
ASN1_OCTET_STRING *x509v3ext::getData() const
|
|
{
|
|
return ext ? X509_EXTENSION_get_data(ext) : nullptr;;
|
|
}
|
|
|
|
QString x509v3ext::getValue() const
|
|
{
|
|
BioByteArray bba;
|
|
if (!isValid())
|
|
return QString();
|
|
int ret = X509V3_EXT_print(bba, ext, X509V3_EXT_DEFAULT, 0);
|
|
if (ign_openssl_error() || !ret)
|
|
ret = ASN1_STRING_print(bba, (ASN1_STRING *)getData());
|
|
if (ign_openssl_error() || !ret)
|
|
return QString();
|
|
return bba.qstring().trimmed();
|
|
}
|
|
|
|
QString x509v3ext::getHtmlValue() const
|
|
{
|
|
QString text = getValue();
|
|
text.replace(QRegularExpression("&"), "&");
|
|
text.replace(QRegularExpression("<"), "<");
|
|
text.replace(QRegularExpression(">"), ">");
|
|
text.replace(QRegularExpression("\n"), "<br>\n");
|
|
return text;
|
|
}
|
|
|
|
QString x509v3ext::getConsoleValue(const QString &indent) const
|
|
{
|
|
QString text = getValue();
|
|
text.replace(QRegularExpression("\n"), QString("\n") + indent);
|
|
return text;
|
|
}
|
|
|
|
static QString vlist2Section(QStringList vlist, QString tag, QString *sect)
|
|
{
|
|
/* Check for commas in the text */
|
|
if (!vlist.join("").contains(","))
|
|
return vlist.join(", ");
|
|
|
|
*sect += QString("\n[%1_sect]\n").arg(tag);
|
|
|
|
for (int i=0; i<vlist.count(); i++) {
|
|
QString s = vlist[i];
|
|
int eq = s.indexOf(":");
|
|
*sect += QString("%1.%2=%3\n").arg(s.left(eq)).
|
|
arg(i).arg(s.mid(eq+1));
|
|
}
|
|
return QString("@%1_sect\n").arg(tag);
|
|
}
|
|
|
|
static QString obj2SnOid(const ASN1_OBJECT *a)
|
|
{
|
|
QString obj;
|
|
int nid = OBJ_obj2nid(a);
|
|
|
|
if (nid == NID_undef)
|
|
obj = OBJ_obj2QString(a);
|
|
else
|
|
obj = OBJ_nid2sn(nid);
|
|
|
|
return obj;
|
|
}
|
|
|
|
static const char *asn1Type2Name(int type)
|
|
{
|
|
#define ASN1_GEN_STR(x,y) { x,y }
|
|
struct {
|
|
const char *strnam;
|
|
int tag;
|
|
} tags[] = {
|
|
ASN1_GEN_STR("BOOL", V_ASN1_BOOLEAN),
|
|
ASN1_GEN_STR("NULL", V_ASN1_NULL),
|
|
ASN1_GEN_STR("INT", V_ASN1_INTEGER),
|
|
ASN1_GEN_STR("ENUM", V_ASN1_ENUMERATED),
|
|
ASN1_GEN_STR("OID", V_ASN1_OBJECT),
|
|
ASN1_GEN_STR("UTC", V_ASN1_UTCTIME),
|
|
ASN1_GEN_STR("GENTIME", V_ASN1_GENERALIZEDTIME),
|
|
ASN1_GEN_STR("OCT", V_ASN1_OCTET_STRING),
|
|
ASN1_GEN_STR("BITSTR", V_ASN1_BIT_STRING),
|
|
ASN1_GEN_STR("UNIV", V_ASN1_UNIVERSALSTRING),
|
|
ASN1_GEN_STR("IA5", V_ASN1_IA5STRING),
|
|
ASN1_GEN_STR("UTF8", V_ASN1_UTF8STRING),
|
|
ASN1_GEN_STR("BMP", V_ASN1_BMPSTRING),
|
|
ASN1_GEN_STR("VISIBLE", V_ASN1_VISIBLESTRING),
|
|
ASN1_GEN_STR("PRINTABLE", V_ASN1_PRINTABLESTRING),
|
|
ASN1_GEN_STR("T61", V_ASN1_T61STRING),
|
|
ASN1_GEN_STR("GENSTR", V_ASN1_GENERALSTRING),
|
|
ASN1_GEN_STR("NUMERIC", V_ASN1_NUMERICSTRING),
|
|
};
|
|
for (unsigned i=0; i< ARRAY_SIZE(tags); i++) {
|
|
if (tags[i].tag == type)
|
|
return tags[i].strnam;
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
static bool asn1TypePrintable(int type)
|
|
{
|
|
switch (type) {
|
|
case V_ASN1_IA5STRING:
|
|
case V_ASN1_UTF8STRING:
|
|
case V_ASN1_BMPSTRING:
|
|
case V_ASN1_VISIBLESTRING:
|
|
case V_ASN1_PRINTABLESTRING:
|
|
case V_ASN1_T61STRING:
|
|
case V_ASN1_GENERALSTRING:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static QString ipv6_from_binary(const unsigned char *p)
|
|
{
|
|
QString ip;
|
|
int i, skip =0, skiplen = 0, skippos =0;
|
|
|
|
/* find largest gap */
|
|
for (i = 0; i < 17; i += 2) {
|
|
if (i==16 || (p[i] | p[i +1])) {
|
|
if (skiplen < skip) {
|
|
skiplen = skip;
|
|
skippos = i - skip;
|
|
}
|
|
skip = 0;
|
|
} else {
|
|
skip += 2;
|
|
}
|
|
}
|
|
for (i = 0, skip = 0; i < 16; i += 2) {
|
|
int x = p[i] << 8 | p[i+1];
|
|
skip += skippos == i;
|
|
switch (!x*4 + skip) {
|
|
case 5: // skip first 0
|
|
skip = 2;
|
|
ip += ":";
|
|
case 6: // skip next 0
|
|
break;
|
|
default: // no reduction
|
|
skip = 0;
|
|
ip += QString("%1%2").arg(i? ":" : "").arg(x,0,16);
|
|
}
|
|
}
|
|
if (skip == 2)
|
|
ip += ":";
|
|
return ip;
|
|
}
|
|
|
|
static bool
|
|
genName2conf(GENERAL_NAME *gen, QString tag, QString *single, QString *sect)
|
|
{
|
|
unsigned char *p;
|
|
QString ret;
|
|
|
|
switch (gen->type) {
|
|
case GEN_EMAIL: ret = "email:%1"; break;
|
|
case GEN_DNS: ret = "DNS:%1"; break;
|
|
case GEN_URI: ret = "URI:%1"; break;
|
|
|
|
case GEN_DIRNAME: {
|
|
tag += "_dirname";
|
|
x509name xn(gen->d.dirn);
|
|
*sect += QString("\n[%1]\n"). arg(tag);
|
|
*sect += xn.taggedValues();
|
|
*single = QString("dirName:") + tag;
|
|
return true;
|
|
}
|
|
case GEN_IPADD:
|
|
p = gen->d.ip->data;
|
|
if (gen->d.ip->length == 4) {
|
|
*single = QString("IP:%1.%2.%3.%4").
|
|
arg(p[0]).arg(p[1]).arg(p[2]).arg(p[3]);
|
|
return true;
|
|
} else if(gen->d.ip->length == 8) {
|
|
*single = QString("IP:%1.%2.%3.%4/%5.%6.%7.%8").
|
|
arg(p[0]).arg(p[1]).arg(p[2]).arg(p[3]).
|
|
arg(p[4]).arg(p[5]).arg(p[6]).arg(p[7]);
|
|
return true;
|
|
} else if(gen->d.ip->length == 16) {
|
|
*single = "IP:" + ipv6_from_binary(gen->d.ip->data);
|
|
return true;
|
|
} else if(gen->d.ip->length == 32) {
|
|
*single = "IP:" + ipv6_from_binary(gen->d.ip->data) +
|
|
"/" + ipv6_from_binary(gen->d.ip->data +16);
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case GEN_RID:
|
|
*single = QString("RID:%1").
|
|
arg(obj2SnOid(gen->d.rid));
|
|
return true;
|
|
case GEN_OTHERNAME: {
|
|
int type = gen->d.otherName->value->type;
|
|
ASN1_STRING *a;
|
|
a = gen->d.otherName->value->value.asn1_string;
|
|
if (asn1TypePrintable(type)) {
|
|
*single = QString("otherName:%1;%2:%3").
|
|
arg(obj2SnOid(gen->d.otherName->type_id)).
|
|
arg(asn1Type2Name(type)).
|
|
arg(asn1ToQString(a, true));
|
|
} else {
|
|
*single = QString("otherName:%1;FORMAT:HEX,%2").
|
|
arg(obj2SnOid(gen->d.otherName->type_id)).
|
|
arg(asn1Type2Name(type));
|
|
for (int i=0; i<a->length; i++) {
|
|
*single += QString(":%1").
|
|
arg((int)(a->data[i]), 2, 16, QChar('0'));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
if (!ret.isEmpty())
|
|
*single = ret.arg(asn1ToQString(gen->d.ia5, true));
|
|
return true;
|
|
}
|
|
|
|
static bool genNameStack2conf(STACK_OF(GENERAL_NAME) *gens, QString tag,
|
|
QString *single, QString *sect)
|
|
{
|
|
int i;
|
|
QStringList sl;
|
|
for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
|
|
QString one;
|
|
if (!genName2conf(sk_GENERAL_NAME_value(gens, i),
|
|
QString("%1_%2").arg(tag).arg(i), &one, sect))
|
|
{
|
|
return false;
|
|
}
|
|
sl << one;
|
|
}
|
|
*single = vlist2Section(sl, tag, sect);
|
|
return true;
|
|
}
|
|
|
|
QString x509v3ext::parse_critical() const
|
|
{
|
|
return QString(getCritical() ? "critical," : "");
|
|
}
|
|
|
|
#define TEXTS (\
|
|
B_ASN1_TIME | B_ASN1_DIRECTORYSTRING | B_ASN1_DISPLAYTEXT | \
|
|
B_ASN1_NUMERICSTRING | B_ASN1_T61STRING | B_ASN1_UNIVERSALSTRING)
|
|
|
|
bool x509v3ext::parse_ia5(QString *single, QString *adv) const
|
|
{
|
|
ASN1_STRING *str = (ASN1_STRING *)d2i();
|
|
QString ret;
|
|
|
|
if (!isValid())
|
|
return false;
|
|
|
|
if (!str) {
|
|
const unsigned char *p = getData()->data;
|
|
str = d2i_ASN1_OCTET_STRING(NULL, &p, getData()->length);
|
|
if (ign_openssl_error() || !str)
|
|
return false;
|
|
ret = QString("<ERROR: NOT IA5 but %1>%2").
|
|
arg(asn1Type2Name(str->type)).
|
|
arg(QString(asn1ToQString(str)));
|
|
} else {
|
|
ret = QString(asn1ToQString(str));
|
|
}
|
|
if (single)
|
|
*single = ret;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(OBJ_nid2sn(nid())).arg(ret) +*adv;
|
|
|
|
ASN1_STRING_free(str);
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_generalName(QString *single, QString *adv) const
|
|
{
|
|
bool retval = true;
|
|
QString sect, ret;
|
|
STACK_OF(GENERAL_NAME) *gens = (STACK_OF(GENERAL_NAME) *)d2i();
|
|
|
|
if (!gens)
|
|
return false;
|
|
|
|
QString tag = OBJ_nid2sn(nid());
|
|
|
|
if (!genNameStack2conf(gens, tag, &ret, §))
|
|
retval = false;
|
|
else if (sect.isEmpty() && single) {
|
|
*single = parse_critical() + ret;
|
|
} else if (adv) {
|
|
*adv = QString("%1=%2\n").arg(tag).
|
|
arg(parse_critical() +ret) + *adv + sect;
|
|
}
|
|
sk_GENERAL_NAME_free(gens);
|
|
return retval;
|
|
}
|
|
|
|
bool x509v3ext::parse_eku(QString *single, QString *adv) const
|
|
{
|
|
EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)d2i();
|
|
QStringList sl;
|
|
int i;
|
|
|
|
if (!eku)
|
|
return false;
|
|
|
|
for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
|
|
sl << QString(OBJ_obj2sn(sk_ASN1_OBJECT_value(eku, i)));
|
|
}
|
|
QString r = parse_critical() + sl.join(", ");
|
|
if (single)
|
|
*single = r;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(OBJ_nid2sn(nid())).arg(r) + *adv;
|
|
|
|
EXTENDED_KEY_USAGE_free(eku);
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_ainfo(QString *single, QString *adv) const
|
|
{
|
|
bool retval = true;
|
|
QString sect, ret;
|
|
QString tag = OBJ_nid2sn(nid());
|
|
QStringList sl;
|
|
int i;
|
|
|
|
AUTHORITY_INFO_ACCESS *ainfo = (AUTHORITY_INFO_ACCESS *)d2i();
|
|
|
|
if (!ainfo)
|
|
return false;
|
|
|
|
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ainfo); i++) {
|
|
QString one;
|
|
ACCESS_DESCRIPTION *desc = sk_ACCESS_DESCRIPTION_value(ainfo, i);
|
|
if (!genName2conf(desc->location,
|
|
QString("%1_%2").arg(tag).arg(i), &one, §))
|
|
{
|
|
retval = false;
|
|
break;
|
|
}
|
|
sl << QString("%1;%2").arg(OBJ_obj2sn(desc->method)).arg(one);
|
|
}
|
|
if (retval) {
|
|
ret = vlist2Section(sl, tag, §);
|
|
if (sect.isEmpty() && sk_ACCESS_DESCRIPTION_num(ainfo) == 1 && single) {
|
|
*single = parse_critical() + ret;
|
|
} else if (adv) {
|
|
*adv = QString("%1=%2\n").arg(tag).
|
|
arg(parse_critical() + ret) + *adv + sect;
|
|
}
|
|
}
|
|
AUTHORITY_INFO_ACCESS_free(ainfo);
|
|
return retval;
|
|
}
|
|
|
|
static const BIT_STRING_BITNAME reason_flags[] = {
|
|
{0, "", "unused"},
|
|
{1, "", "keyCompromise"},
|
|
{2, "", "CACompromise"},
|
|
{3, "", "affiliationChanged"},
|
|
{4, "", "superseded"},
|
|
{5, "", "cessationOfOperation"},
|
|
{6, "", "certificateHold"},
|
|
{7, "", "privilegeWithdrawn"},
|
|
{8, "", "AACompromise"},
|
|
{-1, NULL, NULL}
|
|
};
|
|
|
|
static QString parse_bits(const BIT_STRING_BITNAME *flags,
|
|
ASN1_BIT_STRING *str)
|
|
{
|
|
const BIT_STRING_BITNAME *pbn;
|
|
QStringList r;
|
|
for (pbn = flags; pbn->sname; pbn++) {
|
|
if (ASN1_BIT_STRING_get_bit(str, pbn->bitnum))
|
|
r << QString(pbn->sname);
|
|
}
|
|
return r.join(", ");
|
|
}
|
|
|
|
bool x509v3ext::parse_Crldp(QString *single, QString *adv) const
|
|
{
|
|
QString othersect;
|
|
QStringList crldps;
|
|
const char *sn = OBJ_nid2sn(nid());
|
|
|
|
STACK_OF(DIST_POINT) *crld = (STACK_OF(DIST_POINT)*)d2i();
|
|
|
|
if (!crld)
|
|
return false;
|
|
|
|
if (sk_DIST_POINT_num(crld) == 1 && single) {
|
|
DIST_POINT *point = sk_DIST_POINT_value(crld, 0);
|
|
if (point->distpoint && !point->reasons && !point->CRLissuer &&
|
|
!point->distpoint->type)
|
|
{
|
|
QString sect, ret;
|
|
if (!genNameStack2conf(point->distpoint->name.fullname,
|
|
"", &ret, §))
|
|
goto could_not_parse;
|
|
|
|
if (sect.isEmpty()) {
|
|
if (single)
|
|
*single = parse_critical() +ret;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(sn).
|
|
arg(parse_critical() +ret) +*adv;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(int i = 0; i < sk_DIST_POINT_num(crld); i++) {
|
|
DIST_POINT *point = sk_DIST_POINT_value(crld, i);
|
|
QString tag = QString("crlDistributionPoint%1_sect").arg(i);
|
|
QString crldpsect = QString("\n[%1]\n").arg(tag);
|
|
if (point->distpoint) {
|
|
if (!point->distpoint->type) {
|
|
QString ret;
|
|
if (!genNameStack2conf(point->distpoint->name.fullname,
|
|
tag + "_fullname", &ret, &othersect))
|
|
goto could_not_parse;
|
|
|
|
crldpsect += "fullname=" + ret +"\n";
|
|
} else {
|
|
QString mysect = tag + "_relativename";
|
|
x509name xn(point->distpoint->name.relativename);
|
|
crldpsect += "relativename=" + mysect + "\n";
|
|
othersect += QString("\n[%1]\n").arg(mysect) +
|
|
xn.taggedValues();
|
|
}
|
|
}
|
|
if (point->reasons) {
|
|
crldpsect += QString("reasons=%1\n").
|
|
arg(parse_bits(reason_flags, point->reasons));
|
|
}
|
|
if (point->CRLissuer) {
|
|
QString ret;
|
|
if (genNameStack2conf(point->CRLissuer,
|
|
tag +"_crlissuer", &ret, &othersect))
|
|
goto could_not_parse;
|
|
crldpsect += "CRLissuer=" + ret + "\n";
|
|
}
|
|
crldps << tag;
|
|
othersect = crldpsect + othersect;
|
|
}
|
|
sk_DIST_POINT_free(crld);
|
|
if (crldps.size() == 0)
|
|
return true;
|
|
if (adv) {
|
|
*adv = QString("%1=%2\n").arg(sn).
|
|
arg(parse_critical() + crldps.join(", ")) +
|
|
*adv + othersect;
|
|
}
|
|
return true;
|
|
|
|
could_not_parse:
|
|
sk_DIST_POINT_free(crld);
|
|
return false;
|
|
}
|
|
|
|
static void gen_cpol_notice(QString tag, USERNOTICE *notice, QString *adv)
|
|
{
|
|
*adv += QString("\n[%1]\n").arg(tag);
|
|
if (notice->exptext) {
|
|
*adv += QString("explicitText=%1\n").
|
|
arg(asn1ToQString(notice->exptext, true));
|
|
}
|
|
if (notice->noticeref) {
|
|
NOTICEREF *ref = notice->noticeref;
|
|
QStringList sl;
|
|
int i;
|
|
*adv += QString("organization=%1\n").
|
|
arg(asn1ToQString(ref->organization, true));
|
|
for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) {
|
|
a1int num(sk_ASN1_INTEGER_value(ref->noticenos, i));
|
|
sl << num.toDec();
|
|
}
|
|
if (sl.size())
|
|
*adv += QString("noticeNumbers=%1\n").
|
|
arg(sl.join(", "));
|
|
}
|
|
}
|
|
|
|
static bool gen_cpol_qual_sect(QString tag, POLICYINFO *pinfo, QString *adv)
|
|
{
|
|
QString polsect = QString("\n[%1]\n").arg(tag);
|
|
QString noticetag, _adv;
|
|
STACK_OF(POLICYQUALINFO) *quals = pinfo->qualifiers;
|
|
int i;
|
|
|
|
if (!quals)
|
|
return false;
|
|
|
|
if (!adv)
|
|
adv = &_adv;
|
|
|
|
polsect += QString("policyIdentifier=%1\n").
|
|
arg(obj2SnOid(pinfo->policyid));
|
|
|
|
for (i = 0; i < sk_POLICYQUALINFO_num(quals); i++) {
|
|
POLICYQUALINFO *qualinfo = sk_POLICYQUALINFO_value(quals, i);
|
|
switch (OBJ_obj2nid(qualinfo->pqualid)) {
|
|
case NID_id_qt_cps:
|
|
polsect += QString("CPS.%1=%2\n").arg(i).
|
|
arg(asn1ToQString(qualinfo->d.cpsuri, true));
|
|
break;
|
|
case NID_id_qt_unotice:
|
|
noticetag = QString("%1_notice%2_sect").arg(tag).arg(i);
|
|
polsect += QString("userNotice.%1=@%2\n").arg(i).
|
|
arg(noticetag);
|
|
gen_cpol_notice(noticetag, qualinfo->d.usernotice, adv);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
*adv = polsect + *adv;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool x509v3ext::parse_certpol(QString *, QString *adv) const
|
|
{
|
|
bool retval = true;
|
|
QStringList pols;
|
|
QString myadv;
|
|
int i;
|
|
STACK_OF(POLICYINFO) *pol = (STACK_OF(POLICYINFO) *)d2i();
|
|
|
|
if (!pol)
|
|
return false;
|
|
|
|
for (i = 0; i < sk_POLICYINFO_num(pol); i++) {
|
|
POLICYINFO *pinfo = sk_POLICYINFO_value(pol, i);
|
|
if (!pinfo->qualifiers) {
|
|
pols << obj2SnOid(pinfo->policyid);
|
|
continue;
|
|
}
|
|
QString tag = QString("certpol%1_sect").arg(i);
|
|
pols << QString("@") + tag;
|
|
if (!gen_cpol_qual_sect(tag, pinfo, &myadv)) {
|
|
retval = false;
|
|
break;
|
|
}
|
|
}
|
|
if (retval && adv)
|
|
*adv = QString("certificatePolicies=%1ia5org,%2\n").
|
|
arg(parse_critical()).arg(pols.join(", ")) + *adv + myadv;
|
|
sk_POLICYINFO_free(pol);
|
|
return retval;
|
|
}
|
|
|
|
bool x509v3ext::parse_bc(QString *single, QString *adv) const
|
|
{
|
|
BASIC_CONSTRAINTS *bc = (BASIC_CONSTRAINTS *)d2i();
|
|
|
|
if (!bc)
|
|
return false;
|
|
|
|
QString ret = a1int(bc->pathlen).toDec();
|
|
if (!ret.isEmpty())
|
|
ret = ",pathlen:" + ret;
|
|
ret = parse_critical() + (bc->ca ? "CA:TRUE" : "CA:FALSE") + ret;
|
|
if (single)
|
|
*single = ret;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(OBJ_nid2sn(nid())).arg(ret) +*adv;
|
|
BASIC_CONSTRAINTS_free(bc);
|
|
return true;
|
|
}
|
|
|
|
static const BIT_STRING_BITNAME key_usage_type_table[] = {
|
|
{0, "Digital Signature", "digitalSignature"},
|
|
{1, "Non Repudiation", "nonRepudiation"},
|
|
{2, "Key Encipherment", "keyEncipherment"},
|
|
{3, "Data Encipherment", "dataEncipherment"},
|
|
{4, "Key Agreement", "keyAgreement"},
|
|
{5, "Certificate Sign", "keyCertSign"},
|
|
{6, "CRL Sign", "cRLSign"},
|
|
{7, "Encipher Only", "encipherOnly"},
|
|
{8, "Decipher Only", "decipherOnly"},
|
|
{-1, NULL, NULL}
|
|
};
|
|
|
|
static const BIT_STRING_BITNAME ns_cert_type_table[] = {
|
|
{0, "SSL Client", "client"},
|
|
{1, "SSL Server", "server"},
|
|
{2, "S/MIME", "email"},
|
|
{3, "Object Signing", "objsign"},
|
|
{4, "Unused", "reserved"},
|
|
{5, "SSL CA", "sslCA"},
|
|
{6, "S/MIME CA", "emailCA"},
|
|
{7, "Object Signing CA", "objCA"},
|
|
{-1, NULL, NULL}
|
|
};
|
|
|
|
bool x509v3ext::parse_bitstring(QString *single, QString *adv) const
|
|
{
|
|
ASN1_BIT_STRING *bs;
|
|
const BIT_STRING_BITNAME *bnames;
|
|
int n = nid();
|
|
|
|
switch (n) {
|
|
case NID_key_usage: bnames = key_usage_type_table; break;
|
|
case NID_netscape_cert_type: bnames = ns_cert_type_table; break;
|
|
default: return false;
|
|
}
|
|
bs = (ASN1_BIT_STRING *)d2i();
|
|
|
|
if (!bs)
|
|
return false;
|
|
|
|
QString ret = parse_critical() + parse_bits(bnames, bs);
|
|
if (single)
|
|
*single = ret;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(OBJ_nid2sn(nid())).arg(ret) +*adv;
|
|
ASN1_BIT_STRING_free(bs);
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_sKeyId(QString *, QString *adv) const
|
|
{
|
|
if (adv)
|
|
*adv = QString("%1=hash\n").arg(OBJ_nid2sn(nid())) + *adv;
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_aKeyId(QString *, QString *adv) const
|
|
{
|
|
QStringList ret;
|
|
AUTHORITY_KEYID *akeyid = (AUTHORITY_KEYID *)d2i();
|
|
ign_openssl_error();
|
|
|
|
if (!akeyid)
|
|
return false;
|
|
|
|
if (akeyid->keyid)
|
|
ret << "keyid";
|
|
if (akeyid->issuer)
|
|
ret << "issuer:always";
|
|
if (adv)
|
|
*adv = QString("%1=%2\n").arg(OBJ_nid2sn(nid())).
|
|
arg(ret.join(", ")) + *adv;
|
|
AUTHORITY_KEYID_free(akeyid);
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_generic(QString *, QString *adv) const
|
|
{
|
|
if (!isValid())
|
|
return false;
|
|
|
|
const ASN1_OBJECT *o = object();
|
|
QString der, obj = o ? obj2SnOid(o) : QString("INVALID");
|
|
ASN1_OCTET_STRING *v = getData();
|
|
|
|
for (int i=0; v && i < v->length; i++)
|
|
der += QString(":%1").arg((int)(v->data[i]), 2, 16, QChar('0'));
|
|
|
|
if (adv)
|
|
*adv = QString("%1=%2DER%3\n").arg(obj).
|
|
arg(parse_critical()).arg(der) + *adv;
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_inhibitAnyPolicy(QString *, QString *adv) const
|
|
{
|
|
ASN1_INTEGER *a = (ASN1_INTEGER *)d2i();
|
|
ign_openssl_error();
|
|
|
|
if (!a)
|
|
return false;
|
|
|
|
a1int val(a);
|
|
if (adv) {
|
|
*adv = QString("%1=%2%3\n").arg(OBJ_nid2sn(nid())).
|
|
arg(parse_critical()).arg(val.toDec()) + *adv;
|
|
}
|
|
ASN1_INTEGER_free(a);
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_policyConstraints(QString *, QString *adv) const
|
|
{
|
|
QStringList v;
|
|
a1int a1null(0L), a;
|
|
POLICY_CONSTRAINTS *pol = (POLICY_CONSTRAINTS *)d2i();
|
|
ign_openssl_error();
|
|
|
|
if (!pol)
|
|
return false;
|
|
|
|
a = a1int(pol->requireExplicitPolicy);
|
|
if (a != a1null)
|
|
v << QString("requireExplicitPolicy:%1").arg(a.toDec());
|
|
|
|
a = a1int(pol->inhibitPolicyMapping);
|
|
if (a != a1null)
|
|
v << QString("inhibitPolicyMapping:%1").arg(a.toDec());
|
|
|
|
if (adv)
|
|
*adv = QString("%1=%2%3\n").arg(OBJ_nid2sn(nid())).
|
|
arg(parse_critical()).arg(v.join(", ")) + *adv;
|
|
POLICY_CONSTRAINTS_free(pol);
|
|
return true;
|
|
|
|
}
|
|
|
|
bool x509v3ext::parse_policyMappings(QString *, QString *adv) const
|
|
{
|
|
bool retval = true;
|
|
QStringList polMaps;
|
|
QString myadv;
|
|
POLICY_MAPPINGS *pmaps = (POLICY_MAPPINGS *)d2i();
|
|
ign_openssl_error();
|
|
|
|
if (!pmaps)
|
|
return false;
|
|
|
|
for (int i = 0; i < sk_POLICY_MAPPING_num(pmaps); i++) {
|
|
POLICY_MAPPING *pmap = sk_POLICY_MAPPING_value(pmaps, i);
|
|
polMaps << QString("%1 = %2").
|
|
arg(obj2SnOid(pmap->issuerDomainPolicy)).
|
|
arg(obj2SnOid(pmap->subjectDomainPolicy));
|
|
}
|
|
if (polMaps.size() > 0 && adv) {
|
|
*adv = QString("policyMappings=%1@policyMappings_sect\n").
|
|
arg(parse_critical()) + *adv +
|
|
QString("[policyMappings_sect]\n") + polMaps.join("\n");
|
|
}
|
|
sk_POLICY_MAPPING_free(pmaps);
|
|
return retval;
|
|
}
|
|
|
|
static bool nameConstraint(STACK_OF(GENERAL_SUBTREE) *trees,
|
|
QString prefix, QString tag, QString *single, QString *sect)
|
|
{
|
|
QStringList sl;
|
|
for (int i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) {
|
|
QString one;
|
|
GENERAL_SUBTREE *tree = sk_GENERAL_SUBTREE_value(trees, i);
|
|
if (!genName2conf(tree->base,
|
|
QString("%1_%2").arg(tag).arg(i), &one, sect))
|
|
{
|
|
return false;
|
|
}
|
|
qDebug("%s: %d '%s'\n", __func__, i, CCHAR(one));
|
|
sl << prefix + ";" + one;
|
|
}
|
|
*single = vlist2Section(sl, tag+prefix, sect);
|
|
qDebug("Single: '%s'\n", CCHAR(*single));
|
|
return true;
|
|
}
|
|
|
|
bool x509v3ext::parse_nameConstraints(QString *single, QString *adv) const
|
|
{
|
|
bool retval = true;
|
|
QString sect, ret;
|
|
QStringList permEx;
|
|
QString tag = OBJ_nid2sn(nid());
|
|
NAME_CONSTRAINTS *cons = (NAME_CONSTRAINTS *)d2i();
|
|
ign_openssl_error();
|
|
|
|
if (!cons)
|
|
return false;
|
|
|
|
if (!nameConstraint(cons->permittedSubtrees, "permitted",
|
|
tag, &ret, §))
|
|
retval = false;
|
|
if (ret.size() > 0)
|
|
permEx << ret;
|
|
if (!nameConstraint(cons->excludedSubtrees, "excluded",
|
|
tag, &ret, §))
|
|
retval = false;
|
|
if (ret.size() > 0)
|
|
permEx << ret;
|
|
|
|
if (retval && permEx.size() > 0) {
|
|
ret = permEx.join(", ");
|
|
qDebug() << retval << ret;
|
|
if (single)
|
|
*single = ret;
|
|
else if (adv)
|
|
*adv = QString("%1=%2\n").arg(tag).
|
|
arg(parse_critical() +ret) + *adv + sect;
|
|
}
|
|
NAME_CONSTRAINTS_free(cons);
|
|
return retval;
|
|
}
|
|
|
|
bool x509v3ext::genConf(QString *single, QString *adv) const
|
|
{
|
|
int n = nid();
|
|
switch (n) {
|
|
case NID_crl_distribution_points:
|
|
return parse_Crldp(single, adv);
|
|
case NID_subject_alt_name:
|
|
case NID_issuer_alt_name:
|
|
return parse_generalName(single, adv);
|
|
case NID_info_access:
|
|
return parse_ainfo(single, adv);
|
|
case NID_ext_key_usage:
|
|
return parse_eku(single, adv);
|
|
case NID_certificate_policies:
|
|
return parse_certpol(single, adv);
|
|
case NID_netscape_comment:
|
|
case NID_netscape_base_url:
|
|
case NID_netscape_revocation_url:
|
|
case NID_netscape_ca_revocation_url:
|
|
case NID_netscape_renewal_url:
|
|
case NID_netscape_ca_policy_url:
|
|
case NID_netscape_ssl_server_name:
|
|
return parse_ia5(single, adv);
|
|
case NID_basic_constraints:
|
|
return parse_bc(single, adv);
|
|
case NID_key_usage:
|
|
case NID_netscape_cert_type:
|
|
return parse_bitstring(single, adv);
|
|
case NID_subject_key_identifier:
|
|
return parse_sKeyId(single, adv);
|
|
case NID_authority_key_identifier:
|
|
return parse_aKeyId(single, adv);
|
|
case NID_inhibit_any_policy:
|
|
return parse_inhibitAnyPolicy(single, adv);
|
|
case NID_policy_constraints:
|
|
return parse_policyConstraints(single, adv);
|
|
case NID_policy_mappings:
|
|
return parse_policyMappings(single, adv);
|
|
case NID_name_constraints:
|
|
return parse_nameConstraints(single, adv);
|
|
case NID_id_pkix_OCSP_noCheck:
|
|
if (adv)
|
|
*adv = "noCheck = ignored\n" + *adv;
|
|
return true;
|
|
default:
|
|
return parse_generic(single, adv);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString x509v3ext::getHtml() const
|
|
{
|
|
QString html;
|
|
html = "<b><u>" + getObject();
|
|
if (getCritical() != 0)
|
|
html += " <font color=\"red\">critical</font>";
|
|
html += ":</u></b><br><tt>" + getHtmlValue() + "</tt>";
|
|
return html;
|
|
}
|
|
|
|
QString x509v3ext::getConsole(const QString &indent) const
|
|
{
|
|
QString text, twoind = indent + indent;
|
|
text = indent + COL_BOLD COL_UNDER + getObject();
|
|
if (getCritical() != 0)
|
|
text += " " COL_RED COL_UNDER "[critical]";
|
|
text += COL_RESET "\n" + twoind + getConsoleValue(twoind);
|
|
return text;
|
|
}
|
|
|
|
X509_EXTENSION *x509v3ext::get() const
|
|
{
|
|
return ext ? X509_EXTENSION_dup(ext) : nullptr;
|
|
}
|
|
|
|
bool x509v3ext::isValid() const
|
|
{
|
|
return ext && getData() && getData()->length > 0 &&
|
|
OBJ_obj2nid(X509_EXTENSION_get_object(ext)) != NID_undef;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
bool extList::genConf(int nid, QString *single, QString *adv)
|
|
{
|
|
int i = idxByNid(nid);
|
|
if (i != -1) {
|
|
if (at(i).genConf(single, adv))
|
|
removeAt(i);
|
|
ign_openssl_error();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void extList::genGenericConf(QString *adv)
|
|
{
|
|
for (int i=0; i< size();) {
|
|
if (at(i).genConf(NULL, adv) || at(i).parse_generic(NULL, adv))
|
|
removeAt(i);
|
|
else
|
|
i++;
|
|
ign_openssl_error();
|
|
}
|
|
}
|
|
|
|
void extList::setStack(const STACK_OF(X509_EXTENSION) *st, int start)
|
|
{
|
|
clear();
|
|
int cnt = sk_X509_EXTENSION_num(st);
|
|
x509v3ext e;
|
|
for (int i=start; i<cnt; i++) {
|
|
e.set(sk_X509_EXTENSION_value(st,i));
|
|
append(e);
|
|
}
|
|
}
|
|
|
|
STACK_OF(X509_EXTENSION) *extList::getStack() const
|
|
{
|
|
STACK_OF(X509_EXTENSION) *sk = sk_X509_EXTENSION_new_null();
|
|
Q_CHECK_PTR(sk);
|
|
foreach(const x509v3ext &e, *this)
|
|
sk_X509_EXTENSION_push(sk, e.get());
|
|
return sk;
|
|
}
|
|
|
|
bool extList::search(const QRegularExpression &pattern) const
|
|
{
|
|
foreach(const x509v3ext &e, *this)
|
|
if (e.getValue().contains(pattern))
|
|
return true;
|
|
return false;
|
|
}
|
|
QString extList::getHtml(const QString &sep) const
|
|
{
|
|
x509v3ext e;
|
|
QStringList s;
|
|
foreach(const x509v3ext &e, *this)
|
|
s << e.getHtml();
|
|
QString a = s.join(sep);
|
|
return a;
|
|
}
|
|
|
|
QString extList::getConsole(const QString &indent) const
|
|
{
|
|
x509v3ext e;
|
|
QStringList s;
|
|
foreach(const x509v3ext &e, *this)
|
|
s << e.getConsole(indent);
|
|
QString a = "\n" + s.join("\n");
|
|
return a;
|
|
}
|
|
|
|
|
|
bool extList::delByNid(int nid)
|
|
{
|
|
for(int i = 0; i< size(); i++) {
|
|
if (at(i).nid() == nid) {
|
|
removeAt(i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int extList::idxByNid(int nid) const
|
|
{
|
|
for(int i = 0; i< size(); i++) {
|
|
if (at(i).nid() == nid) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int extList::delInvalid(void)
|
|
{
|
|
int removed=0;
|
|
QMutableListIterator<x509v3ext> i(*this);
|
|
while (i.hasNext()) {
|
|
if (!i.next().isValid()) {
|
|
i.remove();
|
|
removed=1;
|
|
}
|
|
}
|
|
return removed;
|
|
}
|