diff --git a/lib/db_x509.cpp b/lib/db_x509.cpp index fa550b59..25571dd5 100644 --- a/lib/db_x509.cpp +++ b/lib/db_x509.cpp @@ -566,6 +566,15 @@ int db_x509::exportFlags(const QModelIndex &idx) const 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("\n").arg(tag).toLatin1()); + } +} + void db_x509::exportItems(const QModelIndexList &list, 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_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()) crt->writeCert(file, true); } else if (xport->match_all(F_UNREVOKED)) { diff --git a/lib/db_x509.h b/lib/db_x509.h index 2395822d..eaa61172 100644 --- a/lib/db_x509.h +++ b/lib/db_x509.h @@ -23,6 +23,8 @@ class db_x509: public db_x509super { Q_OBJECT + static void writeTaggedPEM(BioByteArray &b, + const QString &tag, XFile &file); protected: dbheaderList getHeaders(); void dereferenceIssuer(); diff --git a/lib/pki_evp.cpp b/lib/pki_evp.cpp index 97f41fd2..7b0eb0e8 100644 --- a/lib/pki_evp.cpp +++ b/lib/pki_evp.cpp @@ -768,11 +768,10 @@ QSqlError pki_evp::deleteSqlData() return q.lastError(); } -bool pki_evp::pem(BioByteArray &b) +bool pki_evp::pem(BioByteArray &b, const pki_export *xport) { EVP_PKEY *pkey; int keytype; - const pki_export *xport = pki_export::by_id(Settings["KeyFormat"]); if (xport->match_all(F_PEM | F_PRIVATE)) { pkey = decryptKey(); diff --git a/lib/pki_evp.h b/lib/pki_evp.h index 6166aaf9..bcf4bf98 100644 --- a/lib/pki_evp.h +++ b/lib/pki_evp.h @@ -62,7 +62,7 @@ class pki_evp: public pki_key static QString removeTypeFromIntName(QString n); void fromPEMbyteArray(const QByteArray &ba, const QString &name); 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, const pass_info &p); void writeDefault(const QString &dirname) const; diff --git a/lib/pki_export.cpp b/lib/pki_export.cpp index 5dc7c6d8..f99b71d1 100644 --- a/lib/pki_export.cpp +++ b/lib/pki_export.cpp @@ -58,6 +58,7 @@ QList 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( 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( 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( 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")), diff --git a/lib/pki_export.h b/lib/pki_export.h index d80171b7..deaa72e2 100644 --- a/lib/pki_export.h +++ b/lib/pki_export.h @@ -47,6 +47,7 @@ enum { F_INDEX = BIT(17), F_CAL = BIT(18),/* Calendar entry */ F_DER = BIT(19),/* DER format */ + F_OVPN = BIT(20),/* OpenVPN format */ }; class pki_export : public QObject { diff --git a/lib/pki_key.cpp b/lib/pki_key.cpp index ec22c0a8..b7a575e8 100644 --- a/lib/pki_key.cpp +++ b/lib/pki_key.cpp @@ -125,8 +125,11 @@ void pki_key::write_SSH2_ed25519_private(BIO *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)) return false; if (xport->match_all(F_SSH2)) diff --git a/lib/pki_key.h b/lib/pki_key.h index 7ddc0192..de35ff11 100644 --- a/lib/pki_key.h +++ b/lib/pki_key.h @@ -234,6 +234,7 @@ class pki_key: public pki_base qFatal("generate in pki_key"); } bool pem(BioByteArray &); + virtual bool pem(BioByteArray &b, const pki_export *xport); QVariant column_data(const dbheader *hd) const; QString modulus() const; QString pubEx() const; diff --git a/test/export.cpp b/test/export.cpp index 67df1112..aab40470 100644 --- a/test/export.cpp +++ b/test/export.cpp @@ -215,6 +215,15 @@ void test_main::exportFormat() export_by_id(7, file, list, certs); verify_key(file, QList { END_HASH, ENDKEY_HASH }, true); 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 { + ROOT_HASH, INTER_HASH, END_HASH, ENDKEY_HASH }, true); + check_pems(file, 4, QStringList { " RSA PRIVATE KEY-", + " CERTIFICATE-", " CERTIFICATE-"," CERTIFICATE-", + "", "", "", "", + "", "", "", "" }); // Export Endentity as PKCS#7 file = AUTOFILE(CERTP7) export_by_id(8, file, list, certs);