1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
#include "stdafx.h"
#include "WinCert.h"
#ifdef WINDOWS
#include "WinErrorMsg.h"
#include "Exception.h"
#include "Core/Io/Text.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32.lib")
namespace ssl {
static std::vector<BYTE> decodeBase64(Str *data) {
DWORD size = 0;
if (!CryptStringToBinaryW(data->c_str(), NULL, CRYPT_STRING_BASE64_ANY, NULL, &size, NULL, NULL))
throwError(data->engine(), S("Failed to decode PEM data: "), GetLastError());
std::vector<BYTE> result(size, 0);
if (!CryptStringToBinaryW(data->c_str(), NULL, CRYPT_STRING_BASE64_ANY, &result[0], &size, NULL, NULL))
throwError(data->engine(), S("Failed to decode PEM data: "), GetLastError());
return result;
}
WinSSLCert *WinSSLCert::fromPEM(Str *data) {
std::vector<BYTE> raw(decodeBase64(data));
const CERT_CONTEXT *c = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &raw[0], DWORD(raw.size()));
if (!c)
throwError(data->engine(), S("Failed to decode certificate data: "), GetLastError());
return new WinSSLCert(c);
}
WinSSLCert::WinSSLCert(const CERT_CONTEXT *c) : data(c) {}
WinSSLCert::~WinSSLCert() {
CertFreeCertificateContext(data);
}
WinSSLCert *WinSSLCert::windows() {
ref();
return this;
}
OpenSSLCert *WinSSLCert::openSSL() {
throw new (runtime::someEngine()) SSLError(S("OpenSSL is not supported on Windows."));
}
WinSSLCert *WinSSLCert::copy() {
const CERT_CONTEXT *c = CertCreateCertificateContext(data->dwCertEncodingType, data->pbCertEncoded, data->cbCertEncoded);
if (!c)
throwError(runtime::someEngine(), S("Failed to copy a certificate: "), GetLastError());
return new WinSSLCert(c);
}
static GcArray<wchar> *decodeName(Engine &e, CERT_NAME_BLOB &blob, DWORD stringType) {
DWORD size = CertNameToStrW(X509_ASN_ENCODING, &blob, stringType, NULL, 0);
GcArray<wchar> *result = runtime::allocArray<wchar>(e, &wcharArrayType, size);
CertNameToStrW(X509_ASN_ENCODING, &blob, stringType, result->v, size);
return result;
}
void WinSSLCert::output(StrBuf *to) {
CERT_INFO *info = data->pCertInfo;
*to << S("Subject: ") << decodeName(to->engine(), info->Subject, CERT_X500_NAME_STR)->v;
*to << S(", issuer: ") << decodeName(to->engine(), info->Issuer, CERT_X500_NAME_STR)->v;
}
WinSSLCertKey *WinSSLCertKey::fromPEM(Str *data) {
std::vector<BYTE> raw(decodeBase64(data));
DWORD size = 0;
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
&raw[0], DWORD(raw.size()), 0, NULL, NULL, &size))
throwError(data->engine(), S("Failed to decode the key: "), GetLastError());
std::vector<BYTE> decoded(size, 0);
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
&raw[0], DWORD(raw.size()), 0, NULL, &decoded[0], &size))
throwError(data->engine(), S("Failed to decode the key: "), GetLastError());
return new WinSSLCertKey(decoded);
}
WinSSLCertKey::WinSSLCertKey(const std::vector<byte> &data) : data(data) {}
WinSSLCertKey::~WinSSLCertKey() {}
const wchar *WinSSLCertKey::validate(SSLCert *cert) {
RefPtr<WinSSLCert> c = cert->windows();
// Try to import it into a temporary store and associate it with the certificate?
TODO(L"Implement early validation");
return NULL;
}
WinSSLCertKey *WinSSLCertKey::windows() {
ref();
return this;
}
OpenSSLCertKey *WinSSLCertKey::openSSL() {
// This is probably fairly easy to do, but useless since we can not do it for certificates at the moment.
throw new (runtime::someEngine()) SSLError(S("OpenSSL is not supported on Windows."));
}
WinKeyStore::WinKeyStore(WinSSLCertKey *k, bool anonymous) : provider(0), key(0) {
memset(name, 0, sizeof(name));
if (anonymous) {
if (!CryptAcquireContext(&provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
throwError(runtime::someEngine(), S("Failed to acquire a temporary keystore: "), GetLastError());
} else {
DWORD pid = GetCurrentProcessId();
Nat id = 0;
while (true) {
id++;
_snwprintf_s(name, sizeof(name), sizeof(name), S("STORM_%u_%u"), pid, id);
if (CryptAcquireContext(&provider, name, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
break;
DWORD error = GetLastError();
if (error != NTE_EXISTS)
throwError(runtime::someEngine(), S("Failed to acquire a persistent keystore (note: this can not be done as a guest user): "), error);
}
}
// Add the key!
if (!CryptImportKey(provider, &k->data[0], DWORD(k->data.size()), NULL, 0, &key)) {
DWORD error = GetLastError();
CryptReleaseContext(provider, 0);
throwError(runtime::someEngine(), S("Failed to import the key: "), error);
}
}
WinKeyStore::~WinKeyStore() {
CryptDestroyKey(key);
CryptReleaseContext(provider, 0);
if (name[0] != '\0') {
if (!CryptAcquireContext(&provider, name, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET)) {
Nat error = GetLastError();
WARNING(L"Failed to destroy the keystore: " << name << S(" (") << toHex(error) << S(")"));
}
}
}
void WinKeyStore::attach(PCCERT_CONTEXT certificate) {
CRYPT_KEY_PROV_INFO info = { 0 };
info.pwszContainerName = name;
info.pwszProvName = MS_ENHANCED_PROV;
info.dwProvType = PROV_RSA_FULL;
info.dwKeySpec = AT_KEYEXCHANGE;
info.dwFlags = CERT_SET_KEY_PROV_HANDLE_PROP_ID;
if (!CertSetCertificateContextProperty(certificate, CERT_KEY_PROV_INFO_PROP_ID, 0, &info))
throwError(runtime::someEngine(), S("Failed to attach the certificate to the key: "), GetLastError());
}
}
#endif
|