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#if OPENSSL_VERSION_NUMBER < 0x10100000L
18 q_OPENSSL_add_all_algorithms_noconf();
19#endif
20}
21
22QOpcUaKeyPairPrivate::~QOpcUaKeyPairPrivate()
23{
24 if (m_keyData) {
25 q_EVP_PKEY_free(a: m_keyData);
26 m_keyData = nullptr;
27 }
28}
29
30static int passwordCallback(char *passphraseBuffer, int maximumPassphraseSize, int writeOperation, void *userData) {
31 QOpcUaKeyPair *source = reinterpret_cast<QOpcUaKeyPair*>(userData);
32 QString passphrase;
33 source->passphraseNeeded(passphrase, maximumLength: maximumPassphraseSize, writeOperation: writeOperation == 1);
34
35 if (passphrase.isEmpty())
36 return -1;
37
38 memcpy(dest: passphraseBuffer, src: passphrase.toUtf8().constData(), n: qMin(a: maximumPassphraseSize, b: passphrase.size()));
39 return passphrase.size();
40}
41
42bool QOpcUaKeyPairPrivate::loadFromPemData(const QByteArray &data) {
43 Q_Q(QOpcUaKeyPair);
44
45 if (m_keyData) {
46 q_EVP_PKEY_free(a: m_keyData);
47 m_keyData = nullptr;
48 }
49 m_hasPrivateKey = false;
50
51 BIO *bio = q_BIO_new_mem_buf(a: (void *)data.constData(), b: data.size());
52 if (!bio) {
53 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
54 return false;
55 }
56 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
57
58 if (data.startsWith(bv: "-----BEGIN PRIVATE KEY-----") || data.startsWith(bv: "-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
59 if (!q_PEM_read_bio_PrivateKey(a: bio, b: &m_keyData, c: &passwordCallback, d: q /* userData */)) {
60 qCWarning(lcSsl) << "Failed to load private key:" << getOpenSslError();
61 return false;
62 }
63 m_hasPrivateKey = true;
64 } else {
65 if (!q_PEM_read_bio_PUBKEY(a: bio, b: &m_keyData, NULL, NULL)) {
66 qCWarning(lcSsl) << "Failed to load public key:" << getOpenSslError();
67 return false;
68 }
69 }
70
71 return true;
72}
73
74QByteArray QOpcUaKeyPairPrivate::publicKeyToByteArray() const
75{
76 if (!m_keyData) {
77 qCWarning(lcSsl) << "No public key to write";
78 return QByteArray();
79 }
80
81 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
82 if (!bio) {
83 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
84 return QByteArray();
85 }
86 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
87
88 if (0 == q_PEM_write_bio_PUBKEY(a: bio, b: m_keyData)) {
89 qCWarning(lcSsl) << "Failed to write public key:" << getOpenSslError();
90 return QByteArray();
91 }
92
93 char *buf;
94 int length = q_BIO_get_mem_data(bio, &buf);
95 QByteArray data(buf, length);
96 return data;
97}
98
99bool QOpcUaKeyPairPrivate::generateRsaKey(QOpcUaKeyPair::RsaKeyStrength strength)
100{
101 if (m_keyData) {
102 q_EVP_PKEY_free(a: m_keyData);
103 m_keyData = nullptr;
104 }
105
106#if OPENSSL_VERSION_NUMBER >= 0x10100000L
107 EVP_PKEY_CTX *ctx = q_EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, e: nullptr);
108 if (!ctx) {
109 qCWarning(lcSsl) << "Failed to allocate context:" << getOpenSslError();
110 return false;
111 }
112 Deleter<EVP_PKEY_CTX> ctxDeleter(ctx, q_EVP_PKEY_CTX_free);
113
114 if (q_EVP_PKEY_keygen_init(ctx) <= 0) {
115 qCWarning(lcSsl) << "Failed to initialize context:" << getOpenSslError();
116 return false;
117 }
118
119 if (q_EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, static_cast<int>(strength)) <= 0) {
120 qCWarning(lcSsl) << "Failed to set context property:" << getOpenSslError();
121 return false;
122 }
123
124 if (q_EVP_PKEY_keygen(ctx, pkey: &m_keyData) <= 0) {
125 qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError();
126 return false;
127 }
128
129#else
130 RSA *rsa;
131 BIGNUM *publicExponent;
132
133 publicExponent = q_BN_new();
134 if (publicExponent == NULL) {
135 qCWarning(lcSsl) << "Failed to allocate public exponent:" << getOpenSslError();
136 return false;
137 }
138 Deleter<BIGNUM> publicExponentDeleter(publicExponent, q_BN_free);
139
140 if (q_BN_set_word(publicExponent, RSA_F4) == 0) {
141 qCWarning(lcSsl) << "Failed to set public exponent:" << getOpenSslError();
142 return false;
143 }
144
145 rsa = q_RSA_new();
146 if (rsa == NULL) {
147 qCWarning(lcSsl) << "Failed to allocate RSA:" << getOpenSslError();
148 return false;
149 }
150 Deleter<RSA> rsaDeleter(rsa, q_RSA_free);
151
152 int result = q_RSA_generate_key_ex(rsa, static_cast<int>(strength), publicExponent, nullptr /* progress callback */);
153 if (result == 0) {
154 qCWarning(lcSsl) << "Failed to generate key:" << getOpenSslError();
155 return false;
156 }
157
158 m_keyData = q_EVP_PKEY_new();
159 if (!m_keyData) {
160 qCWarning(lcSsl) << "Failed to allocate key data:" << getOpenSslError();
161 return false;
162 }
163
164 if (!q_EVP_PKEY_set1_RSA(m_keyData, rsa)) {
165 qCWarning(lcSsl) << "Failed to transfer key data:" << getOpenSslError();
166 return false;
167 }
168#endif
169 m_hasPrivateKey = true;
170 return true;
171}
172
173QOpcUaKeyPair::KeyType QOpcUaKeyPairPrivate::keyType() const
174{
175 if (!m_keyData)
176 return QOpcUaKeyPair::KeyType::Empty;
177 switch (q_EVP_PKEY_base_id(a: m_keyData)) {
178 case EVP_PKEY_RSA:
179 return QOpcUaKeyPair::KeyType::Rsa;
180 default:
181 return QOpcUaKeyPair::KeyType::Unknown;
182 }
183}
184
185QByteArray QOpcUaKeyPairPrivate::privateKeyToByteArray(QOpcUaKeyPair::Cipher cipher, const QString &password) const
186{
187 if (!m_keyData) {
188 qCWarning(lcSsl) << "No private key to write";
189 return QByteArray();
190 }
191
192 BIO *bio = q_BIO_new(a: q_BIO_s_mem());
193 if (!bio) {
194 qCWarning(lcSsl) << "Failed to allocate a buffer:" << getOpenSslError();
195 return QByteArray();
196 }
197 Deleter<BIO> bioDeleter(bio, q_BIO_free_all);
198
199 const EVP_CIPHER *enc = NULL;
200 if (cipher == QOpcUaKeyPair::Cipher::Unencrypted)
201 enc = NULL;
202 else if (cipher == QOpcUaKeyPair::Cipher::Aes128Cbc)
203 enc = q_EVP_aes_128_cbc();
204 else {
205 qCWarning(lcSsl) << "Unknown cipher given";
206 return QByteArray();
207 }
208
209 if (0 == q_PEM_write_bio_PKCS8PrivateKey(a: bio, b: m_keyData, c: enc,
210 d: enc ? password.toUtf8().data() : NULL,
211 e: enc ? password.size() : 0,
212 NULL /* callback */, NULL /* userdata */)) {
213 qCWarning(lcSsl) << "Failed to write private key:" << getOpenSslError();
214 return QByteArray();
215 }
216
217 char *buf;
218 int length = q_BIO_get_mem_data(bio, &buf);
219 QByteArray data(buf, length);
220 return data;
221}
222
223bool QOpcUaKeyPairPrivate::hasPrivateKey() const
224{
225 if (!m_keyData)
226 return false;
227
228 return m_hasPrivateKey;
229}
230
231QT_END_NAMESPACE
232

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