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 <rich@kde.org>
Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
This commit is contained in:
Andrew Knight 2014-08-12 12:21:57 +03:00
parent cec893e4f0
commit 8c864ac498
2 changed files with 148 additions and 9 deletions

View File

@ -42,41 +42,153 @@
#include "qsslkey.h"
#include "qsslkey_p.h"
#include <QtCore/qfunctions_winrt.h>
#include <wrl.h>
#include <windows.security.cryptography.h>
#include <windows.security.cryptography.core.h>
#include <windows.security.cryptography.certificates.h>
#include <windows.storage.streams.h>
#include <robuffer.h>
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<IAsymmetricKeyAlgorithmProviderStatics> keyProviderFactory;
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_AsymmetricKeyAlgorithmProvider).Get(),
&keyProviderFactory);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IAsymmetricAlgorithmNamesStatics> 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<ICryptographicBufferStatics> bufferFactory;
ComPtr<IAsymmetricKeyAlgorithmProvider> keyProvider;
};
Q_GLOBAL_STATIC(SslKeyGlobal, g)
// Use the opaque struct for key storage
struct EVP_PKEY {
ComPtr<ICryptographicKey> 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<IBuffer> 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<IBuffer> buffer;
hr = opaque->key->ExportDefaultPublicKeyBlobType(&buffer);
RETURN_IF_FAILED("Failed to export key", return result);
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
hr = buffer.As(&byteAccess);
Q_ASSERT_SUCCEEDED(hr);
char *data;
hr = byteAccess->Buffer(reinterpret_cast<byte **>(&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

View File

@ -47,6 +47,14 @@
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qnetworkproxy.h>
#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);