mirror of
https://github.com/chris2511/xca.git
synced 2026-09-14 11:06:19 +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.
634 lines
12 KiB
C++
634 lines
12 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2009 - 2015 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
|
|
#include "pki_key.h"
|
|
#include "func.h"
|
|
#include "db.h"
|
|
#include <openssl/rand.h>
|
|
#include <openssl/pem.h>
|
|
#include <QProgressDialog>
|
|
#include <QApplication>
|
|
#include <QDir>
|
|
#include "widgets/PwDialog.h"
|
|
|
|
builtin_curves pki_key::builtinCurves;
|
|
|
|
pki_key::pki_key(const QString name)
|
|
:pki_base(name)
|
|
{
|
|
key = EVP_PKEY_new();
|
|
ucount = 0;
|
|
class_name = "pki_key";
|
|
}
|
|
|
|
pki_key::pki_key(const pki_key *pk)
|
|
:pki_base(pk->desc)
|
|
{
|
|
ucount = pk->ucount;
|
|
key = EVP_PKEY_new();
|
|
if (pk->key) {
|
|
QByteArray ba = i2d_bytearray(I2D_VOID(i2d_PUBKEY), pk->key);
|
|
d2i(ba);
|
|
}
|
|
}
|
|
|
|
pki_key::~pki_key()
|
|
{
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
}
|
|
|
|
void pki_key::d2i(QByteArray &ba)
|
|
{
|
|
EVP_PKEY *k = (EVP_PKEY*)d2i_bytearray(D2I_VOID(d2i_PUBKEY), ba);
|
|
pki_openssl_error();
|
|
if (k) {
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
key = k;
|
|
}
|
|
}
|
|
|
|
void pki_key::d2i_old(QByteArray &ba, int type)
|
|
{
|
|
const unsigned char *p, *p1;
|
|
p = p1 = (const unsigned char *)ba.constData();
|
|
EVP_PKEY *k = d2i_PublicKey(type, NULL, &p1, ba.count());
|
|
|
|
pki_openssl_error();
|
|
|
|
if (k) {
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
key = k;
|
|
}
|
|
ba = ba.mid(p1-p);
|
|
}
|
|
|
|
QByteArray pki_key::i2d()
|
|
{
|
|
return i2d_bytearray(I2D_VOID(i2d_PUBKEY), key);
|
|
}
|
|
|
|
BIO *pki_key::pem(BIO *b, int format)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
QByteArray ba;
|
|
if (!b)
|
|
b = BIO_new(BIO_s_mem());
|
|
|
|
switch (format) {
|
|
case exportType::SSH2_public:
|
|
ba = SSH2publicQByteArray();
|
|
BIO_write(b, ba.data(), ba.size());
|
|
break;
|
|
case exportType::PEM_private:
|
|
pkey = decryptKey();
|
|
PEM_write_bio_PrivateKey(b, pkey, NULL, NULL, 0, NULL, NULL);
|
|
EVP_PKEY_free(pkey);
|
|
break;
|
|
case exportType::PEM_key:
|
|
PEM_write_bio_PUBKEY(b, key);
|
|
break;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
QString pki_key::length()
|
|
{
|
|
bool dsa_unset = false;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
if (EVP_PKEY_id(key) == EVP_PKEY_DSA) {
|
|
const BIGNUM *p = NULL;
|
|
DSA *dsa = EVP_PKEY_get0_DSA(key);
|
|
if (dsa)
|
|
DSA_get0_pqg(dsa, &p, NULL, NULL);
|
|
dsa_unset = p == NULL;
|
|
}
|
|
#else
|
|
dsa_unset = key->type == EVP_PKEY_DSA && key->pkey.dsa->p == NULL;
|
|
#endif
|
|
|
|
if (dsa_unset)
|
|
return QString("???");
|
|
|
|
return QString("%1 bit").arg(EVP_PKEY_bits(key));
|
|
}
|
|
|
|
QString pki_key::getTypeString() const
|
|
{
|
|
QString type;
|
|
|
|
switch (EVP_PKEY_type(getKeyType())) {
|
|
case EVP_PKEY_RSA:
|
|
type = "RSA";
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
type = "DSA";
|
|
break;
|
|
case EVP_PKEY_EC:
|
|
type = "EC";
|
|
break;
|
|
default:
|
|
type = "---";
|
|
}
|
|
return type;
|
|
}
|
|
|
|
QString pki_key::getMsg(msg_type msg)
|
|
{
|
|
/*
|
|
* We do not construct english sentences (just a little bit)
|
|
* from fragments to allow proper translations.
|
|
* The drawback are all the slightly different duplicated messages
|
|
*
|
|
* %1 will be replaced by "RSA", "DSA", "EC"
|
|
* %2 is the internal name of the key
|
|
*/
|
|
QString ktype = getTypeString();
|
|
if (isPubKey()) {
|
|
switch (msg) {
|
|
case msg_import: return tr("Successfully imported the %1 public key '%2'").arg(ktype);
|
|
case msg_delete: return tr("Delete the %1 public key '%2'?").arg(ktype);
|
|
default: break;
|
|
}
|
|
} else {
|
|
switch (msg) {
|
|
case msg_import: return tr("Successfully imported the %1 private key '%2'").arg(ktype);
|
|
case msg_delete: return tr("Delete the %1 private key '%2'?").arg(ktype);
|
|
case msg_create: return tr("Successfully created the %1 private key '%2'").arg(ktype);
|
|
default: break;
|
|
}
|
|
}
|
|
if (msg == msg_delete_multi) {
|
|
/* %1: Number of keys; %2: list of keynames */
|
|
return tr("Delete the %1 keys: %2?");
|
|
}
|
|
return pki_base::getMsg(msg);
|
|
}
|
|
|
|
QString pki_key::getIntNameWithType()
|
|
{
|
|
return QString("%1 (%2:%3%4)").arg(getIntName()).arg(getTypeString()).
|
|
arg(length()).arg(isPubKey() ?
|
|
QString(" ") + tr("Public key") : QString(""));
|
|
}
|
|
|
|
QString pki_key::removeTypeFromIntName(QString n)
|
|
{
|
|
int i;
|
|
if (n.right(1) != ")" )
|
|
return n;
|
|
i = n.lastIndexOf(" (");
|
|
if (i > 0)
|
|
n.truncate(i);
|
|
return n;
|
|
}
|
|
|
|
bool pki_key::isToken()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool pki_key::isPrivKey() const
|
|
{
|
|
return !isPubKey();
|
|
}
|
|
|
|
int pki_key::incUcount()
|
|
{
|
|
ucount++;
|
|
return ucount;
|
|
}
|
|
int pki_key::decUcount()
|
|
{
|
|
ucount--;
|
|
return ucount;
|
|
}
|
|
|
|
int pki_key::getUcount()
|
|
{
|
|
return ucount;
|
|
}
|
|
|
|
int pki_key::getKeyType() const
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
return EVP_PKEY_id(key);
|
|
#else
|
|
return key->type;
|
|
#endif
|
|
}
|
|
|
|
QString pki_key::modulus()
|
|
{
|
|
if (getKeyType() == EVP_PKEY_RSA) {
|
|
const BIGNUM *n = NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
RSA *rsa = EVP_PKEY_get0_RSA(key);
|
|
RSA_get0_key(rsa, &n, NULL, NULL);
|
|
#else
|
|
n = key->pkey.rsa->n;
|
|
#endif
|
|
|
|
return BN2QString(n);
|
|
}
|
|
|
|
|
|
return QString();
|
|
}
|
|
|
|
QString pki_key::pubEx()
|
|
{
|
|
if (getKeyType() == EVP_PKEY_RSA) {
|
|
const BIGNUM *e = NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
RSA *rsa = EVP_PKEY_get0_RSA(key);
|
|
RSA_get0_key(rsa, NULL, &e, NULL);
|
|
#else
|
|
e = key->pkey.rsa->e;
|
|
#endif
|
|
return BN2QString(e);
|
|
}
|
|
|
|
|
|
return QString();
|
|
}
|
|
|
|
QString pki_key::subprime()
|
|
{
|
|
if (getKeyType() == EVP_PKEY_DSA) {
|
|
const BIGNUM *q = NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
DSA *dsa = EVP_PKEY_get0_DSA(key);
|
|
if (dsa)
|
|
DSA_get0_pqg(dsa, NULL, &q, NULL);
|
|
#else
|
|
q = key->pkey.dsa->q;
|
|
#endif
|
|
|
|
return BN2QString(q);
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
QString pki_key::pubkey()
|
|
{
|
|
if (getKeyType() == EVP_PKEY_DSA) {
|
|
const BIGNUM *pubkey = NULL;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
DSA *dsa = EVP_PKEY_get0_DSA(key);
|
|
if (dsa)
|
|
DSA_get0_key(dsa, &pubkey, NULL);
|
|
#else
|
|
pubkey = key->pkey.dsa->pub_key;
|
|
#endif
|
|
|
|
return BN2QString(pubkey);
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
#ifndef OPENSSL_NO_EC
|
|
int pki_key::ecParamNid()
|
|
{
|
|
const EC_KEY *ec;
|
|
|
|
if (getKeyType() != EVP_PKEY_EC)
|
|
return NID_undef;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ec = EVP_PKEY_get0_EC_KEY(key);
|
|
#else
|
|
ec = key->pkey.ec;
|
|
#endif
|
|
|
|
return EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
|
|
}
|
|
|
|
QString pki_key::ecPubKey()
|
|
{
|
|
QString pub;
|
|
const EC_KEY *ec = NULL;
|
|
|
|
if (getKeyType() == EVP_PKEY_EC) {
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ec = EVP_PKEY_get0_EC_KEY(key);
|
|
#else
|
|
ec = key->pkey.ec;
|
|
#endif
|
|
|
|
BIGNUM *pub_key = EC_POINT_point2bn(EC_KEY_get0_group(ec),
|
|
EC_KEY_get0_public_key(ec),
|
|
EC_KEY_get_conv_form(ec), NULL, NULL);
|
|
if (pub_key) {
|
|
pub = BN2QString(pub_key);
|
|
BN_free(pub_key);
|
|
}
|
|
}
|
|
return pub;
|
|
}
|
|
#endif
|
|
|
|
QList<int> pki_key::possibleHashNids()
|
|
{
|
|
QList<int> nids;
|
|
|
|
switch (EVP_PKEY_type(getKeyType())) {
|
|
case EVP_PKEY_RSA:
|
|
nids << NID_md5 << NID_sha1 << NID_sha224 << NID_sha256 <<
|
|
NID_sha384 << NID_sha512 << NID_ripemd160;
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
nids << NID_sha1;
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
|
nids << NID_sha256;
|
|
#endif
|
|
break;
|
|
case EVP_PKEY_EC:
|
|
nids << NID_sha1;
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
|
nids << NID_sha224 << NID_sha256 << NID_sha384 << NID_sha512;
|
|
#endif
|
|
break;
|
|
}
|
|
return nids;
|
|
};
|
|
|
|
bool pki_key::compare(pki_base *ref)
|
|
{
|
|
pki_key *kref = (pki_key *)ref;
|
|
|
|
if (kref->getKeyType() != getKeyType())
|
|
return false;
|
|
if (!kref || !kref->key || !key)
|
|
return false;
|
|
|
|
int r = EVP_PKEY_cmp(key, kref->key);
|
|
pki_openssl_error();
|
|
return r == 1;
|
|
}
|
|
|
|
void pki_key::writePublic(const QString fname, bool pem)
|
|
{
|
|
FILE *fp = fopen_write(fname);
|
|
if (fp == NULL) {
|
|
fopen_error(fname);
|
|
return;
|
|
}
|
|
if (pem)
|
|
PEM_write_PUBKEY(fp, key);
|
|
else
|
|
i2d_PUBKEY_fp(fp, key);
|
|
|
|
fclose(fp);
|
|
pki_openssl_error();
|
|
}
|
|
|
|
QString pki_key::BNoneLine(BIGNUM *bn) const
|
|
{
|
|
QString x;
|
|
if (bn) {
|
|
char *hex = BN_bn2hex(bn);
|
|
x = hex;
|
|
OPENSSL_free(hex);
|
|
pki_openssl_error();
|
|
}
|
|
return x;
|
|
}
|
|
|
|
QString pki_key::BN2QString(const BIGNUM *bn) const
|
|
{
|
|
if (bn == NULL)
|
|
return "--";
|
|
QString x="";
|
|
char zs[10];
|
|
int j;
|
|
int size = BN_num_bytes(bn);
|
|
unsigned char *buf = (unsigned char *)OPENSSL_malloc(size);
|
|
check_oom(buf);
|
|
BN_bn2bin(bn, buf);
|
|
for (j = 0; j< size; j++) {
|
|
sprintf(zs, "%02X%c",buf[j], ((j+1)%16 == 0) ? '\n' :
|
|
j<size-1 ? ':' : ' ');
|
|
x += zs;
|
|
}
|
|
OPENSSL_free(buf);
|
|
pki_openssl_error();
|
|
return x;
|
|
}
|
|
|
|
QVariant pki_key::column_data(dbheader *hd)
|
|
{
|
|
QStringList sl;
|
|
sl << tr("Common") << tr("Private") << tr("Bogus") << tr("PIN");
|
|
switch (hd->id) {
|
|
case HD_key_type:
|
|
return QVariant(getTypeString());
|
|
case HD_key_size:
|
|
return QVariant(length());
|
|
case HD_key_use:
|
|
return QVariant(getUcount());
|
|
case HD_key_passwd:
|
|
if (isPubKey())
|
|
return QVariant(tr("No password"));
|
|
if (ownPass<0 || ownPass>3)
|
|
return QVariant("Holla die Waldfee");
|
|
return QVariant(sl[ownPass]);
|
|
case HD_key_curve:
|
|
QString r;
|
|
#ifndef OPENSSL_NO_EC
|
|
if (getKeyType() == EVP_PKEY_EC)
|
|
r = OBJ_nid2sn(ecParamNid());
|
|
#endif
|
|
return QVariant(r);
|
|
}
|
|
return pki_base::column_data(hd);
|
|
}
|
|
|
|
BIGNUM *pki_key::ssh_key_data2bn(QByteArray *ba, bool skip)
|
|
{
|
|
const unsigned char *d = (const unsigned char *)ba->constData();
|
|
uint32_t len;
|
|
BIGNUM *bn = NULL;
|
|
|
|
if (ba->size() < 4)
|
|
throw errorEx(tr("Invalid SSH2 public key"));
|
|
len = (d[0] << 24) + (d[1] << 16) + (d[2] << 8) + d[3];
|
|
if (!skip) {
|
|
bn = BN_bin2bn(d+4, len, NULL);
|
|
if (!ba)
|
|
throw errorEx(tr("Invalid SSH2 public key"));
|
|
}
|
|
if (ba->size() < (ssize_t)len + 4)
|
|
throw errorEx(tr("Invalid SSH2 public key"));
|
|
ba->remove(0, len+4);
|
|
return bn;
|
|
}
|
|
|
|
EVP_PKEY *pki_key::load_ssh2_key(FILE *fp)
|
|
{
|
|
/* See RFC 4253 Section 6.6 */
|
|
QByteArray ba;
|
|
QStringList sl;
|
|
int type;
|
|
EVP_PKEY *pk = NULL;
|
|
|
|
ba.resize(4096);
|
|
|
|
if (!fgets(ba.data(), ba.size(), fp)) {
|
|
return NULL;
|
|
}
|
|
sl = QString(ba).split(" ", QString::SkipEmptyParts);
|
|
if (sl.size() < 2)
|
|
return NULL;
|
|
if (sl[0].startsWith("ssh-rsa"))
|
|
type = EVP_PKEY_RSA;
|
|
else if (sl[0].startsWith("ssh-dss"))
|
|
type = EVP_PKEY_DSA;
|
|
else
|
|
return NULL;
|
|
|
|
ba = QByteArray::fromBase64(sl[1].toLatin1());
|
|
switch (type) {
|
|
case EVP_PKEY_RSA: {
|
|
RSA *rsa = RSA_new();
|
|
/* Skip "ssh-rsa..." */
|
|
ssh_key_data2bn(&ba, true);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIGNUM *e = ssh_key_data2bn(&ba);
|
|
BIGNUM *n = ssh_key_data2bn(&ba);
|
|
RSA_set0_key(rsa, n, e, NULL);
|
|
#else
|
|
rsa->e = ssh_key_data2bn(&ba);
|
|
rsa->n = ssh_key_data2bn(&ba);
|
|
#endif
|
|
|
|
pk = EVP_PKEY_new();
|
|
EVP_PKEY_assign_RSA(pk, rsa);
|
|
break;
|
|
}
|
|
case EVP_PKEY_DSA: {
|
|
DSA *dsa = DSA_new();
|
|
/* Skip "ssh-dsa..." */
|
|
ssh_key_data2bn(&ba, true);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIGNUM *p = ssh_key_data2bn(&ba);
|
|
BIGNUM *q = ssh_key_data2bn(&ba);
|
|
BIGNUM *g = ssh_key_data2bn(&ba);
|
|
BIGNUM *pubkey = ssh_key_data2bn(&ba);
|
|
DSA_set0_pqg(dsa, p, q, g);
|
|
DSA_set0_key(dsa, pubkey, NULL);
|
|
#else
|
|
dsa->p = ssh_key_data2bn(&ba);
|
|
dsa->q = ssh_key_data2bn(&ba);
|
|
dsa->g = ssh_key_data2bn(&ba);
|
|
dsa->pub_key = ssh_key_data2bn(&ba);
|
|
#endif
|
|
|
|
pk = EVP_PKEY_new();
|
|
EVP_PKEY_assign_DSA(pk, dsa);
|
|
}
|
|
}
|
|
return pk;
|
|
}
|
|
|
|
void pki_key::ssh_key_QBA2data(QByteArray &ba, QByteArray *data)
|
|
{
|
|
int size = ba.size();
|
|
unsigned char p[4];
|
|
|
|
p[0] = (size >> 24) & 0xff;
|
|
p[1] = (size >> 16) & 0xff;
|
|
p[2] = (size >> 8) & 0xff;
|
|
p[3] = size & 0xff;
|
|
data->append((char*)p, sizeof p);
|
|
data->append(ba);
|
|
}
|
|
|
|
void pki_key::ssh_key_bn2data(const BIGNUM *bn, QByteArray *data)
|
|
{
|
|
QByteArray big;
|
|
big.resize(BN_num_bytes(bn));
|
|
BN_bn2bin(bn, (unsigned char *)big.data());
|
|
pki_openssl_error();
|
|
if ((unsigned char)big[0] >= 0x80)
|
|
big.prepend('\0');
|
|
ssh_key_QBA2data(big, data);
|
|
}
|
|
|
|
QByteArray pki_key::SSH2publicQByteArray()
|
|
{
|
|
QByteArray txt, data;
|
|
|
|
switch (getKeyType()) {
|
|
case EVP_PKEY_RSA:
|
|
txt = "ssh-rsa";
|
|
ssh_key_QBA2data(txt, &data);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
{
|
|
RSA *rsa = EVP_PKEY_get0_RSA(key);
|
|
const BIGNUM *n, *e;
|
|
RSA_get0_key(rsa, &n, &e, NULL);
|
|
ssh_key_bn2data(e, &data);
|
|
ssh_key_bn2data(n, &data);
|
|
}
|
|
#else
|
|
ssh_key_bn2data(key->pkey.rsa->e, &data);
|
|
ssh_key_bn2data(key->pkey.rsa->n, &data);
|
|
#endif
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
txt = "ssh-dss";
|
|
ssh_key_QBA2data(txt, &data);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
{
|
|
DSA *dsa = EVP_PKEY_get0_DSA(key);
|
|
const BIGNUM *p, *q, *g, *pubkey;
|
|
DSA_get0_pqg(dsa, &p, &q, &g);
|
|
DSA_get0_key(dsa, &pubkey, NULL);
|
|
ssh_key_bn2data(p, &data);
|
|
ssh_key_bn2data(q, &data);
|
|
ssh_key_bn2data(g, &data);
|
|
ssh_key_bn2data(pubkey, &data);
|
|
}
|
|
#else
|
|
ssh_key_bn2data(key->pkey.dsa->p, &data);
|
|
ssh_key_bn2data(key->pkey.dsa->q, &data);
|
|
ssh_key_bn2data(key->pkey.dsa->g, &data);
|
|
ssh_key_bn2data(key->pkey.dsa->pub_key, &data);
|
|
#endif
|
|
break;
|
|
default:
|
|
return QByteArray();
|
|
}
|
|
return txt + " " + data.toBase64() + "\n";
|
|
}
|
|
|
|
void pki_key::writeSSH2public(QString fname)
|
|
{
|
|
QFile f(fname);
|
|
|
|
if (!f.open(QIODevice::ReadWrite))
|
|
fopen_error(fname);
|
|
else {
|
|
QByteArray txt = SSH2publicQByteArray();
|
|
if (f.write(txt) != txt.size())
|
|
throw errorEx(tr("Failed writing to %1").arg(fname));
|
|
f.close();
|
|
}
|
|
}
|