mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
The API changed heavily. New functions arrived, old functions disappeared and many structures became opaque. This version of the patch implements pkcs11 signing as follows: - openssl < 1.0.0: rsa & dsa without engine - openssl 1.0.x: rsa, dsa & ec with engine - openssl >= 1.1.0: rsa, dsa & ec without engine In the operation, we therefore also gain implementation of dsa signing for openssl < 1.0.0 (ec disabled because EC_KEY_METHOD was not yet invented!). I've given up trying to use a PKEY_ENGINE with openssl 1.1: seems not possible anymore. I've succeeded compiling the patched xca with openssl 0.9.8n, 1.0.2j and 1.1.0e. I've successfully tested pkcs11 signing using softhsm with openssl 1.0.2j and 1.1.0e. The patch also removes gcc7 new warnings.
553 lines
14 KiB
C++
553 lines
14 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2014 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
|
|
#include "func.h"
|
|
#include "exception.h"
|
|
#include "lib/asn1time.h"
|
|
#include "widgets/validity.h"
|
|
#include <openssl/objects.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/buffer.h>
|
|
|
|
#if defined(Q_WS_MAC)
|
|
#include <QDesktopServices>
|
|
#endif
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QStringList>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QComboBox>
|
|
#include <QMessageBox>
|
|
#include <QApplication>
|
|
#include <QPushButton>
|
|
#include <QProgressBar>
|
|
#include <QTextEdit>
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
#else
|
|
/* for htons() */
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
QPixmap *loadImg(const char *name )
|
|
{
|
|
return new QPixmap(QString(":") + name);
|
|
}
|
|
|
|
QStringList getLibExtensions()
|
|
{
|
|
QStringList l;
|
|
#if defined(_WIN32) || defined(USE_CYGWIN)
|
|
l << QString("*.dll") << QString("*.DLL");
|
|
#elif defined(Q_WS_MAC)
|
|
l << QString("*.dylib") << QString("*.so");
|
|
#else
|
|
l << QString("*.so");
|
|
#endif
|
|
return l;
|
|
}
|
|
|
|
/* returns e.g. /usr/local/share/xca for unix systems
|
|
* or HKEY_LOCAL_MACHINE->Software->xca for WIN32
|
|
* (e.g. c:\Program Files\xca )
|
|
*/
|
|
|
|
QString getPrefix()
|
|
{
|
|
#ifdef WIN32
|
|
static char inst_dir[100] = "";
|
|
char *p;
|
|
ULONG dwLength = 100;
|
|
LONG lRc;
|
|
HKEY hKey;
|
|
|
|
if (inst_dir[0] != '\0') {
|
|
/* if we already once discovered the directory just return it */
|
|
return QString(inst_dir);
|
|
}
|
|
// fallback: directory of xca.exe
|
|
GetModuleFileName(0, inst_dir, dwLength - 1);
|
|
p = strrchr(inst_dir, '\\');
|
|
if (p) {
|
|
*p = '\0';
|
|
return QString(inst_dir);
|
|
}
|
|
p = inst_dir;
|
|
*p = '\0';
|
|
lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\xca", 0, KEY_READ, &hKey);
|
|
if (lRc != ERROR_SUCCESS) {
|
|
XCA_WARN("Registry Key: 'HKEY_LOCAL_MACHINE->Software->xca' not found");
|
|
return QString(inst_dir);
|
|
}
|
|
lRc = RegQueryValueEx(hKey, "Install_Dir", NULL, NULL,
|
|
(unsigned char *)inst_dir, &dwLength);
|
|
if (lRc != ERROR_SUCCESS){
|
|
XCA_WARN("Registry Key: 'HKEY_LOCAL_MACHINE->Software->xca->Install_Dir' not found");
|
|
}
|
|
lRc = RegCloseKey(hKey);
|
|
return QString(inst_dir);
|
|
|
|
#elif defined(Q_WS_MAC)
|
|
// since this is platform-specific anyway,
|
|
// this is a more robust way to get the bundle directory
|
|
QDir bundleDir(qApp->applicationDirPath());
|
|
bundleDir.cdUp();
|
|
return bundleDir.canonicalPath() + "/Resources";
|
|
#else
|
|
#ifndef XCA_PREFIX
|
|
#define XCA_PREFIX PREFIX "/share/xca"
|
|
#endif
|
|
return QString(XCA_PREFIX);
|
|
#endif
|
|
|
|
}
|
|
|
|
QString getHomeDir()
|
|
{
|
|
QString hd;
|
|
#ifdef WIN32
|
|
LPITEMIDLIST pidl = NULL;
|
|
TCHAR buf[255] = "";
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl))) {
|
|
SHGetPathFromIDList(pidl, buf);
|
|
}
|
|
hd = buf;
|
|
#else
|
|
hd = QDir::homePath();
|
|
#endif
|
|
return hd;
|
|
}
|
|
|
|
QString getLibDir()
|
|
{
|
|
QString hd;
|
|
#ifdef WIN32
|
|
LPITEMIDLIST pidl = NULL;
|
|
TCHAR buf[255] = "";
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_SYSTEM, &pidl))) {
|
|
SHGetPathFromIDList(pidl, buf);
|
|
}
|
|
hd = buf;
|
|
#else
|
|
hd = QString("/usr/lib");
|
|
#endif
|
|
return hd;
|
|
}
|
|
|
|
QString getDocDir()
|
|
{
|
|
#if defined(WIN32) || defined (Q_WS_MAC)
|
|
return getPrefix();
|
|
#else
|
|
return QString(DOCDIR);
|
|
#endif
|
|
}
|
|
|
|
// The intent of this function is to return the proper location for
|
|
// user-controlled settings on the current platform
|
|
// i.e. PROFILE\Application Data\xca on windows, HOME/.xca on UNIX,
|
|
// ~/Library/Preferences/xca on Mac OS X
|
|
QString getUserSettingsDir()
|
|
{
|
|
QString rv;
|
|
#ifdef WIN32
|
|
LPITEMIDLIST pidl = NULL;
|
|
TCHAR buf[255] = "";
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl))) {
|
|
SHGetPathFromIDList(pidl, buf);
|
|
}
|
|
rv = buf;
|
|
rv += QDir::separator();
|
|
rv += "xca";
|
|
#elif defined(Q_WS_MAC)
|
|
rv = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
|
rv.insert(rv.count() - QCoreApplication::applicationName().count(),
|
|
QCoreApplication::organizationName());
|
|
#else
|
|
rv = QDir::homePath();
|
|
rv += QDir::separator();
|
|
rv += ".xca";
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
// Qt's open and save dialogs result in some undesirable quirks.
|
|
// This function makes sure that a filename has the user-selected extension.
|
|
QString getFullFilename(const QString & filename, const QString & selectedFilter)
|
|
{
|
|
QString rv = filename.trimmed(), ext;
|
|
QRegExp rx(".* \\( ?\\*(.[a-z]{1,3}) ?\\)");
|
|
rx.indexIn(selectedFilter);
|
|
ext = rx.cap(1);
|
|
if (!ext.isEmpty() && !rv.endsWith(ext)) {
|
|
rv += ext;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
QByteArray filename2bytearray(const QString &fname)
|
|
{
|
|
#ifdef WIN32
|
|
return fname.toLocal8Bit();
|
|
#else
|
|
return fname.toUtf8();
|
|
#endif
|
|
}
|
|
|
|
QString filename2QString(const char *fname)
|
|
{
|
|
#ifdef WIN32
|
|
return QString::fromLocal8Bit(fname);
|
|
#else
|
|
return QString::fromUtf8(fname);
|
|
#endif
|
|
}
|
|
|
|
QString compressFilename(QString filename, int maxlen)
|
|
{
|
|
if (filename.length() < maxlen)
|
|
return filename;
|
|
|
|
QString fn = filename.replace("\\", "/");
|
|
int len, lastslash = fn.lastIndexOf('/');
|
|
QString base = filename.mid(lastslash);
|
|
len = base.length();
|
|
len = maxlen - len -3;
|
|
if (len < 0)
|
|
return QString("...") + base.right(maxlen -3);
|
|
fn = fn.left(len);
|
|
lastslash = fn.lastIndexOf('/');
|
|
|
|
return filename.left(lastslash+1) + "..." + base;
|
|
}
|
|
|
|
QString asn1ToQString(const ASN1_STRING *str, bool quote)
|
|
{
|
|
QString qs;
|
|
unsigned short *bmp;
|
|
int i;
|
|
|
|
if (!str)
|
|
return qs;
|
|
|
|
switch (str->type) {
|
|
case V_ASN1_BMPSTRING:
|
|
bmp = (unsigned short*)str->data;
|
|
for (i = 0; i < str->length/2; i++) {
|
|
unsigned short s = ntohs(bmp[i]);
|
|
qs += QString::fromUtf16(&s, 1);
|
|
}
|
|
break;
|
|
case V_ASN1_UTF8STRING:
|
|
qs = QString::fromUtf8((const char*)str->data, str->length);
|
|
break;
|
|
case V_ASN1_T61STRING:
|
|
qs = QString::fromLocal8Bit((const char*)str->data, str->length);
|
|
break;
|
|
default:
|
|
qs = QString::fromLatin1((const char*)str->data, str->length);
|
|
}
|
|
#if 0
|
|
printf("Convert %s (%d %d) string to '%s' len %d:", ASN1_tag2str(str->type), str->type, V_ASN1_UTF8STRING, CCHAR(qs), str->length);
|
|
for (int i=0; i< str->length; i++)
|
|
printf(" %02x", str->data[i]);
|
|
printf("\n");
|
|
#endif
|
|
if (quote)
|
|
qs.replace('\n', "\\n\\");
|
|
return qs;
|
|
}
|
|
|
|
/* returns an encoded ASN1 string from QString for a special nid*/
|
|
ASN1_STRING *QStringToAsn1(const QString s, int nid)
|
|
{
|
|
QByteArray ba = s.toUtf8();
|
|
const unsigned char *utf8 = (const unsigned char *)ba.constData();
|
|
unsigned long global_mask = ASN1_STRING_get_default_mask();
|
|
unsigned long mask = DIRSTRING_TYPE & global_mask;
|
|
ASN1_STRING *out = NULL;
|
|
ASN1_STRING_TABLE *tbl;
|
|
|
|
tbl = ASN1_STRING_TABLE_get(nid);
|
|
if (tbl) {
|
|
mask = tbl->mask;
|
|
if (!(tbl->flags & STABLE_NO_MASK))
|
|
mask &= global_mask;
|
|
}
|
|
ASN1_mbstring_copy(&out, utf8, -1, MBSTRING_UTF8, mask);
|
|
openssl_error(QString("'%1' (%2)").arg(s).arg(OBJ_nid2ln(nid)));
|
|
return out;
|
|
}
|
|
|
|
const char *OBJ_ln2sn(const char *ln)
|
|
{
|
|
return OBJ_nid2sn(OBJ_ln2nid(ln));
|
|
}
|
|
|
|
const char *OBJ_sn2ln(const char *sn)
|
|
{
|
|
return OBJ_nid2ln(OBJ_sn2nid(sn));
|
|
}
|
|
|
|
const char *OBJ_obj2sn(ASN1_OBJECT *a)
|
|
{
|
|
OBJ_obj2nid(a);
|
|
openssl_error();
|
|
return OBJ_nid2sn(OBJ_obj2nid(a));
|
|
}
|
|
|
|
QString OBJ_obj2QString(const ASN1_OBJECT *a, int no_name)
|
|
{
|
|
char buf[512];
|
|
int len;
|
|
|
|
len = OBJ_obj2txt(buf, 256, a, no_name);
|
|
openssl_error();
|
|
return QString::fromLatin1(buf, len);
|
|
}
|
|
|
|
QByteArray i2d_bytearray(int(*i2d)(const void*, unsigned char **),
|
|
const void *data)
|
|
{
|
|
QByteArray ba;
|
|
|
|
ba.resize(i2d(data, NULL));
|
|
unsigned char *p = (unsigned char*)ba.data();
|
|
i2d(data, &p);
|
|
openssl_error();
|
|
return ba;
|
|
}
|
|
|
|
void *d2i_bytearray(void *(*d2i)(void *, unsigned char **, long),
|
|
QByteArray &ba)
|
|
{
|
|
unsigned char *p, *p1;
|
|
void *ret;
|
|
p = p1 = (unsigned char *)ba.constData();
|
|
ret = d2i(NULL, &p1, ba.count());
|
|
ba = ba.mid(p1-p);
|
|
openssl_error();
|
|
return ret;
|
|
}
|
|
|
|
void _openssl_error(const QString txt, const char *file, int line)
|
|
{
|
|
QString error;
|
|
|
|
while (int i = ERR_get_error() ) {
|
|
error += QString(ERR_error_string(i, NULL)) + "\n";
|
|
fputs(CCHAR(QString("OpenSSL error (%1:%2) : %3\n").
|
|
arg(file).arg(line).arg(ERR_error_string(i, NULL))),
|
|
stderr);
|
|
}
|
|
if (!error.isEmpty()) {
|
|
if (!txt.isEmpty())
|
|
error = txt + "\n" + error + "\n" +
|
|
QString("(%1:%2)").arg(file).arg(line);
|
|
throw errorEx(error);
|
|
}
|
|
}
|
|
|
|
#undef PRINT_IGNORED_ANYWAY
|
|
bool _ign_openssl_error(const QString txt, const char *file, int line)
|
|
{
|
|
// ignore openssl errors
|
|
QString errtxt;
|
|
#if PRINT_IGNORED_ANYWAY
|
|
if (!txt.isEmpty() && ERR_peek_error())
|
|
fprintf(stderr, "%s\n", CCHAR(txt));
|
|
#else
|
|
(void)txt;
|
|
(void)file;
|
|
(void)line;
|
|
#endif
|
|
while (int i = ERR_get_error() ) {
|
|
errtxt = ERR_error_string(i, NULL);
|
|
#if PRINT_IGNORED_ANYWAY
|
|
fprintf(stderr, CCHAR(QString("IGNORED (%1:%2) : %3\n").
|
|
arg(file).arg(line).arg(errtxt)));
|
|
#endif
|
|
}
|
|
return !errtxt.isEmpty();
|
|
}
|
|
|
|
void inc_progress_bar(int, int, void *p)
|
|
{
|
|
QProgressBar *bar = (QProgressBar *)p;
|
|
int value = bar->value();
|
|
|
|
if (value == bar->maximum()) {
|
|
bar->reset();
|
|
} else {
|
|
bar->setValue(value +1);
|
|
}
|
|
}
|
|
|
|
static long mem_ctrl(BIO *b, int cmd, long num, void *ptr)
|
|
{
|
|
BUF_MEM *bm;
|
|
int flags;
|
|
long (*ctrl)(BIO *, int, long, void *);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIO_get_mem_ptr(b, &bm);
|
|
flags = BIO_get_flags(b);
|
|
ctrl = BIO_meth_get_ctrl((BIO_METHOD *) BIO_s_mem());
|
|
#else
|
|
bm = (BUF_MEM *)b->ptr;
|
|
flags = b->flags;
|
|
ctrl = BIO_s_mem()->ctrl;
|
|
#endif
|
|
if (!bm->data || !(flags & BIO_FLAGS_MEM_RDONLY))
|
|
return ctrl(b, cmd, num, ptr);
|
|
|
|
switch (cmd) {
|
|
case BIO_C_FILE_SEEK:
|
|
if (num > (long)bm->max)
|
|
num = bm->max;
|
|
bm->data -= (bm->max - bm->length) - num;
|
|
bm->length = bm->max - num;
|
|
/* fallthrough */
|
|
case BIO_C_FILE_TELL:
|
|
return bm->max - bm->length;
|
|
}
|
|
return ctrl(b, cmd, num, ptr);
|
|
}
|
|
|
|
BIO_METHOD *BIO_METHOD_copy(const BIO_METHOD *src, BIO_METHOD *dst = NULL)
|
|
{
|
|
if (src)
|
|
return NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
if (!dst) {
|
|
// The only way to get the type and name of the source method is
|
|
// to bind it to a BIO.
|
|
BIO *bio = BIO_new(src);
|
|
if (!bio)
|
|
return NULL;
|
|
if (!(dst = BIO_meth_new(BIO_method_type(bio), BIO_method_name(bio)))) {
|
|
BIO_free(bio);
|
|
return NULL;
|
|
}
|
|
BIO_free(bio);
|
|
}
|
|
|
|
BIO_meth_set_write(dst, BIO_meth_get_write((BIO_METHOD *) src));
|
|
BIO_meth_set_read(dst, BIO_meth_get_read((BIO_METHOD *) src));
|
|
BIO_meth_set_puts(dst, BIO_meth_get_puts((BIO_METHOD *) src));
|
|
BIO_meth_set_gets(dst, BIO_meth_get_gets((BIO_METHOD *) src));
|
|
BIO_meth_set_ctrl(dst, BIO_meth_get_ctrl((BIO_METHOD *) src));
|
|
BIO_meth_set_create(dst, BIO_meth_get_create((BIO_METHOD *) src));
|
|
BIO_meth_set_destroy(dst, BIO_meth_get_destroy((BIO_METHOD *) src));
|
|
BIO_meth_set_callback_ctrl(dst,
|
|
BIO_meth_get_callback_ctrl((BIO_METHOD *) src));
|
|
#else
|
|
if (!dst)
|
|
if (!(dst = (BIO_METHOD *) OPENSSL_malloc(sizeof *dst)))
|
|
return NULL;
|
|
*dst = *src;
|
|
#endif
|
|
|
|
return dst;
|
|
}
|
|
|
|
BIO_METHOD *BIO_METHOD_seekable_romem()
|
|
{
|
|
static BIO_METHOD *mymeth = NULL;
|
|
|
|
if (!mymeth) {
|
|
mymeth = BIO_METHOD_copy(BIO_s_mem());
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIO_meth_set_ctrl(mymeth, mem_ctrl);
|
|
#else
|
|
mymeth->ctrl = mem_ctrl;
|
|
#endif
|
|
}
|
|
|
|
return mymeth;
|
|
}
|
|
|
|
BIO *BIO_QBA_mem_buf(QByteArray &a)
|
|
{
|
|
BIO *bio;
|
|
BIO_METHOD *meth = BIO_METHOD_seekable_romem();
|
|
|
|
if (!meth)
|
|
return NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
bio = BIO_new(meth);
|
|
if (bio) {
|
|
BUF_MEM bm;
|
|
bm.data = a.data();
|
|
bm.length = a.size();
|
|
bm.max = a.size();
|
|
BIO_set_mem_buf(bio, &bm, BIO_CLOSE);
|
|
BIO_set_flags(bio, BIO_get_flags(bio) | BIO_FLAGS_MEM_RDONLY);
|
|
}
|
|
#else
|
|
bio = BIO_new_mem_buf(a.data(), a.size());
|
|
if (bio)
|
|
bio->method = meth;
|
|
#endif
|
|
|
|
return bio;
|
|
}
|
|
|
|
bool translate_dn = false;
|
|
QMap<int, QString> dn_translations;
|
|
|
|
void dn_translations_setup()
|
|
{
|
|
dn_translations[NID_countryName] = QObject::tr("Country code");
|
|
dn_translations[NID_stateOrProvinceName] = QObject::tr("State or Province");
|
|
dn_translations[NID_localityName] = QObject::tr("Locality");
|
|
dn_translations[NID_organizationName] = QObject::tr("Organisation");
|
|
dn_translations[NID_organizationalUnitName] = QObject::tr("Organisational unit");
|
|
dn_translations[NID_commonName] = QObject::tr("Common name");
|
|
dn_translations[NID_pkcs9_emailAddress] = QObject::tr("E-Mail address");
|
|
dn_translations[NID_serialNumber] = QObject::tr("Serial number");
|
|
dn_translations[NID_givenName] = QObject::tr("Given name");
|
|
dn_translations[NID_surname] = QObject::tr("Surname");
|
|
dn_translations[NID_title] = QObject::tr("Title");
|
|
dn_translations[NID_initials] = QObject::tr("Initials");
|
|
dn_translations[NID_description] = QObject::tr("Description");
|
|
dn_translations[NID_role] = QObject::tr("Role");
|
|
dn_translations[NID_pseudonym] = QObject::tr("Pseudonym");
|
|
dn_translations[NID_generationQualifier] = QObject::tr("Generation Qualifier");
|
|
dn_translations[NID_x500UniqueIdentifier] = QObject::tr("x500 Unique Identifier");
|
|
dn_translations[NID_name] = QObject::tr("Name");
|
|
dn_translations[NID_dnQualifier] = QObject::tr("DN Qualifier");
|
|
dn_translations[NID_pkcs9_unstructuredName] = QObject::tr("Unstructured name");
|
|
dn_translations[NID_pkcs9_challengePassword] = QObject::tr("Challenge password");
|
|
|
|
dn_translations[NID_basic_constraints] = QObject::tr("Basic Constraints");
|
|
dn_translations[NID_subject_alt_name] = QObject::tr("Subject alternative name");
|
|
dn_translations[NID_issuer_alt_name] = QObject::tr("issuer alternative name");
|
|
dn_translations[NID_subject_key_identifier] = QObject::tr("Subject key identifier");
|
|
dn_translations[NID_authority_key_identifier] = QObject::tr("Authority key identifier");
|
|
dn_translations[NID_key_usage] = QObject::tr("Key usage");
|
|
dn_translations[NID_ext_key_usage] = QObject::tr("Extended key usage");
|
|
dn_translations[NID_crl_distribution_points] = QObject::tr("CRL distribution points");
|
|
dn_translations[NID_info_access] = QObject::tr("Authority information access");
|
|
dn_translations[NID_netscape_cert_type] = QObject::tr("Certificate type");
|
|
dn_translations[NID_netscape_base_url] = QObject::tr("Base URL");
|
|
dn_translations[NID_netscape_revocation_url] = QObject::tr("Revocation URL");
|
|
dn_translations[NID_netscape_ca_revocation_url] = QObject::tr("CA Revocation URL");
|
|
dn_translations[NID_netscape_renewal_url] = QObject::tr("Certificate renewal URL");
|
|
dn_translations[NID_netscape_ca_policy_url] = QObject::tr("CA policy URL");
|
|
dn_translations[NID_netscape_ssl_server_name] = QObject::tr("SSL server name");
|
|
dn_translations[NID_netscape_comment] = QObject::tr("Comment");
|
|
}
|