/* 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 #include #include #include 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); 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 &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 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; }