Close #518: Export certificates for ovpn file

Create ovpn file with "ca", "cert" and "key"
Additionally put all intermediate CAs (if any)
tagged as "extra-certs".

"tls-auth" "OpenVPN Static key V1" not supported (yet)

Extend export-test by OpenVPN test case
This commit is contained in:
Christian Hohnstaedt 2024-02-11 00:24:19 +01:00
parent 6d01928d5d
commit 0a1a21608d
9 changed files with 48 additions and 5 deletions

View File

@ -566,6 +566,15 @@ int db_x509::exportFlags(const QModelIndex &idx) const
return disable_flags; return disable_flags;
} }
void db_x509::writeTaggedPEM(BioByteArray &b, const QString &tag, XFile &file)
{
if (b.size() > 0) {
file.write(QString("<%1>\n").arg(tag).toLatin1());
file.write(b);
file.write(QString("</%1>\n").arg(tag).toLatin1());
}
}
void db_x509::exportItems(const QModelIndexList &list, void db_x509::exportItems(const QModelIndexList &list,
const pki_export *xport, XFile &file) const const pki_export *xport, XFile &file) const
{ {
@ -582,7 +591,25 @@ void db_x509::exportItems(const QModelIndexList &list,
} }
if (xport->match_all(F_PEM)) { if (xport->match_all(F_PEM)) {
if (xport->match_all(F_CHAIN)) { if (xport->match_all(F_OVPN)) {
BioByteArray key, cert, extra, ca;
pki_evp *pkey = (pki_evp *)crt->getRefKey();
if (pkey)
pkey->pem(key, pki_export::by_id(20)); // PEM unencrypted
for (; crt && crt != oldcrt; oldcrt = crt, crt = crt->getSigner())
{
if (crt == crt->getSigner())
crt->pem(ca);
else if (cert.size() == 0)
crt->pem(cert);
else
crt->pem(extra);
}
writeTaggedPEM(ca, "ca", file);
writeTaggedPEM(extra, "extra-certs", file);
writeTaggedPEM(cert, "cert", file);
writeTaggedPEM(key, "key", file);
} else if (xport->match_all(F_CHAIN)) {
for (; crt && crt != oldcrt; oldcrt = crt, crt = crt->getSigner()) for (; crt && crt != oldcrt; oldcrt = crt, crt = crt->getSigner())
crt->writeCert(file, true); crt->writeCert(file, true);
} else if (xport->match_all(F_UNREVOKED)) { } else if (xport->match_all(F_UNREVOKED)) {

View File

@ -23,6 +23,8 @@ class db_x509: public db_x509super
{ {
Q_OBJECT Q_OBJECT
static void writeTaggedPEM(BioByteArray &b,
const QString &tag, XFile &file);
protected: protected:
dbheaderList getHeaders(); dbheaderList getHeaders();
void dereferenceIssuer(); void dereferenceIssuer();

View File

@ -768,11 +768,10 @@ QSqlError pki_evp::deleteSqlData()
return q.lastError(); return q.lastError();
} }
bool pki_evp::pem(BioByteArray &b) bool pki_evp::pem(BioByteArray &b, const pki_export *xport)
{ {
EVP_PKEY *pkey; EVP_PKEY *pkey;
int keytype; int keytype;
const pki_export *xport = pki_export::by_id(Settings["KeyFormat"]);
if (xport->match_all(F_PEM | F_PRIVATE)) { if (xport->match_all(F_PEM | F_PRIVATE)) {
pkey = decryptKey(); pkey = decryptKey();

View File

@ -62,7 +62,7 @@ class pki_evp: public pki_key
static QString removeTypeFromIntName(QString n); static QString removeTypeFromIntName(QString n);
void fromPEMbyteArray(const QByteArray &ba, const QString &name); void fromPEMbyteArray(const QByteArray &ba, const QString &name);
void fload(const QString &fname); void fload(const QString &fname);
bool pem(BioByteArray &); virtual bool pem(BioByteArray &b, const pki_export *xport);
EVP_PKEY *load_ssh_ed25519_privatekey(const QByteArray &ba, EVP_PKEY *load_ssh_ed25519_privatekey(const QByteArray &ba,
const pass_info &p); const pass_info &p);
void writeDefault(const QString &dirname) const; void writeDefault(const QString &dirname) const;

View File

@ -58,6 +58,7 @@ QList<pki_export*> pki_export::elements {
new pki_export( 1, x509, "crt", "PEM", F_PEM | F_USUAL | F_SINGLE, tr("PEM Text format with headers")), new pki_export( 1, x509, "crt", "PEM", F_PEM | F_USUAL | F_SINGLE, tr("PEM Text format with headers")),
new pki_export( 3, x509, "pem", "PEM", F_PEM | F_MULTI, tr("Concatenated list of all selected items in one PEM text file")), new pki_export( 3, x509, "pem", "PEM", F_PEM | F_MULTI, tr("Concatenated list of all selected items in one PEM text file")),
new pki_export( 2, x509, "pem", tr("PEM chain"), F_PEM | F_USUAL | F_CHAIN | F_SINGLE, tr("Concatenated text format of the complete certificate chain in one PEM file")), new pki_export( 2, x509, "pem", tr("PEM chain"), F_PEM | F_USUAL | F_CHAIN | F_SINGLE, tr("Concatenated text format of the complete certificate chain in one PEM file")),
new pki_export( 4, x509, "ovpn","OpenVPN", F_PEM | F_OVPN | F_CHAIN | F_PRIVATE | F_SINGLE, tr("The complete certificate chain and the private key of the selected certificate with tags usable in OpenVPN configuration files")),
new pki_export( 6, x509, "pem", tr("PEM + key"), F_PEM | F_PRIVATE| F_SINGLE, tr("Concatenation of the certificate and the unencrypted private key in one PEM file")), new pki_export( 6, x509, "pem", tr("PEM + key"), F_PEM | F_PRIVATE| F_SINGLE, tr("Concatenation of the certificate and the unencrypted private key in one PEM file")),
new pki_export( 7, x509, "pem", "PEM + PKCS#8", F_PEM | F_PKCS8 | F_PRIVATE | F_CRYPT, tr("Concatenation of the certificate and the encrypted private key in PKCS#8 format in one file")), new pki_export( 7, x509, "pem", "PEM + PKCS#8", F_PEM | F_PKCS8 | F_PRIVATE | F_CRYPT, tr("Concatenation of the certificate and the encrypted private key in PKCS#8 format in one file")),
new pki_export( 8, x509, "p7b", "PKCS #7", F_PKCS7 | F_USUAL | F_SINGLE, tr("PKCS#7 encoded single certificate")), new pki_export( 8, x509, "p7b", "PKCS #7", F_PKCS7 | F_USUAL | F_SINGLE, tr("PKCS#7 encoded single certificate")),

View File

@ -47,6 +47,7 @@ enum {
F_INDEX = BIT(17), F_INDEX = BIT(17),
F_CAL = BIT(18),/* Calendar entry */ F_CAL = BIT(18),/* Calendar entry */
F_DER = BIT(19),/* DER format */ F_DER = BIT(19),/* DER format */
F_OVPN = BIT(20),/* OpenVPN format */
}; };
class pki_export : public QObject { class pki_export : public QObject {

View File

@ -125,8 +125,11 @@ void pki_key::write_SSH2_ed25519_private(BIO *b,
bool pki_key::pem(BioByteArray &b) bool pki_key::pem(BioByteArray &b)
{ {
const pki_export *xport = pki_export::by_id(Settings["KeyFormat"]); return pem(b, pki_export::by_id(Settings["KeyFormat"]));
}
bool pki_key::pem(BioByteArray &b, const pki_export *xport)
{
if (xport->match_all(F_PRIVATE)) if (xport->match_all(F_PRIVATE))
return false; return false;
if (xport->match_all(F_SSH2)) if (xport->match_all(F_SSH2))

View File

@ -234,6 +234,7 @@ class pki_key: public pki_base
qFatal("generate in pki_key"); qFatal("generate in pki_key");
} }
bool pem(BioByteArray &); bool pem(BioByteArray &);
virtual bool pem(BioByteArray &b, const pki_export *xport);
QVariant column_data(const dbheader *hd) const; QVariant column_data(const dbheader *hd) const;
QString modulus() const; QString modulus() const;
QString pubEx() const; QString pubEx() const;

View File

@ -215,6 +215,15 @@ void test_main::exportFormat()
export_by_id(7, file, list, certs); export_by_id(7, file, list, certs);
verify_key(file, QList<unsigned> { END_HASH, ENDKEY_HASH }, true); verify_key(file, QList<unsigned> { END_HASH, ENDKEY_HASH }, true);
check_pems(file, 2, QStringList { " ENCRYPTED PRIVATE KEY-", " CERTIFICATE-" }); check_pems(file, 2, QStringList { " ENCRYPTED PRIVATE KEY-", " CERTIFICATE-" });
// Export OpenVPN format
file = AUTOFILE(OPENVPN)
export_by_id(4, file, list, certs);
verify_key(file, QList<unsigned> {
ROOT_HASH, INTER_HASH, END_HASH, ENDKEY_HASH }, true);
check_pems(file, 4, QStringList { " RSA PRIVATE KEY-",
" CERTIFICATE-", " CERTIFICATE-"," CERTIFICATE-",
"<ca>", "</ca>", "<extra-certs>", "</extra-certs>",
"<cert>", "</cert>", "<key>", "</key>" });
// Export Endentity as PKCS#7 // Export Endentity as PKCS#7
file = AUTOFILE(CERTP7) file = AUTOFILE(CERTP7)
export_by_id(8, file, list, certs); export_by_id(8, file, list, certs);