mirror of
https://github.com/chris2511/xca.git
synced 2026-09-13 18:46:25 +05:00
If the database password is empty, dumping private keys resulted in an error message. Fix this by setting the encryption algorithm to NULL if the password is empty. Additionally throw the error after closing the filedescriptor.
941 lines
20 KiB
C++
941 lines
20 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2015 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
|
|
#include "pki_evp.h"
|
|
#include "pass_info.h"
|
|
#include "Passwd.h"
|
|
#include "func.h"
|
|
#include "db.h"
|
|
#include "entropy.h"
|
|
#include "widgets/PwDialog.h"
|
|
|
|
#include <openssl/rand.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/pem.h>
|
|
|
|
#include <QProgressDialog>
|
|
#include <QApplication>
|
|
#include <QDir>
|
|
|
|
#include "openssl_compat.h"
|
|
|
|
Passwd pki_evp::passwd;
|
|
Passwd pki_evp::oldpasswd;
|
|
|
|
QString pki_evp::passHash = QString();
|
|
|
|
QPixmap *pki_evp::icon[2]= { NULL, NULL };
|
|
|
|
void pki_evp::init()
|
|
{
|
|
class_name = "pki_evp";
|
|
ownPass = ptCommon;
|
|
dataVersion=2;
|
|
pkiType=asym_key;
|
|
}
|
|
|
|
QString pki_evp::removeTypeFromIntName(QString n)
|
|
{
|
|
if (n.right(1) != ")" )
|
|
return n;
|
|
n.truncate(n.length() - 6);
|
|
return n;
|
|
}
|
|
|
|
void pki_evp::setOwnPass(enum passType x)
|
|
{
|
|
EVP_PKEY *pk=NULL, *pk_back = key;
|
|
int oldOwnPass = ownPass;
|
|
|
|
if (ownPass == x || isPubKey())
|
|
return;
|
|
|
|
try {
|
|
pk = decryptKey();
|
|
if (pk == NULL)
|
|
return;
|
|
|
|
key = pk;
|
|
ownPass = x;
|
|
encryptKey();
|
|
}
|
|
catch (errorEx &err) {
|
|
if (pk)
|
|
EVP_PKEY_free(pk);
|
|
key = pk_back;
|
|
ownPass = oldOwnPass;
|
|
throw(err);
|
|
}
|
|
EVP_PKEY_free(pk_back);
|
|
}
|
|
|
|
void pki_evp::generate(int bits, int type, QProgressBar *progress, int curve_nid)
|
|
{
|
|
Entropy::seed_rng();
|
|
|
|
#ifdef OPENSSL_NO_EC
|
|
(void)curve_nid;
|
|
#endif
|
|
progress->setMinimum(0);
|
|
progress->setMaximum(100);
|
|
progress->setValue(50);
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
BN_GENCB _bar, *bar = &_bar;
|
|
#else
|
|
BN_GENCB *bar = BN_GENCB_new();
|
|
#endif
|
|
BN_GENCB_set_old(bar, inc_progress_bar, progress);
|
|
|
|
switch (type) {
|
|
case EVP_PKEY_RSA: {
|
|
RSA *rsakey = RSA_new();
|
|
BIGNUM *e = BN_new();
|
|
BN_set_word(e, 0x10001);
|
|
if (RSA_generate_key_ex(rsakey, bits, e, bar))
|
|
EVP_PKEY_assign_RSA(key, rsakey);
|
|
else
|
|
RSA_free(rsakey);
|
|
BN_free(e);
|
|
break;
|
|
}
|
|
case EVP_PKEY_DSA: {
|
|
DSA *dsakey = DSA_new();
|
|
progress->setMaximum(500);
|
|
if (DSA_generate_parameters_ex(dsakey, bits, NULL, 0, NULL,
|
|
NULL, bar) && DSA_generate_key(dsakey))
|
|
EVP_PKEY_assign_DSA(key, dsakey);
|
|
else
|
|
DSA_free(dsakey);
|
|
break;
|
|
}
|
|
#ifndef OPENSSL_NO_EC
|
|
case EVP_PKEY_EC:
|
|
EC_KEY *eckey;
|
|
EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_nid);
|
|
if (!group)
|
|
break;
|
|
eckey = EC_KEY_new();
|
|
if (eckey == NULL) {
|
|
EC_GROUP_free(group);
|
|
break;
|
|
}
|
|
EC_GROUP_set_asn1_flag(group, 1);
|
|
if (EC_KEY_set_group(eckey, group)) {
|
|
if (EC_KEY_generate_key(eckey)) {
|
|
EVP_PKEY_assign_EC_KEY(key, eckey);
|
|
EC_GROUP_free(group);
|
|
break;
|
|
}
|
|
}
|
|
EC_KEY_free(eckey);
|
|
EC_GROUP_free(group);
|
|
break;
|
|
#endif
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BN_GENCB_free(bar);
|
|
#endif
|
|
|
|
pki_openssl_error();
|
|
encryptKey();
|
|
}
|
|
|
|
pki_evp::pki_evp(const pki_evp *pk)
|
|
:pki_key(pk)
|
|
{
|
|
init();
|
|
pki_openssl_error();
|
|
ownPass = pk->ownPass;
|
|
encKey = pk->encKey;
|
|
}
|
|
|
|
pki_evp::pki_evp(const QString name, int type)
|
|
:pki_key(name)
|
|
{
|
|
init();
|
|
EVP_PKEY_set_type(key, type);
|
|
pki_openssl_error();
|
|
}
|
|
|
|
pki_evp::pki_evp(EVP_PKEY *pkey)
|
|
:pki_key()
|
|
{
|
|
init();
|
|
if (key) {
|
|
EVP_PKEY_free(key);
|
|
}
|
|
key = pkey;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
static bool EVP_PKEY_isPrivKey(EVP_PKEY *key)
|
|
{
|
|
const BIGNUM *b;
|
|
int keytype = EVP_PKEY_id(key);
|
|
|
|
switch (EVP_PKEY_type(keytype)) {
|
|
case EVP_PKEY_RSA:
|
|
RSA_get0_key(EVP_PKEY_get0_RSA(key), NULL, NULL, &b);
|
|
return b ? true: false;
|
|
case EVP_PKEY_DSA:
|
|
DSA_get0_key(EVP_PKEY_get0_DSA(key), NULL, &b);
|
|
return b ? true: false;
|
|
#ifndef OPENSSL_NO_EC
|
|
case EVP_PKEY_EC:
|
|
return EC_KEY_get0_private_key(
|
|
EVP_PKEY_get0_EC_KEY(key)) ? true: false;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
|
|
static bool EVP_PKEY_isPrivKey(EVP_PKEY *key)
|
|
{
|
|
int keytype;
|
|
|
|
keytype = EVP_PKEY_id(key);
|
|
|
|
switch (EVP_PKEY_type(keytype)) {
|
|
case EVP_PKEY_RSA:
|
|
return key->pkey.rsa->d ? true: false;
|
|
case EVP_PKEY_DSA:
|
|
return key->pkey.dsa->priv_key ? true: false;
|
|
#ifndef OPENSSL_NO_EC
|
|
case EVP_PKEY_EC:
|
|
return EC_KEY_get0_private_key(key->pkey.ec) ? true: false;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
void pki_evp::openssl_pw_error(QString fname)
|
|
{
|
|
switch (ERR_peek_error() & 0xff000fff) {
|
|
case ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_DECRYPT):
|
|
case ERR_PACK(ERR_LIB_PEM, 0, PEM_R_BAD_PASSWORD_READ):
|
|
case ERR_PACK(ERR_LIB_EVP, 0, EVP_R_BAD_DECRYPT):
|
|
pki_ign_openssl_error();
|
|
throw errorEx(tr("Failed to decrypt the key (bad password) ")+
|
|
fname, class_name, E_PASSWD);
|
|
}
|
|
}
|
|
|
|
void pki_evp::fromPEMbyteArray(QByteArray &ba, QString name)
|
|
{
|
|
BIO *bio = BIO_new_mem_buf(ba.data(), ba.length());
|
|
EVP_PKEY *pkey;
|
|
pass_info p(XCA_TITLE,
|
|
tr("Please enter the password to decrypt the private key.") +
|
|
" " + name);
|
|
pkey = PEM_read_bio_PrivateKey(bio, NULL, PwDialog::pwCallback, &p);
|
|
openssl_pw_error(name);
|
|
if (!pkey){
|
|
pki_ign_openssl_error();
|
|
BIO_free(bio);
|
|
bio = BIO_new_mem_buf(ba.data(), ba.length());
|
|
pkey = PEM_read_bio_PUBKEY(bio, NULL, PwDialog::pwCallback, &p);
|
|
}
|
|
BIO_free(bio);
|
|
if (pkey){
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
key = pkey;
|
|
if (EVP_PKEY_isPrivKey(key))
|
|
bogusEncryptKey();
|
|
setIntName(rmslashdot(name));
|
|
}
|
|
openssl_error(name);
|
|
}
|
|
|
|
static void search_ec_oid(EVP_PKEY *pkey)
|
|
{
|
|
#ifndef OPENSSL_NO_EC
|
|
EC_KEY *ec;
|
|
int keytype = EVP_PKEY_id(pkey);
|
|
|
|
if (keytype != EVP_PKEY_EC)
|
|
return;
|
|
|
|
ec = EVP_PKEY_get0_EC_KEY(pkey);
|
|
|
|
const EC_GROUP *ec_group = EC_KEY_get0_group(ec);
|
|
EC_GROUP *builtin;
|
|
|
|
if (!ec_group)
|
|
return;
|
|
if (EC_GROUP_get_curve_name(ec_group))
|
|
return;
|
|
/* There is an EC_GROUP with a missing OID
|
|
* because of explicit parameters */
|
|
foreach(builtin_curve curve, pki_key::builtinCurves) {
|
|
builtin = EC_GROUP_new_by_curve_name(curve.nid);
|
|
if (EC_GROUP_cmp(builtin, ec_group, NULL) == 0) {
|
|
EC_GROUP_set_curve_name((EC_GROUP *)ec_group, curve.nid);
|
|
EC_GROUP_set_asn1_flag((EC_GROUP *)ec_group, 1);
|
|
EC_GROUP_free(builtin);
|
|
break;
|
|
}
|
|
EC_GROUP_free(builtin);
|
|
}
|
|
#else
|
|
(void)pkey;
|
|
#endif
|
|
}
|
|
|
|
void pki_evp::fload(const QString fname)
|
|
{
|
|
pass_info p(XCA_TITLE, tr("Please enter the password to decrypt the private key from file:\n%1").
|
|
arg(compressFilename(fname)));
|
|
pem_password_cb *cb = PwDialog::pwCallback;
|
|
FILE *fp = fopen_read(fname);
|
|
EVP_PKEY *pkey;
|
|
|
|
pki_ign_openssl_error();
|
|
if (!fp) {
|
|
fopen_error(fname);
|
|
return;
|
|
}
|
|
pkey = PEM_read_PrivateKey(fp, NULL, cb, &p);
|
|
try {
|
|
openssl_pw_error(fname);
|
|
} catch (errorEx &err) {
|
|
fclose(fp);
|
|
throw err;
|
|
}
|
|
if (!pkey) {
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
pkey = d2i_PrivateKey_fp(fp, NULL);
|
|
}
|
|
if (!pkey) {
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
pkey = d2i_PKCS8PrivateKey_fp(fp, NULL, cb, &p);
|
|
}
|
|
if (!pkey) {
|
|
PKCS8_PRIV_KEY_INFO *p8inf;
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
p8inf = d2i_PKCS8_PRIV_KEY_INFO_fp(fp, NULL);
|
|
if (p8inf) {
|
|
pkey = EVP_PKCS82PKEY(p8inf);
|
|
PKCS8_PRIV_KEY_INFO_free(p8inf);
|
|
}
|
|
}
|
|
if (!pkey) {
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
pkey = PEM_read_PUBKEY(fp, NULL, cb, &p);
|
|
}
|
|
if (!pkey) {
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
pkey = d2i_PUBKEY_fp(fp, NULL);
|
|
}
|
|
if (!pkey) {
|
|
pki_ign_openssl_error();
|
|
rewind(fp);
|
|
try {
|
|
pkey = load_ssh2_key(fp);
|
|
} catch (errorEx &err) {
|
|
fclose(fp);
|
|
throw err;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
if (!pkey || pki_ign_openssl_error()) {
|
|
if (pkey)
|
|
EVP_PKEY_free(pkey);
|
|
throw errorEx(tr("Unable to load the private key in file %1. Tried PEM and DER private, public, PKCS#8 key types and SSH2 format.").arg(fname));
|
|
}
|
|
if (pkey){
|
|
search_ec_oid(pkey);
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
key = pkey;
|
|
if (EVP_PKEY_isPrivKey(key))
|
|
bogusEncryptKey();
|
|
setIntName(rmslashdot(fname));
|
|
}
|
|
}
|
|
|
|
void pki_evp::fromData(const unsigned char *p, db_header_t *head)
|
|
{
|
|
int version, type, size;
|
|
void *ptr = NULL;
|
|
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
key = NULL;
|
|
|
|
size = head->len - sizeof(db_header_t);
|
|
version = head->version;
|
|
|
|
QByteArray ba((const char*)p, size);
|
|
|
|
type = db::intFromData(ba);
|
|
ownPass = db::intFromData(ba);
|
|
if (version < 2) {
|
|
d2i_old(ba, type);
|
|
} else {
|
|
d2i(ba);
|
|
}
|
|
pki_openssl_error();
|
|
|
|
if (key)
|
|
ptr = EVP_PKEY_get0(key);
|
|
if (!ptr)
|
|
throw errorEx(tr("Ignoring unsupported private key"));
|
|
|
|
encKey = ba;
|
|
}
|
|
|
|
EVP_PKEY *pki_evp::decryptKey() const
|
|
{
|
|
unsigned char *p;
|
|
const unsigned char *p1;
|
|
int outl, decsize;
|
|
unsigned char iv[EVP_MAX_IV_LENGTH];
|
|
unsigned char ckey[EVP_MAX_KEY_LENGTH];
|
|
|
|
EVP_PKEY *tmpkey;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_CIPHER_CTX ctxbuf;
|
|
#endif
|
|
EVP_CIPHER_CTX *ctx;
|
|
const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
|
|
Passwd ownPassBuf;
|
|
int ret;
|
|
|
|
if (isPubKey()) {
|
|
unsigned char *q;
|
|
outl = i2d_PUBKEY(key, NULL);
|
|
p = q = (unsigned char *)OPENSSL_malloc(outl);
|
|
check_oom(q);
|
|
i2d_PUBKEY(key, &p);
|
|
p = q;
|
|
tmpkey = d2i_PUBKEY(NULL, (const unsigned char**)&p, outl);
|
|
OPENSSL_free(q);
|
|
return tmpkey;
|
|
}
|
|
/* This key has its own password */
|
|
if (ownPass == ptPrivate) {
|
|
pass_info pi(XCA_TITLE, tr("Please enter the password to decrypt the private key: '%1'").arg(getIntName()));
|
|
ret = PwDialog::execute(&pi, &ownPassBuf, false);
|
|
if (ret != 1)
|
|
throw errorEx(tr("Password input aborted"), class_name);
|
|
} else if (ownPass == ptBogus) { // BOGUS pass
|
|
ownPassBuf = "Bogus";
|
|
} else {
|
|
ownPassBuf = passwd;
|
|
while (md5passwd(ownPassBuf) != passHash &&
|
|
sha512passwd(ownPassBuf, passHash) != passHash)
|
|
{
|
|
pass_info p(XCA_TITLE, tr("Please enter the database password for decrypting the key '%1'").arg(getIntName()));
|
|
ret = PwDialog::execute(&p, &ownPassBuf, false);
|
|
if (ret != 1)
|
|
throw errorEx(tr("Password input aborted"), class_name);
|
|
}
|
|
}
|
|
p = (unsigned char *)OPENSSL_malloc(encKey.count());
|
|
check_oom(p);
|
|
pki_openssl_error();
|
|
p1 = p;
|
|
memset(iv, 0, EVP_MAX_IV_LENGTH);
|
|
|
|
memcpy(iv, encKey.constData(), 8); /* recover the iv */
|
|
/* generate the key */
|
|
EVP_BytesToKey(cipher, EVP_sha1(), iv,
|
|
ownPassBuf.constUchar(),
|
|
ownPassBuf.size(), 1, ckey, NULL);
|
|
/* we use sha1 as message digest,
|
|
* because an md5 version of the password is
|
|
* stored in the database...
|
|
*/
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
#else
|
|
ctx = &ctxbuf;
|
|
#endif
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
EVP_DecryptInit(ctx, cipher, ckey, iv);
|
|
EVP_DecryptUpdate(ctx, p , &outl,
|
|
(const unsigned char*)encKey.constData() +8, encKey.count() -8);
|
|
|
|
decsize = outl;
|
|
EVP_DecryptFinal(ctx, p + decsize , &outl);
|
|
decsize += outl;
|
|
//printf("Decrypt decsize=%d, encKey_len=%d\n", decsize, encKey.count() -8);
|
|
pki_openssl_error();
|
|
tmpkey = d2i_PrivateKey(getKeyType(), NULL, &p1, decsize);
|
|
pki_openssl_error();
|
|
OPENSSL_free(p);
|
|
EVP_CIPHER_CTX_cleanup(ctx);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
#endif
|
|
pki_openssl_error();
|
|
if (EVP_PKEY_type(getKeyType()) == EVP_PKEY_RSA) {
|
|
RSA *rsa = EVP_PKEY_get0_RSA(tmpkey);
|
|
RSA_blinding_on(rsa, NULL);
|
|
}
|
|
return tmpkey;
|
|
}
|
|
|
|
QByteArray pki_evp::toData()
|
|
{
|
|
QByteArray ba;
|
|
|
|
ba += db::intToData(getKeyType());
|
|
ba += db::intToData(ownPass);
|
|
ba += i2d();
|
|
ba += encKey;
|
|
return ba;
|
|
}
|
|
|
|
EVP_PKEY *pki_evp::priv2pub(EVP_PKEY* key)
|
|
{
|
|
int keylen;
|
|
unsigned char *p, *p1;
|
|
EVP_PKEY *pubkey;
|
|
|
|
keylen = i2d_PUBKEY(key, NULL);
|
|
p1 = p = (unsigned char *)OPENSSL_malloc(keylen);
|
|
check_oom(p);
|
|
|
|
/* convert rsa/dsa/ec to Pubkey */
|
|
keylen = i2d_PUBKEY(key, &p);
|
|
pki_openssl_error();
|
|
p = p1;
|
|
pubkey = d2i_PUBKEY(NULL, (const unsigned char**)&p, keylen);
|
|
OPENSSL_free(p1);
|
|
pki_openssl_error();
|
|
return pubkey;
|
|
}
|
|
|
|
void pki_evp::encryptKey(const char *password)
|
|
{
|
|
int outl, keylen;
|
|
EVP_PKEY *pkey1 = NULL;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_CIPHER_CTX ctxbuf;
|
|
#endif
|
|
EVP_CIPHER_CTX *ctx;
|
|
const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
|
|
unsigned char iv[EVP_MAX_IV_LENGTH], *punenc, *punenc1;
|
|
unsigned char ckey[EVP_MAX_KEY_LENGTH];
|
|
Passwd ownPassBuf;
|
|
|
|
/* This key has its own, private password */
|
|
if (ownPass == ptPrivate) {
|
|
int ret;
|
|
pass_info p(XCA_TITLE, tr("Please enter the password to protect the private key: '%1'").
|
|
arg(getIntName()));
|
|
ret = PwDialog::execute(&p, &ownPassBuf, true);
|
|
if (ret != 1)
|
|
throw errorEx("Password input aborted", class_name);
|
|
} else if (ownPass == ptBogus) { // BOGUS password
|
|
ownPassBuf = "Bogus";
|
|
} else {
|
|
if (password) {
|
|
/* use the password parameter
|
|
* if this is a common password */
|
|
ownPassBuf = password;
|
|
} else {
|
|
int ret = 0;
|
|
ownPassBuf = passwd;
|
|
pass_info p(XCA_TITLE, tr("Please enter the database password for encrypting the key"));
|
|
while (md5passwd(ownPassBuf) != passHash &&
|
|
sha512passwd(ownPassBuf, passHash) != passHash )
|
|
{
|
|
ret = PwDialog::execute(&p, &ownPassBuf, false);
|
|
if (ret != 1)
|
|
throw errorEx("Password input aborted", class_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Prepare Encryption */
|
|
memset(iv, 0, EVP_MAX_IV_LENGTH);
|
|
Entropy::get(iv, 8); /* Generate a salt */
|
|
EVP_BytesToKey(cipher, EVP_sha1(), iv,
|
|
ownPassBuf.constUchar(),
|
|
ownPassBuf.size(), 1, ckey, NULL);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
#else
|
|
ctx = &ctxbuf;
|
|
#endif
|
|
EVP_CIPHER_CTX_init (ctx);
|
|
pki_openssl_error();
|
|
|
|
/* reserve space for unencrypted and encrypted key */
|
|
keylen = i2d_PrivateKey(key, NULL);
|
|
encKey.resize(keylen + EVP_MAX_KEY_LENGTH + 8);
|
|
punenc1 = punenc = (unsigned char *)OPENSSL_malloc(keylen);
|
|
check_oom(punenc);
|
|
keylen = i2d_PrivateKey(key, &punenc1);
|
|
pki_openssl_error();
|
|
|
|
memcpy(encKey.data(), iv, 8); /* store the iv */
|
|
/*
|
|
* Now DER version of privkey is in punenc
|
|
* and privkey is still in key
|
|
*/
|
|
|
|
/* do the encryption */
|
|
/* store key right after the iv */
|
|
EVP_EncryptInit(ctx, cipher, ckey, iv);
|
|
unsigned char *penc = (unsigned char *)encKey.data() +8;
|
|
EVP_EncryptUpdate(ctx, penc, &outl, punenc, keylen);
|
|
int encKey_len = outl;
|
|
EVP_EncryptFinal(ctx, penc + encKey_len, &outl);
|
|
encKey.resize(encKey_len + outl +8);
|
|
/* Cleanup */
|
|
EVP_CIPHER_CTX_cleanup(ctx);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
#endif
|
|
/* wipe out the memory */
|
|
memset(punenc, 0, keylen);
|
|
OPENSSL_free(punenc);
|
|
pki_openssl_error();
|
|
|
|
pkey1 = priv2pub(key);
|
|
check_oom(pkey1);
|
|
EVP_PKEY_free(key);
|
|
key = pkey1;
|
|
pki_openssl_error();
|
|
|
|
//CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
|
|
|
|
//printf("Encrypt: encKey_len=%d\n", encKey_len);
|
|
return;
|
|
}
|
|
|
|
void pki_evp::set_evp_key(EVP_PKEY *pkey)
|
|
{
|
|
if (key)
|
|
free(key);
|
|
key = pkey;
|
|
}
|
|
|
|
void pki_evp::bogusEncryptKey()
|
|
{
|
|
ownPass = ptBogus;
|
|
encryptKey();
|
|
}
|
|
|
|
pki_evp::~pki_evp()
|
|
{
|
|
}
|
|
|
|
|
|
void pki_evp::writePKCS8(const QString fname, const EVP_CIPHER *enc,
|
|
pem_password_cb *cb, bool pem)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
pass_info p(XCA_TITLE, tr("Please enter the password protecting the PKCS#8 key '%1'").arg(getIntName()));
|
|
FILE *fp = fopen_write(fname);
|
|
if (fp != NULL) {
|
|
if (key) {
|
|
pkey = decryptKey();
|
|
if (pkey) {
|
|
if (pem)
|
|
PEM_write_PKCS8PrivateKey(fp, pkey, enc, NULL, 0, cb, &p);
|
|
else
|
|
i2d_PKCS8PrivateKey_fp(fp, pkey, enc, NULL, 0, cb, &p);
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
pki_openssl_error();
|
|
} else
|
|
fopen_error(fname);
|
|
}
|
|
|
|
static int mycb(char *buf, int size, int, void *)
|
|
{
|
|
strncpy(buf, pki_evp::passwd, size);
|
|
return strlen(pki_evp::passwd);
|
|
}
|
|
|
|
void pki_evp::writeDefault(const QString fname)
|
|
{
|
|
writeKey(fname + QDir::separator() + getIntName() + ".pem",
|
|
pki_evp::passwd[0] ? EVP_des_ede3_cbc() : NULL,
|
|
mycb, true);
|
|
}
|
|
|
|
void pki_evp::writeKey(const QString fname, const EVP_CIPHER *enc,
|
|
pem_password_cb *cb, bool pem)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
int keytype;
|
|
pass_info p(XCA_TITLE, tr("Please enter the export password for the private key '%1'").arg(getIntName()));
|
|
if (isPubKey()) {
|
|
writePublic(fname, pem);
|
|
return;
|
|
}
|
|
FILE *fp = fopen_write(fname);
|
|
if (!fp) {
|
|
fopen_error(fname);
|
|
return;
|
|
}
|
|
if (key){
|
|
pkey = decryptKey();
|
|
if (pkey) {
|
|
if (pem) {
|
|
keytype = EVP_PKEY_id(pkey);
|
|
switch (keytype) {
|
|
case EVP_PKEY_RSA:
|
|
PEM_write_RSAPrivateKey(fp,
|
|
EVP_PKEY_get0_RSA(pkey),
|
|
enc, NULL, 0, cb, &p);
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
PEM_write_DSAPrivateKey(fp,
|
|
EVP_PKEY_get0_DSA(pkey),
|
|
enc, NULL, 0, cb, &p);
|
|
break;
|
|
#ifndef OPENSSL_NO_EC
|
|
case EVP_PKEY_EC:
|
|
PEM_write_ECPrivateKey(fp,
|
|
EVP_PKEY_get0_EC_KEY(pkey),
|
|
enc, NULL, 0, cb, &p);
|
|
break;
|
|
#endif
|
|
default:
|
|
PEM_write_PrivateKey(fp,
|
|
pkey,
|
|
enc, NULL, 0, cb, &p);
|
|
}
|
|
} else {
|
|
i2d_PrivateKey_fp(fp, pkey);
|
|
}
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
pki_openssl_error();
|
|
}
|
|
|
|
bool pki_evp::isPubKey() const
|
|
{
|
|
if (encKey.count() == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int pki_evp::verify()
|
|
{
|
|
bool veri = false;
|
|
return true;
|
|
if (getKeyType() == EVP_PKEY_RSA && isPrivKey()) {
|
|
RSA *rsa = EVP_PKEY_get0_RSA(key);
|
|
if (RSA_check_key(rsa) == 1)
|
|
veri = true;
|
|
}
|
|
if (isPrivKey())
|
|
veri = true;
|
|
pki_openssl_error();
|
|
return veri;
|
|
}
|
|
|
|
const EVP_MD *pki_evp::getDefaultMD()
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
return EVP_sha1();
|
|
#else
|
|
const EVP_MD *md;
|
|
switch (getKeyType()) {
|
|
case EVP_PKEY_RSA: md = EVP_sha1(); break;
|
|
case EVP_PKEY_DSA: md = EVP_dss1(); break;
|
|
#ifndef OPENSSL_NO_EC
|
|
case EVP_PKEY_EC: md = EVP_ecdsa(); break;
|
|
#endif
|
|
default: md = NULL; break;
|
|
}
|
|
return md;
|
|
#endif
|
|
}
|
|
|
|
QVariant pki_evp::getIcon(dbheader *hd)
|
|
{
|
|
if (hd->id != HD_internal_name)
|
|
return QVariant();
|
|
int pixnum= isPubKey() ? 1 : 0;
|
|
return QVariant(*icon[pixnum]);
|
|
}
|
|
|
|
QString pki_evp::md5passwd(QByteArray pass)
|
|
{
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_MD_CTX mdctxbuf;
|
|
#endif
|
|
EVP_MD_CTX *mdctx;
|
|
QString str;
|
|
int n;
|
|
int j;
|
|
unsigned char m[EVP_MAX_MD_SIZE];
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
mdctx = EVP_MD_CTX_new();
|
|
#else
|
|
mdctx = &mdctxbuf;
|
|
#endif
|
|
|
|
EVP_DigestInit(mdctx, EVP_md5());
|
|
EVP_DigestUpdate(mdctx, pass.constData(), pass.size());
|
|
EVP_DigestFinal(mdctx, m, (unsigned*)&n);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_MD_CTX_free(mdctx);
|
|
#endif
|
|
|
|
for (j=0; j<n; j++) {
|
|
char zs[4];
|
|
sprintf(zs, "%02X%c",m[j], (j+1 == n) ?'\0':':');
|
|
str += zs;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
QString pki_evp::sha512passwd(QByteArray pass, QString salt)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_MD_CTX mdctxbuf;
|
|
#endif
|
|
EVP_MD_CTX *mdctx;
|
|
QString str;
|
|
int n;
|
|
int j;
|
|
unsigned char m[EVP_MAX_MD_SIZE];
|
|
|
|
if (salt.length() <5)
|
|
abort();
|
|
|
|
str = salt.left(5);
|
|
pass = str.toLatin1() + pass;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
mdctx = EVP_MD_CTX_new();
|
|
#else
|
|
mdctx = &mdctxbuf;
|
|
#endif
|
|
|
|
EVP_DigestInit(mdctx, EVP_sha512());
|
|
EVP_DigestUpdate(mdctx, pass.constData(), pass.size());
|
|
EVP_DigestFinal(mdctx, m, (unsigned*)&n);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_MD_CTX_free(mdctx);
|
|
#endif
|
|
|
|
for (j=0; j<n; j++) {
|
|
char zs[4];
|
|
sprintf(zs, "%02X",m[j]);
|
|
str += zs;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
void pki_evp::veryOldFromData(unsigned char *p, int size )
|
|
{
|
|
unsigned char *sik, *pdec, *pdec1, *sik1;
|
|
int outl, decsize;
|
|
unsigned char iv[EVP_MAX_IV_LENGTH];
|
|
unsigned char ckey[EVP_MAX_KEY_LENGTH];
|
|
memset(iv, 0, EVP_MAX_IV_LENGTH);
|
|
RSA *rsakey;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
EVP_CIPHER_CTX ctxbuf;
|
|
#endif
|
|
EVP_CIPHER_CTX *ctx;
|
|
const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
|
|
sik = (unsigned char *)OPENSSL_malloc(size);
|
|
check_oom(sik);
|
|
pki_openssl_error();
|
|
pdec = (unsigned char *)OPENSSL_malloc(size);
|
|
if (pdec == NULL ) {
|
|
OPENSSL_free(sik);
|
|
check_oom(pdec);
|
|
}
|
|
pdec1=pdec;
|
|
sik1=sik;
|
|
memcpy(iv, p, 8); /* recover the iv */
|
|
/* generate the key */
|
|
EVP_BytesToKey(cipher, EVP_sha1(), iv, oldpasswd.constUchar(),
|
|
oldpasswd.size(), 1, ckey, NULL);
|
|
/* we use sha1 as message digest,
|
|
* because an md5 version of the password is
|
|
* stored in the database...
|
|
*/
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
#else
|
|
ctx = &ctxbuf;
|
|
#endif
|
|
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
EVP_DecryptInit(ctx, cipher, ckey, iv);
|
|
EVP_DecryptUpdate(ctx, pdec , &outl, p + 8, size - 8);
|
|
decsize = outl;
|
|
EVP_DecryptFinal(ctx, pdec + decsize, &outl);
|
|
decsize += outl;
|
|
pki_openssl_error();
|
|
memcpy(sik, pdec, decsize);
|
|
if (getKeyType() == EVP_PKEY_RSA) {
|
|
rsakey=d2i_RSAPrivateKey(NULL,(const unsigned char **)&pdec, decsize);
|
|
if (pki_ign_openssl_error()) {
|
|
rsakey = d2i_RSA_PUBKEY(NULL, (const unsigned char **)&sik, decsize);
|
|
}
|
|
pki_openssl_error();
|
|
if (rsakey) EVP_PKEY_assign_RSA(key, rsakey);
|
|
}
|
|
OPENSSL_free(sik1);
|
|
OPENSSL_free(pdec1);
|
|
EVP_CIPHER_CTX_cleanup(ctx);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
#endif
|
|
pki_openssl_error();
|
|
encryptKey();
|
|
}
|
|
|
|
void pki_evp::oldFromData(unsigned char *p, int size )
|
|
{
|
|
int version, type;
|
|
|
|
QByteArray ba;
|
|
|
|
version = intFromData(ba);
|
|
if (version != 1) { // backward compatibility
|
|
veryOldFromData(p, size);
|
|
return;
|
|
}
|
|
if (key)
|
|
EVP_PKEY_free(key);
|
|
|
|
key = NULL;
|
|
type = intFromData(ba);
|
|
ownPass = intFromData(ba);
|
|
|
|
d2i_old(ba, type);
|
|
pki_openssl_error();
|
|
|
|
encKey = ba;
|
|
}
|
|
|