1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-24 08:12:53 +01:00

Implement PEM writing for CNG implementation

This commit is contained in:
Ted John
2018-05-26 18:56:18 +01:00
parent 9467438c1c
commit b8d37548ed
5 changed files with 139 additions and 9 deletions

View File

@@ -24,11 +24,13 @@
#include "../platform/Platform2.h"
#include "IStream.hpp"
#include <stdexcept>
#include <sstream>
#include <string>
#include <tuple>
// CNG: Cryptography API: Next Generation (CNG)
// available in Windows Vista onwards.
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
@@ -244,6 +246,45 @@ public:
}
};
class DerWriter
{
private:
std::vector<uint8_t> _buffer;
public:
void WriteSequenceHeader()
{
_buffer.push_back(0x30);
_buffer.push_back(0x81);
_buffer.push_back(0x89);
}
void WriteInteger(const std::vector<uint8_t>& data)
{
if (data.size() < 128)
{
_buffer.push_back((uint8_t)data.size());
}
else if (data.size() <= std::numeric_limits<uint8_t>().max())
{
_buffer.push_back(0b10000001);
_buffer.push_back((uint8_t)data.size());
}
else if (data.size() <= std::numeric_limits<uint16_t>().max())
{
_buffer.push_back(0b10000010);
_buffer.push_back((data.size() >> 8) & 0xFF);
_buffer.push_back(data.size() & 0xFF);
}
_buffer.insert(_buffer.end(), data.begin(), data.end());
}
std::vector<uint8_t>&& Complete()
{
return std::move(_buffer);
}
};
class CngRsaKey final : public RsaKey
{
private:
@@ -258,6 +299,11 @@ private:
public:
NCRYPT_KEY_HANDLE GetKeyHandle() const { return _hKey; }
~CngRsaKey()
{
NCryptFreeObject(_hKey);
}
void SetPrivate(const std::string_view& pem) override
{
auto der = ReadPEM(pem, SZ_PRIVATE_BEGIN_TOKEN, SZ_PRIVATE_END_TOKEN);
@@ -284,9 +330,27 @@ public:
_hKey = ImportKey(params);
}
std::string GetPrivate() override { return GetKey(true); }
std::string GetPrivate() override
{
return "";
}
std::string GetPublic() override { return GetKey(false); }
std::string GetPublic() override
{
auto params = ExportKey(true);
DerWriter derWriter;
derWriter.WriteSequenceHeader();
derWriter.WriteInteger(params.Modulus);
derWriter.WriteInteger(params.Exponent);
auto derBytes = derWriter.Complete();
auto b64 = EncodeBase64(derBytes);
std::ostringstream sb;
sb << std::string(SZ_PUBLIC_BEGIN_TOKEN) << std::endl;
sb << b64 << std::endl;
sb << std::string(SZ_PUBLIC_END_TOKEN) << std::endl;
return sb.str();
}
private:
static constexpr std::string_view SZ_PUBLIC_BEGIN_TOKEN = "-----BEGIN RSA PUBLIC KEY-----";
@@ -296,11 +360,6 @@ private:
NCRYPT_KEY_HANDLE _hKey{};
std::string GetKey(bool isPrivate)
{
throw std::runtime_error("Not implemented");
}
static std::vector<uint8_t> ReadPEM(const std::string_view& pem, const std::string_view& beginToken, const std::string_view& endToken)
{
auto beginPos = pem.find(beginToken);
@@ -335,6 +394,21 @@ private:
return input;
}
static std::string EncodeBase64(const std::vector<uint8_t>& input)
{
DWORD chString;
if (!CryptBinaryToStringA(input.data(), (DWORD)input.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &chString))
{
throw std::runtime_error("CryptBinaryToStringA failed");
}
std::string result(chString, 0);
if (!CryptBinaryToStringA(input.data(), (DWORD)input.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, result.data(), &chString))
{
throw std::runtime_error("CryptBinaryToStringA failed");
}
return result;
}
static std::vector<uint8_t> DecodeBase64(const std::string_view& input)
{
DWORD cbBinary;
@@ -379,6 +453,38 @@ private:
CngThrowOnBadStatus("NCryptImportKey", status);
return hKey;
}
RsaKeyParams ExportKey(bool onlyPublic)
{
auto blobType = onlyPublic ? BCRYPT_RSAPUBLIC_BLOB : BCRYPT_RSAPRIVATE_BLOB;
std::vector<uint8_t> output;
NCRYPT_PROV_HANDLE hProv{};
try
{
auto status = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0);
CngThrowOnBadStatus("NCryptOpenStorageProvider", status);
DWORD cbOutput{};
status = NCryptExportKey(hProv, _hKey, blobType, NULL, NULL, 0, &cbOutput, 0);
CngThrowOnBadStatus("NCryptExportKey", status);
output = std::vector<uint8_t>(cbOutput);
status = NCryptExportKey(hProv, _hKey, blobType, NULL, output.data(), cbOutput, NULL, 0);
CngThrowOnBadStatus("NCryptExportKey", status);
NCryptFreeObject(hProv);
}
catch (const std::exception&)
{
NCryptFreeObject(hProv);
}
RsaKeyParams params;
const auto& header = *((BCRYPT_RSAKEY_BLOB*)output.data());
size_t i = sizeof(BCRYPT_RSAKEY_BLOB);
params.Modulus.insert(params.Modulus.end(), output.begin() + i, output.begin() + i + header.cbModulus);
i += header.cbModulus;
params.Exponent.insert(params.Exponent.end(), output.begin() + i, output.begin() + i + header.cbPublicExp);
return params;
}
};
class CngRsaAlgorithm final : public RsaAlgorithm

View File

@@ -112,6 +112,11 @@ class OpenSSLRsaKey final : public RsaKey
public:
EVP_PKEY * GetEvpKey() const { return _evpKey; }
~OpenSSLRsaKey()
{
EVP_PKEY_free(_evpKey);
}
void SetPrivate(const std::string_view& pem) override
{
SetKey(pem, true);

View File

@@ -56,10 +56,10 @@ namespace File
std::vector<uint8> result;
#if defined(_WIN32) && !defined(__MINGW32__)
auto pathW = String::ToUtf16(path.data());
auto pathW = String::ToUtf16(std::string(path));
std::ifstream fs(pathW, std::ios::in | std::ios::binary);
#else
std::ifstream fs(path.data(), std::ios::in | std::ios::binary);
std::ifstream fs(std::string(path), std::ios::in | std::ios::binary);
#endif
if (!fs.is_open())
{
@@ -83,6 +83,15 @@ namespace File
return result;
}
std::string ReadAllText(const std::string_view& path)
{
auto bytes = ReadAllBytes(path);
// TODO skip BOM
std::string result(bytes.size(), 0);
std::copy(bytes.begin(), bytes.end(), result.begin());
return result;
}
void WriteAllBytes(const std::string &path, const void * buffer, size_t length)
{
auto fs = FileStream(path, FILE_MODE_WRITE);

View File

@@ -28,6 +28,7 @@ namespace File
bool Delete(const std::string &path);
bool Move(const std::string &srcPath, const std::string &dstPath);
std::vector<uint8> ReadAllBytes(const std::string_view& path);
std::string ReadAllText(const std::string_view& path);
void WriteAllBytes(const std::string &path, const void * buffer, size_t length);
std::vector<std::string> ReadAllLines(const std::string &path);
uint64 GetLastModified(const std::string &path);

View File

@@ -130,3 +130,12 @@ TEST_F(CryptTests, RSA_VerifyWithPublic)
bool verified = rsa->VerifyData(*publicKey, data.data(), data.size(), signature.data(), signature.size());
ASSERT_TRUE(verified);
}
TEST_F(CryptTests, RSAKey_GetPublic)
{
auto inPem = File::ReadAllText("C:/Users/Ted/Documents/OpenRCT2/keys/Ted-b298a310905df8865788bdc864560c3d4c3ba562.pubkey");
auto publicKey = Hash::CreateRSAKey();
publicKey->SetPublic(inPem);
auto outPem = publicKey->GetPublic();
ASSERT_EQ(inPem, outPem);
}