1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopcuakeypair_p.h"
5#include "openssl_symbols_p.h"
6#include "qopcuax509utils_p.h"
7
8QT_BEGIN_NAMESPACE
9
10QOpcUaKeyPairPrivate::QOpcUaKeyPairPrivate()
11 : QObjectPrivate()
12{
13 if (!q_resolveOpenSslSymbols())
14 qFatal(msg: "Failed to resolve symbols");
15
16 q_ERR_load_crypto_strings();
17}
18
19QOpcUaKeyPairPrivate::~QOpcUaKeyPairPrivate()
20{
21 if (m_keyData) {
22 q_EVP_PKEY_free(a: m_keyData);
23 m_keyData = nullptr;
24 }
25}
26
27static int passwordCallback(char *passphraseBuffer, int maximumPassphraseSize, int writeOperation, void *userData) {
28 QOpcUaKeyPair *source = reinterpret_cast<QOpcUaKeyPair*>(userData);
29 QString passphrase;
30 source->passphraseNeeded(passphrase, maximumLength: maximumPassphraseSize, writeOperation: writeOperation == 1);
31
32 if (passphrase.isEmpty())
33 return -1;
34
35 memcpy(dest: passphraseBuffer, src: passphrase.toUtf8().constData(), n: qMin(a: maximumPassphraseSize, b: passphrase.size()));
36 return passphrase.size();
37}
38
39bool QOpcUaKeyPairPrivate::loadFromPemData(const QByteArray &data) {
40 Q_Q(QOpcUaKeyPair);
41
42 if (m_keyData) {
43 q_EVP_PKEY_free(a: m_keyData);
44 m_keyData = nullptr;
45 }
46 m_hasPrivateKey = false;
47
48 BIO *bio = q_BIO_new_mem_buf(a: (void *)data.constData(), b: data.size());
49 if (!bio) {
50 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
51 return false;
52 }
53 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
54
55 if (data.startsWith(bv: "-----BEGIN PRIVATE KEY-----") || data.startsWith(bv: "-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
56 if (!q_PEM_read_bio_PrivateKey(a: bio, b: &m_keyData, c: &passwordCallback, d: q /* userData */)) {
57 qCWarning(lcSsl) << "Failed to load private key:" << getOpenSslError();
58 return false;
59 }
60 m_hasPrivateKey = true;
61 } else {
62 if (!q_PEM_read_bio_PUBKEY(a: bio, b: &m_keyData, NULL, NULL)) {
63 qCWarning(lcSsl) << "Failed to load public key:" << getOpenSslError();
64 return false;
65 }
66 }
67
68 return true;
69}
70
71QByteArray QOpcUaKeyPairPrivate::publicKeyToByteArray() const
72{
73 if (!m_keyData) {
74 qCWarning(lcSsl) << "No public key to write";
75 return QByteArray();
76 }
77
78 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
79 if (!bio) {
80 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
81 return QByteArray();
82 }
83 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
84
85 if (0 == q_PEM_write_bio_PUBKEY(a: bio, b: m_keyData)) {
86 qCWarning(lcSsl) << "Failed to write public key:" << getOpenSslError();
87 return QByteArray();
88 }
89
90 char *buf;
91 int length = q_BIO_get_mem_data(bio, &buf);
92 QByteArray data(buf, length);
93 return data;
94}
95
96bool QOpcUaKeyPairPrivate::generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength)
97{
98 if (m_keyData) {
99 q_EVP_PKEY_free(a: m_keyData);
100 m_keyData = nullptr;
101 }
102
103 EVP_PKEY_CTX *ctx = q_EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, e: nullptr);
104 if (!ctx) {
105 qCWarning(lcSsl) << "Failed to allocate context:" << getOpenSslError();
106 return false;
107 }
108 Deleter<EVP_PKEY_CTX> ctxDeleter(ctx, q_EVP_PKEY_CTX_free);
109
110 if (q_EVP_PKEY_keygen_init(ctx) <= 0) {
111 qCWarning(lcSsl) << "Failed to initialize context:" << getOpenSslError();
112 return false;
113 }
114
115 if (q_EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, static_cast<int>(strength)) <= 0) {
116 qCWarning(lcSsl) << "Failed to set context property:" << getOpenSslError();
117 return false;
118 }
119
120 if (q_EVP_PKEY_keygen(ctx, pkey: &m_keyData) <= 0) {
121 qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError();
122 return false;
123 }
124
125 m_hasPrivateKey = true;
126 return true;
127}
128
129QOpcUaKeyPair::KeyType QOpcUaKeyPairPrivate::keyType() const
130{
131 if (!m_keyData)
132 return QOpcUaKeyPair::KeyType::Empty;
133 switch (q_EVP_PKEY_base_id(pkey: m_keyData)) {
134 case EVP_PKEY_RSA:
135 return QOpcUaKeyPair::KeyType::Rsa;
136 default:
137 return QOpcUaKeyPair::KeyType::Unknown;
138 }
139}
140
141QByteArray QOpcUaKeyPairPrivate::privateKeyToByteArray(QOpcUaKeyPair::Cipher cipher, const QString &password) const
142{
143 if (!m_keyData) {
144 qCWarning(lcSsl) << "No private key to write";
145 return QByteArray();
146 }
147
148 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
149 if (!bio) {
150 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
151 return QByteArray();
152 }
153 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
154
155 const EVP_CIPHER *enc = NULL;
156 if (cipher == QOpcUaKeyPair::Cipher::Unencrypted)
157 enc = NULL;
158 else if (cipher == QOpcUaKeyPair::Cipher::Aes128Cbc)
159 enc = q_EVP_aes_128_cbc();
160 else {
161 qCWarning(lcSsl) << "Unknown cipher given";
162 return QByteArray();
163 }
164
165 if (0 == q_PEM_write_bio_PKCS8PrivateKey(a: bio, b: m_keyData, c: enc,
166 d: enc ? password.toUtf8().data() : NULL,
167 e: enc ? password.size() : 0,
168 NULL /* callback */, NULL /* userdata */)) {
169 qCWarning(lcSsl) << "Failed to write private key:" << getOpenSslError();
170 return QByteArray();
171 }
172
173 char *buf;
174 int length = q_BIO_get_mem_data(bio, &buf);
175 QByteArray data(buf, length);
176 return data;
177}
178
179bool QOpcUaKeyPairPrivate::hasPrivateKey() const
180{
181 if (!m_keyData)
182 return false;
183
184 return m_hasPrivateKey;
185}
186
187QT_END_NAMESPACE
188

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtopcua/src/opcua/x509/qopcuakeypair_openssl.cpp