Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qx509_schannel.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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// Qt-Security score:significant reason:default
4
8
9#include <QtCore/private/qsystemerror_p.h>
10#include <QtNetwork/private/qsslcertificate_p.h>
11
12#include <memory>
13
15
16namespace QTlsPrivate {
17
19
21
23{
24 auto key = std::make_unique<TlsKeySchannel>(QSsl::PublicKey);
25 if (publicKeyAlgorithm != QSsl::Opaque)
26 key->decodeDer(QSsl::PublicKey, publicKeyAlgorithm, publicKeyDerData, {}, false);
27
28 return key.release();
29}
30
32{
33 return Qt::HANDLE(certificateContext.get());
34}
35
36QSslCertificate X509CertificateSchannel::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
37{
38 QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
39 certificateContext->cbCertEncoded);
40 QSslCertificate certificate(derData, QSsl::Der);
41 if (!certificate.isNull()) {
42 auto *certBackend = QTlsBackend::backend<X509CertificateSchannel>(certificate);
43 Q_ASSERT(certBackend);
44 certBackend->certificateContext.reset(CertDuplicateCertificateContext(certificateContext));
45 }
46 return certificate;
47}
48
49bool X509CertificateSchannel::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
50 QList<QSslCertificate> *caCertificates,
51 const QByteArray &passPhrase)
52{
53 // These are required
54 Q_ASSERT(device);
55 Q_ASSERT(key);
56 Q_ASSERT(cert);
57
58 QByteArray pkcs12data = device->readAll();
59 if (pkcs12data.size() == 0)
60 return false;
61
62 CRYPT_DATA_BLOB dataBlob;
63 dataBlob.cbData = pkcs12data.size();
64 dataBlob.pbData = reinterpret_cast<BYTE*>(pkcs12data.data());
65
66 const auto password = QString::fromUtf8(passPhrase);
67
68 const DWORD flags = (CRYPT_EXPORTABLE | PKCS12_NO_PERSIST_KEY | PKCS12_ALWAYS_CNG_KSP);
69
70 auto certStore = QHCertStorePointer(PFXImportCertStore(&dataBlob,
71 reinterpret_cast<LPCWSTR>(password.utf16()),
72 flags));
73
74 if (!certStore) {
75 qCWarning(lcTlsBackendSchannel, "Failed to import PFX data: %s",
76 qPrintable(QSystemError::windowsString()));
77 return false;
78 }
79
80 // first extract the certificate with the private key
81 const auto certContext = QPCCertContextPointer(CertFindCertificateInStore(certStore.get(),
82 X509_ASN_ENCODING |
83 PKCS_7_ASN_ENCODING,
84 0,
85 CERT_FIND_HAS_PRIVATE_KEY,
86 nullptr, nullptr));
87
88 if (!certContext) {
89 qCWarning(lcTlsBackendSchannel, "Failed to find certificate in PFX store: %s",
90 qPrintable(QSystemError::windowsString()));
91 return false;
92 }
93
94 *cert = QSslCertificate_from_CERT_CONTEXT(certContext.get());
95
96 // retrieve the private key for the certificate
97 NCRYPT_KEY_HANDLE keyHandle = {};
98 DWORD keyHandleSize = sizeof(keyHandle);
99 if (!CertGetCertificateContextProperty(certContext.get(), CERT_NCRYPT_KEY_HANDLE_PROP_ID,
100 &keyHandle, &keyHandleSize)) {
101 qCWarning(lcTlsBackendSchannel, "Failed to find private key handle in certificate context: %s",
102 qPrintable(QSystemError::windowsString()));
103 return false;
104 }
105
106 SECURITY_STATUS securityStatus = ERROR_SUCCESS;
107
108 // we need the 'NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG' to make NCryptExportKey succeed
109 DWORD policy = (NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG);
110 DWORD policySize = sizeof(policy);
111
112 securityStatus = NCryptSetProperty(keyHandle, NCRYPT_EXPORT_POLICY_PROPERTY,
113 reinterpret_cast<BYTE*>(&policy), policySize, 0);
114 if (securityStatus != ERROR_SUCCESS) {
115 qCWarning(lcTlsBackendSchannel, "Failed to update export policy of private key: 0x%x",
116 static_cast<unsigned int>(securityStatus));
117 return false;
118 }
119
120 DWORD blobSize = {};
121 securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB,
122 nullptr, nullptr, 0, &blobSize, 0);
123 if (securityStatus != ERROR_SUCCESS) {
124 qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key size: 0x%x",
125 static_cast<unsigned int>(securityStatus));
126 return false;
127 }
128
129 std::vector<BYTE> blob(blobSize);
130 securityStatus = NCryptExportKey(keyHandle, {}, BCRYPT_RSAFULLPRIVATE_BLOB,
131 nullptr, blob.data(), blobSize, &blobSize, 0);
132 if (securityStatus != ERROR_SUCCESS) {
133 qCWarning(lcTlsBackendSchannel, "Failed to retrieve private key from certificate: 0x%x",
134 static_cast<unsigned int>(securityStatus));
135 return false;
136 }
137
138 DWORD privateKeySize = {};
139
140 if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CNG_RSA_PRIVATE_KEY_BLOB,
141 blob.data(), nullptr, &privateKeySize)) {
142 qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s",
143 qPrintable(QSystemError::windowsString()));
144 return false;
145 }
146
147 std::vector<BYTE> privateKeyData(privateKeySize);
148
149 CRYPT_PRIVATE_KEY_INFO privateKeyInfo = {};
150 privateKeyInfo.Algorithm.pszObjId = const_cast<PSTR>(szOID_RSA_RSA);
151 privateKeyInfo.PrivateKey.cbData = privateKeySize;
152 privateKeyInfo.PrivateKey.pbData = privateKeyData.data();
153
154 if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
155 CNG_RSA_PRIVATE_KEY_BLOB, blob.data(),
156 privateKeyInfo.PrivateKey.pbData, &privateKeyInfo.PrivateKey.cbData)) {
157 qCWarning(lcTlsBackendSchannel, "Failed to encode private key to key info: %s",
158 qPrintable(QSystemError::windowsString()));
159 return false;
160 }
161
162
163 DWORD derSize = {};
164
165 if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
166 &privateKeyInfo, nullptr, &derSize)) {
167 qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s",
168 qPrintable(QSystemError::windowsString()));
169
170 return false;
171 }
172
173 QByteArray derData(derSize, Qt::Uninitialized);
174
175 if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
176 &privateKeyInfo, reinterpret_cast<BYTE*>(derData.data()), &derSize)) {
177 qCWarning(lcTlsBackendSchannel, "Failed to encode key info to DER format: %s",
178 qPrintable(QSystemError::windowsString()));
179
180 return false;
181 }
182
183 *key = QSslKey(derData, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey);
184 if (key->isNull()) {
185 qCWarning(lcTlsBackendSchannel, "Failed to parse private key from DER format");
186 return false;
187 }
188
189 // fetch all the remaining certificates as CA certificates
190 if (caCertificates) {
191 PCCERT_CONTEXT caCertContext = nullptr;
192 while ((caCertContext = CertFindCertificateInStore(certStore.get(),
193 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
194 0, CERT_FIND_ANY, nullptr, caCertContext))) {
195 if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
196 certContext->pCertInfo, caCertContext->pCertInfo))
197 continue; // ignore the certificate with private key
198
199 auto caCertificate = QSslCertificate_from_CERT_CONTEXT(caCertContext);
200
201 caCertificates->append(caCertificate);
202 }
203 }
204
205 return true;
206}
207
208} // namespace QTlsPrivate
209
210QT_END_NAMESPACE
TlsKey * publicKey() const override
Qt::HANDLE handle() const override
Q_DISABLE_COPY_MOVE(X509CertificateSchannel)
Namespace containing onternal types that TLS backends implement.
std::unique_ptr< void, QHCertStoreDeleter > QHCertStorePointer
Definition qwincrypt_p.h:42