diff options
Diffstat (limited to 'src/opcua/x509/qopcuakeypair_openssl.cpp')
-rw-r--r-- | src/opcua/x509/qopcuakeypair_openssl.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/opcua/x509/qopcuakeypair_openssl.cpp b/src/opcua/x509/qopcuakeypair_openssl.cpp new file mode 100644 index 0000000..66a7b7e --- /dev/null +++ b/src/opcua/x509/qopcuakeypair_openssl.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt OPC UA module. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopcuakeypair_p.h" +#include "openssl_symbols_p.h" +#include "qopcuax509utils_p.h" + +QT_BEGIN_NAMESPACE + +QOpcUaKeyPairPrivate::QOpcUaKeyPairPrivate() + : QObjectPrivate() +{ + if (!q_resolveOpenSslSymbols()) + qFatal("Failed to resolve symbols"); + + q_ERR_load_crypto_strings(); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + q_OPENSSL_add_all_algorithms_noconf(); +#endif +} + +QOpcUaKeyPairPrivate::~QOpcUaKeyPairPrivate() +{ + if (m_keyData) { + q_EVP_PKEY_free(m_keyData); + m_keyData = nullptr; + } +} + +static int passwordCallback(char *passphraseBuffer, int maximumPassphraseSize, int writeOperation, void *userData) { + QOpcUaKeyPair *source = reinterpret_cast<QOpcUaKeyPair*>(userData); + QString passphrase; + source->passphraseNeeded(passphrase, maximumPassphraseSize, writeOperation == 1); + + if (passphrase.isEmpty()) + return -1; + + memcpy(passphraseBuffer, passphrase.toUtf8().constData(), qMin(maximumPassphraseSize, passphrase.size())); + return passphrase.size(); +} + +bool QOpcUaKeyPairPrivate::loadFromPemData(const QByteArray &data) { + Q_Q(QOpcUaKeyPair); + + if (m_keyData) { + q_EVP_PKEY_free(m_keyData); + m_keyData = nullptr; + } + m_hasPrivateKey = false; + + BIO *bio = q_BIO_new_mem_buf((void *)data.constData(), data.size()); + if (!bio) { + qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError(); + return false; + } + Deleter<BIO> bioDeleter(bio, q_BIO_free_all); + + if (data.startsWith("-----BEGIN PRIVATE KEY-----") || data.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----")) { + if (!q_PEM_read_bio_PrivateKey(bio, &m_keyData, &passwordCallback, q /* userData */)) { + qCWarning(lcSsl) << "Failed to load private key:" << getOpenSslError(); + return false; + } + m_hasPrivateKey = true; + } else { + if (!q_PEM_read_bio_PUBKEY(bio, &m_keyData, NULL, NULL)) { + qCWarning(lcSsl) << "Failed to load public key:" << getOpenSslError(); + return false; + } + } + + return true; +} + +QByteArray QOpcUaKeyPairPrivate::publicKeyToByteArray() const +{ + if (!m_keyData) { + qCWarning(lcSsl) << "No public key to write"; + return QByteArray(); + } + + BIO *bio = q_BIO_new(q_BIO_s_mem()); + if (!bio) { + qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError(); + return QByteArray(); + } + Deleter<BIO> bioDeleter(bio, q_BIO_free_all); + + if (0 == q_PEM_write_bio_PUBKEY(bio, m_keyData)) { + qCWarning(lcSsl) << "Failed to write public key:" << getOpenSslError(); + return QByteArray(); + } + + char *buf; + int length = q_BIO_get_mem_data(bio, &buf); + QByteArray data(buf, length); + return data; +} + +bool QOpcUaKeyPairPrivate::generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength) +{ + if (m_keyData) { + q_EVP_PKEY_free(m_keyData); + m_keyData = nullptr; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_PKEY_CTX *ctx = q_EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); + if (!ctx) { + qCWarning(lcSsl) << "Failed to allocate context:" << getOpenSslError(); + return false; + } + Deleter<EVP_PKEY_CTX> ctxDeleter(ctx, q_EVP_PKEY_CTX_free); + + if (q_EVP_PKEY_keygen_init(ctx) <= 0) { + qCWarning(lcSsl) << "Failed to initialize context:" << getOpenSslError(); + return false; + } + + if (q_EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, static_cast<int>(strength)) <= 0) { + qCWarning(lcSsl) << "Failed to set context property:" << getOpenSslError(); + return false; + } + + if (q_EVP_PKEY_keygen(ctx, &m_keyData) <= 0) { + qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError(); + return false; + } + +#else + RSA *rsa; + BIGNUM *publicExponent; + + publicExponent = q_BN_new(); + if (publicExponent == NULL) { + qCWarning(lcSsl) << "Failed to allocate public exponent:" << getOpenSslError(); + return false; + } + Deleter<BIGNUM> publicExponentDeleter(publicExponent, q_BN_free); + + if (q_BN_set_word(publicExponent, RSA_F4) == 0) { + qCWarning(lcSsl) << "Failed to set public exponent:" << getOpenSslError(); + return false; + } + + rsa = q_RSA_new(); + if (rsa == NULL) { + qCWarning(lcSsl) << "Failed to allocate RSA:" << getOpenSslError(); + return false; + } + Deleter<RSA> rsaDeleter(rsa, q_RSA_free); + + int result = q_RSA_generate_key_ex(rsa, static_cast<int>(strength), publicExponent, nullptr /* progress callback */); + if (result == 0) { + qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError(); + return false; + } + + m_keyData = q_EVP_PKEY_new(); + if (!m_keyData) { + qCWarning(lcSsl) << "Failed to allocate key data:" << getOpenSslError(); + return false; + } + + if (!q_EVP_PKEY_set1_RSA(m_keyData, rsa)) { + qCWarning(lcSsl) << "Failed to transfer key data:" << getOpenSslError(); + return false; + } +#endif + m_hasPrivateKey = true; + return true; +} + +QOpcUaKeyPair::KeyType QOpcUaKeyPairPrivate::keyType() const +{ + if (!m_keyData) + return QOpcUaKeyPair::KeyType::Empty; + switch (q_EVP_PKEY_base_id(m_keyData)) { + case EVP_PKEY_RSA: + return QOpcUaKeyPair::KeyType::Rsa; + default: + return QOpcUaKeyPair::KeyType::Unknown; + } +} + +QByteArray QOpcUaKeyPairPrivate::privateKeyToByteArray(QOpcUaKeyPair::Cipher cipher, const QString &password) const +{ + if (!m_keyData) { + qCWarning(lcSsl) << "No private key to write"; + return QByteArray(); + } + + BIO *bio = q_BIO_new(q_BIO_s_mem()); + if (!bio) { + qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError(); + return QByteArray(); + } + Deleter<BIO> bioDeleter(bio, q_BIO_free_all); + + const EVP_CIPHER *enc = NULL; + if (cipher == QOpcUaKeyPair::Cipher::Unencrypted) + enc = NULL; + else if (cipher == QOpcUaKeyPair::Cipher::Aes128Cbc) + enc = q_EVP_aes_128_cbc(); + else { + qCWarning(lcSsl) << "Unknown cipher given"; + return QByteArray(); + } + + if (0 == q_PEM_write_bio_PKCS8PrivateKey(bio, m_keyData, enc, + enc ? (unsigned char*)password.toUtf8().constData() : NULL, + enc ? password.length() : 0, + NULL /* callback */, NULL /* userdata */)) { + qCWarning(lcSsl) << "Failed to write private key:" << getOpenSslError(); + return QByteArray(); + } + + char *buf; + int length = q_BIO_get_mem_data(bio, &buf); + QByteArray data(buf, length); + return data; +} + +bool QOpcUaKeyPairPrivate::hasPrivateKey() const +{ + if (!m_keyData) + return false; + + return m_hasPrivateKey; +} + +QT_END_NAMESPACE |