summaryrefslogtreecommitdiffstats
path: root/src/opcua/x509/qopcuakeypair_openssl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opcua/x509/qopcuakeypair_openssl.cpp')
-rw-r--r--src/opcua/x509/qopcuakeypair_openssl.cpp264
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