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;
}
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,
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)) {

View File

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

View File

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

View File

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

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( 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")),

View File

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

View File

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

View File

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

View File

@ -215,6 +215,15 @@ void test_main::exportFormat()
export_by_id(7, file, list, certs);
verify_key(file, QList<unsigned> { 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<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
file = AUTOFILE(CERTP7)
export_by_id(8, file, list, certs);