mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
Some checks failed
CMake / build (build/xca-*-Linux.tar.gz, Unix Makefiles, linux, /usr, ubuntu, 5.15.2, ubuntu-latest) (push) Has been cancelled
CMake / build (build/xca-*-Linux.tar.gz, Unix Makefiles, linux, /usr, ubuntu, 6.6.2, ubuntu-latest) (push) Has been cancelled
CMake / build (build/xca-*.dmg, Unix Makefiles, mac, /opt/homebrew/opt/openssl, macos, 6.6.2, macos-latest) (push) Has been cancelled
CMake / build (build/xca-*.msi
build/xca-portable-*.zip
, MinGW Makefiles, windows, D:\msys2\msys64\mingw64, windows, 5.15.2, windows-2019, win64_mingw81) (push) Has been cancelled
A comment line of the format "CSP: <Provider Name>" in the private key will be used as CSP during PKCS#12 / PFX export.
246 lines
6.1 KiB
C++
246 lines
6.1 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2020 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
|
|
#include "pki_pkcs12.h"
|
|
#include "pki_evp.h"
|
|
#include "pki_x509.h"
|
|
|
|
#include "pass_info.h"
|
|
#include "exception.h"
|
|
#include "func.h"
|
|
#include "PwDialogCore.h"
|
|
#include "XcaWarningCore.h"
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/pkcs12.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/stack.h>
|
|
|
|
pki_pkcs12::pki_pkcs12(const QString &d, pki_x509 *acert, pki_key *akey)
|
|
:pki_multi(d), cert(acert), key(akey)
|
|
{
|
|
append_item(key);
|
|
append_item(cert);
|
|
}
|
|
|
|
pki_pkcs12::pki_pkcs12(const QString &fname)
|
|
:pki_multi(fname)
|
|
{
|
|
Passwd pass;
|
|
EVP_PKEY *mykey = NULL;
|
|
X509 *mycert = NULL;
|
|
pass_info p(XCA_TITLE, tr("Please enter the password to decrypt the PKCS#12 file:\n%1").arg(compressFilename(fname)));
|
|
const X509_ALGOR *macalgid = NULL;
|
|
const ASN1_INTEGER *maciter = NULL;
|
|
const ASN1_OBJECT *macobj = NULL;
|
|
|
|
setFilename(fname);
|
|
XFile file(fname);
|
|
file.open_read();
|
|
BioByteArray b(file.readAll());
|
|
|
|
PKCS12 *pkcs12 = d2i_PKCS12_bio(b.ro(), NULL);
|
|
if (pki_ign_openssl_error()) {
|
|
if (pkcs12)
|
|
PKCS12_free(pkcs12);
|
|
throw errorEx(tr("Unable to load the PKCS#12 (pfx) file %1.")
|
|
.arg(fname));
|
|
}
|
|
PKCS12_get0_mac(NULL, &macalgid, NULL, &maciter, pkcs12);
|
|
if (macalgid)
|
|
X509_ALGOR_get0(&macobj, NULL, NULL, macalgid);
|
|
if (macobj) {
|
|
algorithm = OBJ_obj2QString(macobj);
|
|
if (maciter)
|
|
algorithm += QString(", iteration %1").arg(a1int(maciter).toDec());
|
|
}
|
|
while (!PKCS12_verify_mac(pkcs12, pass.constData(), pass.size())) {
|
|
if (pass.size() > 0)
|
|
XCA_PASSWD_ERROR();
|
|
enum open_result result = PwDialogCore::execute(&p, &pass);
|
|
if (result != pw_ok) {
|
|
/* cancel pressed */
|
|
PKCS12_free(pkcs12);
|
|
failed_files << fname;
|
|
throw result;
|
|
}
|
|
}
|
|
STACK_OF(X509) *certstack = sk_X509_new_null();
|
|
PKCS12_parse(pkcs12, pass.constData(), &mykey, &mycert, &certstack);
|
|
int error = ERR_peek_error();
|
|
if (ERR_GET_REASON(error) == PKCS12_R_MAC_VERIFY_FAILURE) {
|
|
pki_ign_openssl_error();
|
|
PKCS12_free(pkcs12);
|
|
sk_X509_free(certstack);
|
|
failed_files << fname;
|
|
|
|
throw errorEx(getClassName(),
|
|
tr("The supplied password was wrong (%1)")
|
|
.arg(ERR_reason_error_string(error)));
|
|
}
|
|
pki_ign_openssl_error();
|
|
if (mycert) {
|
|
unsigned char *str = X509_alias_get0(mycert, NULL);
|
|
if (str)
|
|
alias = QString::fromUtf8((const char *)str);
|
|
alias = QString::fromUtf8(alias.toLatin1());
|
|
cert = new pki_x509(mycert);
|
|
Q_CHECK_PTR(cert);
|
|
if (alias.isEmpty()) {
|
|
cert->autoIntName(fname);
|
|
alias = cert->getIntName();
|
|
} else {
|
|
cert->setIntName(alias);
|
|
}
|
|
cert->pkiSource = imported;
|
|
inheritFilename(cert);
|
|
append_item(cert);
|
|
}
|
|
if (mykey) {
|
|
key = new pki_evp(mykey);
|
|
key->setIntName(alias + "_key");
|
|
key->pkiSource = imported;
|
|
inheritFilename(key);
|
|
append_item(key);
|
|
}
|
|
for (int i = 0; i < sk_X509_num(certstack); ++i) {
|
|
X509 *crt = sk_X509_value(certstack, i);
|
|
if (!crt)
|
|
continue;
|
|
pki_x509 *cacert = new pki_x509(crt);
|
|
Q_CHECK_PTR(cacert);
|
|
if (alias.isEmpty()) {
|
|
cacert->autoIntName(QString());
|
|
} else {
|
|
cacert->setIntName(QString(alias + "_ca_%1").arg(i));
|
|
}
|
|
cacert->pkiSource = imported;
|
|
inheritFilename(cacert);
|
|
append_item(cacert);
|
|
}
|
|
sk_X509_free(certstack);
|
|
PKCS12_free(pkcs12);
|
|
pki_openssl_error();
|
|
}
|
|
|
|
void pki_pkcs12::writePKCS12(XFile &file, encAlgo &encAlgo) const
|
|
{
|
|
Passwd pass;
|
|
PKCS12 *pkcs12;
|
|
|
|
if (cert == NULL || key == NULL)
|
|
my_error(tr("No key or no Cert and no pkcs12"));
|
|
|
|
pass_info p(XCA_TITLE,
|
|
tr("Please enter the password to encrypt the key of certificate '%1' "
|
|
"in the PKCS#12 file:\n%2")
|
|
.arg(cert->getIntName()).arg(nativeSeparator(file.fileName())));
|
|
|
|
if (PwDialogCore::execute(&p, &pass, true) != 1)
|
|
return;
|
|
|
|
STACK_OF(X509) *certstack = sk_X509_new_null();
|
|
foreach(pki_base *pki, multi) {
|
|
pki_x509 *x = dynamic_cast<pki_x509*>(pki);
|
|
if (x && x != cert)
|
|
sk_X509_push(certstack, x->getCert());
|
|
}
|
|
int certAlgoNid, keyAlgoNid;
|
|
certAlgoNid = keyAlgoNid = encAlgo.getEncAlgoNid();
|
|
|
|
// The very ancient 40BitRC2_CBC algorithm at least can
|
|
// be combined with TripleDES_CBC for the keys.
|
|
if (keyAlgoNid == NID_pbe_WithSHA1And40BitRC2_CBC)
|
|
keyAlgoNid = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
|
|
|
|
EVP_PKEY *pkey = key->decryptKey();
|
|
for (const QString &line : key->getComment().split('\n')) {
|
|
QStringList kv = line.split(":");
|
|
qDebug() << line << kv;
|
|
if (kv.size() != 2 || kv[0] != "CSP")
|
|
continue;
|
|
QByteArray csp = kv[1].trimmed().toLatin1();
|
|
EVP_PKEY_add1_attr_by_NID(pkey, NID_ms_csp_name,
|
|
MBSTRING_ASC, (const unsigned char*)csp.constData(), csp.size());
|
|
}
|
|
|
|
pkcs12 = PKCS12_create(pass.data(), getIntName().toUtf8().data(),
|
|
pkey, cert->getCert(), certstack,
|
|
keyAlgoNid, certAlgoNid, 0, 0, 0);
|
|
pki_openssl_error();
|
|
EVP_PKEY_free(pkey);
|
|
Q_CHECK_PTR(pkcs12);
|
|
|
|
if (encAlgo.legacy())
|
|
PKCS12_set_mac(pkcs12, pass.data(), -1, NULL, 0, 1, EVP_sha1());
|
|
|
|
BioByteArray b;
|
|
i2d_PKCS12_bio(b, pkcs12);
|
|
sk_X509_free(certstack);
|
|
pki_openssl_error();
|
|
PKCS12_free(pkcs12);
|
|
file.write(b);
|
|
}
|
|
|
|
void pki_pkcs12::collect_properties(QMap<QString, QString> &prp) const
|
|
{
|
|
if (!algorithm.isEmpty())
|
|
prp["Algorithm"] = algorithm;
|
|
if (!alias.isEmpty())
|
|
prp["Friendly Name"] = alias;
|
|
}
|
|
|
|
// see https://www.rfc-editor.org/rfc/rfc8018 Appendix B.2 for possible encryption schemes
|
|
const QList<int> encAlgo::all_encAlgos(
|
|
{
|
|
NID_pbe_WithSHA1And40BitRC2_CBC,
|
|
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
|
|
NID_aes_256_cbc
|
|
});
|
|
|
|
int encAlgo::default_encAlgo(NID_pbe_WithSHA1And3_Key_TripleDES_CBC);
|
|
|
|
encAlgo::encAlgo(int nid) : encAlgo_nid(nid)
|
|
{
|
|
}
|
|
|
|
encAlgo::encAlgo(const QString &name) : encAlgo_nid(default_encAlgo)
|
|
{
|
|
QString s(name);
|
|
encAlgo_nid = OBJ_txt2nid(CCHAR(s.remove(QChar(' '))));
|
|
ign_openssl_error();
|
|
}
|
|
|
|
QString encAlgo::name() const
|
|
{
|
|
return QString(encAlgo_nid == NID_undef ? "" : OBJ_nid2sn(encAlgo_nid));
|
|
}
|
|
|
|
QString encAlgo::displayName() const
|
|
{
|
|
QString n = name();
|
|
if (legacy())
|
|
n += QString(" (%1)").arg(QObject::tr("insecure"));
|
|
return n;
|
|
}
|
|
|
|
int encAlgo::getEncAlgoNid() const
|
|
{
|
|
return encAlgo_nid;
|
|
}
|
|
|
|
const encAlgo encAlgo::getDefault()
|
|
{
|
|
return encAlgo(default_encAlgo);
|
|
}
|
|
|
|
void encAlgo::setDefault(const QString &def)
|
|
{
|
|
default_encAlgo = encAlgo(def).encAlgo_nid;
|
|
}
|