From 8c864ac49861b397a6d3e8b4a93c39b1ed373644 Mon Sep 17 00:00:00 2001 From: Andrew Knight Date: Tue, 12 Aug 2014 12:21:57 +0300 Subject: winrt: Add partial SSL key support This allows for opening of public key files. It does not, however, support opening private keys (or decrypting/encrypting them). This is due to limitations in the native API. Nearly all public key tests pass (the native API doesn't support the 40-bit key in the test set). The private key tests are expected to fail. Task-number: QTBUG-40688 Change-Id: Id8f2f1ae6526540736ceb2e5371f6a5d80c4ba7b Reviewed-by: Richard J. Moore Reviewed-by: Oliver Wolff --- src/network/ssl/qsslkey_winrt.cpp | 130 +++++++++++++++++++++++-- tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp | 27 +++++ 2 files changed, 148 insertions(+), 9 deletions(-) diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp index 1e416a8c09..1c625081a4 100644 --- a/src/network/ssl/qsslkey_winrt.cpp +++ b/src/network/ssl/qsslkey_winrt.cpp @@ -42,41 +42,153 @@ #include "qsslkey.h" #include "qsslkey_p.h" +#include + +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Security::Cryptography; +using namespace ABI::Windows::Security::Cryptography::Certificates; +using namespace ABI::Windows::Security::Cryptography::Core; +using namespace ABI::Windows::Storage::Streams; + QT_BEGIN_NAMESPACE +struct SslKeyGlobal +{ + SslKeyGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr keyProviderFactory; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_AsymmetricKeyAlgorithmProvider).Get(), + &keyProviderFactory); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr algorithmNames; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_AsymmetricAlgorithmNames).Get(), + &algorithmNames); + Q_ASSERT_SUCCEEDED(hr); + + HString algorithmName; + // The algorithm name doesn't matter for imports, so just use PKCS1 + hr = algorithmNames->get_RsaPkcs1(algorithmName.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + hr = keyProviderFactory->OpenAlgorithm(algorithmName.Get(), &keyProvider); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr bufferFactory; + ComPtr keyProvider; +}; +Q_GLOBAL_STATIC(SslKeyGlobal, g) + +// Use the opaque struct for key storage +struct EVP_PKEY { + ComPtr key; +}; + void QSslKeyPrivate::clear(bool deep) { - Q_UNIMPLEMENTED(); + isNull = true; + + if (opaque) { + if (deep) { + delete opaque; + opaque = 0; + } else { + opaque->key.Reset(); + } + } } void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear) { - Q_UNIMPLEMENTED(); + Q_UNUSED(passPhrase); + + clear(deepClear); + + if (der.isEmpty()) + return; + + if (type != QSsl::PublicKey) { + qWarning("The WinRT SSL backend does not support importing private keys."); + return; + } + + HRESULT hr; + ComPtr buffer; + hr = g->bufferFactory->CreateFromByteArray(der.length(), (BYTE *)der.data(), &buffer); + Q_ASSERT_SUCCEEDED(hr); + + if (!opaque) + opaque = new EVP_PKEY; + + hr = g->keyProvider->ImportDefaultPublicKeyBlob(buffer.Get(), &opaque->key); + RETURN_VOID_IF_FAILED("Failed to import public key"); + + isNull = false; } void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) { - Q_UNIMPLEMENTED(); + decodeDer(derFromPem(pem), passPhrase, deepClear); } int QSslKeyPrivate::length() const { - Q_UNIMPLEMENTED(); - return -1; + if (isNull) + return -1; + + Q_ASSERT(opaque && opaque->key); + HRESULT hr; + UINT32 keySize; + hr = opaque->key->get_KeySize(&keySize); + Q_ASSERT_SUCCEEDED(hr); + return keySize; } QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const { - Q_UNIMPLEMENTED(); - return QByteArray(); + Q_UNUSED(passPhrase); + QByteArray result; + if (isNull) + return result; + + Q_ASSERT(opaque && opaque->key); + HRESULT hr; + ComPtr buffer; + hr = opaque->key->ExportDefaultPublicKeyBlobType(&buffer); + RETURN_IF_FAILED("Failed to export key", return result); + + ComPtr byteAccess; + hr = buffer.As(&byteAccess); + Q_ASSERT_SUCCEEDED(hr); + char *data; + hr = byteAccess->Buffer(reinterpret_cast(&data)); + Q_ASSERT_SUCCEEDED(hr); + UINT32 size; + hr = buffer->get_Length(&size); + Q_ASSERT_SUCCEEDED(hr); + result = pemFromDer(QByteArray::fromRawData(data, size)); + return result; } Qt::HANDLE QSslKeyPrivate::handle() const { - Q_UNIMPLEMENTED(); - return 0; + return opaque ? opaque->key.Get() : 0; } QT_END_NAMESPACE diff --git a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp index e735e463ee..f7b9bcba62 100644 --- a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp +++ b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp @@ -47,6 +47,14 @@ #include #include +#ifdef Q_OS_WINRT +#define WINRT_EXPECT_FAILURES \ + if (type == QSsl::PrivateKey) \ + QEXPECT_FAIL("", "No support for private keys on WinRT: QTBUG-40688", Abort); \ + if (strstr(QTest::currentDataTag(), "rsa-pub-40")) \ + QEXPECT_FAIL("", "Weak public keys are not supported on WinRT", Abort); +#endif + class tst_QSslKey : public QObject { Q_OBJECT @@ -171,6 +179,10 @@ void tst_QSslKey::constructor() QFETCH(QSsl::KeyType, type); QFETCH(QSsl::EncodingFormat, format); +#ifdef Q_OS_WINRT + WINRT_EXPECT_FAILURES +#endif + QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -232,6 +244,10 @@ void tst_QSslKey::length() QFETCH(int, length); QFETCH(QSsl::EncodingFormat, format); +#ifdef Q_OS_WINRT + WINRT_EXPECT_FAILURES +#endif + QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -253,6 +269,10 @@ void tst_QSslKey::toPemOrDer() QFETCH(QSsl::KeyType, type); QFETCH(QSsl::EncodingFormat, format); +#ifdef Q_OS_WINRT + WINRT_EXPECT_FAILURES +#endif + QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -297,6 +317,10 @@ void tst_QSslKey::toEncryptedPemOrDer() QFETCH(QSsl::EncodingFormat, format); QFETCH(QString, password); +#ifdef Q_OS_WINRT + WINRT_EXPECT_FAILURES +#endif + QByteArray plain = readFile(absFilePath); QSslKey key(plain, algorithm, format, type); QVERIFY(!key.isNull()); @@ -374,6 +398,9 @@ void tst_QSslKey::passphraseChecks() QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key } +#ifdef Q_OS_WINRT + QEXPECT_FAIL("", "The WinRT backend does not support private key imports: QTBUG-40688", Abort); +#endif { if (!keyFile.isOpen()) keyFile.open(QIODevice::ReadOnly); -- cgit v1.2.3