mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
Initial Add
This commit is contained in:
872
meshcore/wincrypto-dualmode.cpp
Normal file
872
meshcore/wincrypto-dualmode.cpp
Normal file
@@ -0,0 +1,872 @@
|
||||
/*
|
||||
Copyright 2006 - 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// When compiled in 32bit, this module uses Windows CAPI which is compatible with Windows XP.
|
||||
// When compiled in 64bit, this module uses Windows CNG (Crypto Next Gen) that is compatible and will use TPM modules.
|
||||
|
||||
#if !defined(_NOCAPI) && defined(WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "wincrypto.h"
|
||||
#include "../microstack/ILibParsers.h"
|
||||
#ifdef _WIN64
|
||||
HCRYPTPROV wincrypto_hProv = NULL;
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
||||
LPWSTR wincrypto_CngProviders[3] = { L"Microsoft Platform Crypto Provider", MS_KEY_STORAGE_PROVIDER, NULL };
|
||||
LPWSTR wincrypto_containername = L"MeshAgentContainerNG";
|
||||
LPCTSTR wincrypto_subject = "CN=MeshNodeCertificateNG";
|
||||
#else
|
||||
NCRYPT_PROV_HANDLE wincrypto_hProv = NULL;
|
||||
LPWSTR wincrypto_containername = L"MeshAgentContainer";
|
||||
LPCTSTR wincrypto_subject = "CN=MeshNodeCertificate";
|
||||
LPCWSTR wincrypto_CapiProviders[3] = { L"Intel IPT Cryptographic Provider", NULL, NULL };
|
||||
DWORD wincrypto_CapiProvidersTypes[3] = { PROV_RSA_AES, PROV_RSA_AES, 0xFFFFFFFF };
|
||||
#endif
|
||||
HANDLE wincrypto_hCertStore = NULL;
|
||||
PCCERT_CONTEXT wincrypto_certCtx = NULL;
|
||||
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
|
||||
|
||||
#define wincrypto_TlsServerOid "1.3.6.1.5.5.7.3.1" // TLS Server certificate
|
||||
#define wincrypto_TlsClientOid "1.3.6.1.5.5.7.3.2" // TLS Client certificate
|
||||
#define wincrypto_AmtConsoleOid "2.16.840.1.113741.1.2.1" // Intel AMT Remote Console
|
||||
#define wincrypto_AmtAgentOid "2.16.840.1.113741.1.2.2" // Intel AMT Remote Agent
|
||||
#define wincrypto_AmtSetupOid "2.16.840.1.113741.1.2.3" // Intel AMT Local Console
|
||||
const char* wincrypto_ServerOids[1] = { wincrypto_TlsServerOid };
|
||||
const char* wincrypto_ClientOids[4] = { wincrypto_TlsClientOid, wincrypto_AmtConsoleOid, wincrypto_AmtAgentOid, wincrypto_AmtSetupOid };
|
||||
|
||||
#ifndef RSA2048BIT_KEY
|
||||
#define RSA2048BIT_KEY 0x08000000
|
||||
#endif
|
||||
|
||||
#ifndef RSA3072BIT_KEY
|
||||
#define RSA3072BIT_KEY 0x0C000000
|
||||
#endif
|
||||
|
||||
void __fastcall wincrypto_setregistry(LPWSTR name, LPWSTR value)
|
||||
{
|
||||
HKEY hKey;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, save the key in LOCAL_MACHINE
|
||||
if(RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#endif
|
||||
{
|
||||
RegSetValueExW(hKey, name, 0, REG_SZ, (BYTE*)value, (DWORD)(wcslen(value) * 2));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall wincrypto_setregistryA(char* name, char* value)
|
||||
{
|
||||
HKEY hKey;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, save the key in LOCAL_MACHINE
|
||||
if(RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#endif
|
||||
{
|
||||
RegSetValueExA(hKey, name, 0, REG_SZ, (BYTE*)value, (DWORD)strlen(value));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_getregistry(LPCWSTR name, char** value)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD len = 0;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, open the key in LOCAL_MACHINE
|
||||
if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#endif
|
||||
if (RegQueryValueExW(hKey, name, NULL, NULL, NULL, &len ) != ERROR_SUCCESS || len == 0) { *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
if ((*value = (char*)malloc(len)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (RegQueryValueExW(hKey, name, NULL, NULL, (LPBYTE)(*value), &len ) != ERROR_SUCCESS || len == 0) { free(*value); *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
RegCloseKey(hKey);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_getregistryA(char* name, char** value)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD len = 0;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, open the key in LOCAL_MACHINE
|
||||
if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#endif
|
||||
if (RegQueryValueExA(hKey, name, NULL, NULL, NULL, &len ) != ERROR_SUCCESS || len == 0) { *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
if ((*value = (char*)malloc(len)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (RegQueryValueExA(hKey, name, NULL, NULL, (LPBYTE)(*value), &len ) != ERROR_SUCCESS || len == 0) { free(*value); *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
RegCloseKey(hKey);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_isopen()
|
||||
{
|
||||
return (wincrypto_hProv != NULL && wincrypto_hCertStore != NULL && wincrypto_certCtx != NULL);
|
||||
}
|
||||
|
||||
void __fastcall wincrypto_close()
|
||||
{
|
||||
if (wincrypto_certCtx != NULL) { CertFreeCertificateContext(wincrypto_certCtx); wincrypto_certCtx = NULL; }
|
||||
#ifdef _WIN64
|
||||
if (wincrypto_hProv != NULL) { NCryptFreeObject(wincrypto_hProv); wincrypto_hProv = NULL; }
|
||||
#else
|
||||
if (wincrypto_hProv != NULL) { CryptReleaseContext(wincrypto_hProv, 0); wincrypto_hProv = NULL; }
|
||||
#endif
|
||||
if (wincrypto_hCertStore != NULL) { CertCloseStore(wincrypto_hCertStore, 0); wincrypto_hCertStore = NULL; }
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_open(int newcert)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
DWORD KeyLength = 3072;
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
#else
|
||||
CRYPT_KEY_PROV_INFO ckp;
|
||||
HCRYPTKEY hKeyNode = NULL;
|
||||
DWORD providerType = 0;
|
||||
DWORD len;
|
||||
#endif
|
||||
LPWSTR providerName = NULL;
|
||||
char* providerNameA = NULL;
|
||||
DWORD r = 0;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
BOOL status = FALSE;
|
||||
PBYTE subjectEncoded = NULL;
|
||||
DWORD subjectEncodedSize;
|
||||
CRYPT_KEY_PROV_INFO kpi;
|
||||
#ifdef _CONSOLE
|
||||
DWORD machineflag = 0;
|
||||
#else
|
||||
DWORD machineflag = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
|
||||
SYSTEMTIME st1;
|
||||
SYSTEMTIME st2;
|
||||
CERT_EXTENSIONS exts;
|
||||
CERT_NAME_BLOB sib;
|
||||
CRYPT_ALGORITHM_IDENTIFIER sa;
|
||||
|
||||
CERT_EXTENSION certExtension[2];
|
||||
BYTE* pbPolicyInfo2 = NULL;
|
||||
CERT_BASIC_CONSTRAINTS2_INFO BasicConstraints;
|
||||
BYTE* pbBasicConstraints = NULL;
|
||||
CRYPT_BIT_BLOB keyusage2;
|
||||
DWORD pkSize = 0;
|
||||
|
||||
ZeroMemory(&exts, sizeof(exts));
|
||||
wincrypto_close();
|
||||
|
||||
#ifdef _WIN64
|
||||
// Open the best CNG possible
|
||||
while (providerName == NULL && wincrypto_CngProviders[r] != NULL) {
|
||||
providerName = wincrypto_CngProviders[r];
|
||||
NCryptOpenStorageProvider(&wincrypto_hProv, providerName, 0);
|
||||
if (wincrypto_hProv == NULL) providerName = NULL;
|
||||
r++;
|
||||
}
|
||||
if (wincrypto_hProv == NULL) goto error;
|
||||
#else
|
||||
// Open the best CSP possible with an existing Mesh container
|
||||
status = FALSE;
|
||||
while (status == FALSE && wincrypto_CapiProvidersTypes[r] != 0xFFFFFFFF) {
|
||||
providerName = (LPWSTR)wincrypto_CapiProviders[r];
|
||||
providerType = wincrypto_CapiProvidersTypes[r];
|
||||
if (newcert) { status = CryptAcquireContextW(&wincrypto_hProv, wincrypto_containername, wincrypto_CapiProviders[r], wincrypto_CapiProvidersTypes[r], machineflag | CRYPT_DELETEKEYSET); status = FALSE; }
|
||||
else status = CryptAcquireContextW(&wincrypto_hProv, wincrypto_containername, wincrypto_CapiProviders[r], wincrypto_CapiProvidersTypes[r], machineflag);
|
||||
r++;
|
||||
}
|
||||
|
||||
// If a container does not already exist, create one with the best provider
|
||||
if (status == FALSE)
|
||||
{
|
||||
r = 0;
|
||||
while (status == FALSE && wincrypto_CapiProvidersTypes[r] != 0xFFFFFFFF) {
|
||||
providerName = (LPWSTR)wincrypto_CapiProviders[r];
|
||||
providerType = wincrypto_CapiProvidersTypes[r];
|
||||
status = CryptAcquireContextW(&wincrypto_hProv, wincrypto_containername, wincrypto_CapiProviders[r], wincrypto_CapiProvidersTypes[r], machineflag | CRYPT_NEWKEYSET);
|
||||
r++;
|
||||
}
|
||||
}
|
||||
if (!status) goto error;
|
||||
#endif
|
||||
|
||||
// Create cert subject string in format csp understands
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, NULL, &subjectEncodedSize, NULL)) goto error;
|
||||
if ((subjectEncoded = (PBYTE)malloc(subjectEncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, subjectEncoded, &subjectEncodedSize, NULL)) goto error;
|
||||
sib.cbData = subjectEncodedSize;
|
||||
sib.pbData = subjectEncoded;
|
||||
|
||||
#ifdef _CONSOLE
|
||||
wincrypto_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, wincrypto_hProv, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG, L"MY"); // CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
||||
if (!wincrypto_hCertStore) goto error;
|
||||
#else
|
||||
wincrypto_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, wincrypto_hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG, L"MY"); // CERT_STORE_NO_CRYPT_RELEASE_FLAG // CERT_SYSTEM_STORE_LOCAL_MACHINE
|
||||
if (!wincrypto_hCertStore) goto error;
|
||||
#endif
|
||||
|
||||
// Look for cert and if exists, delete it
|
||||
wincrypto_certCtx = CertFindCertificateInStore(wincrypto_hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL );
|
||||
|
||||
// Check if we can get the private key
|
||||
if (wincrypto_certCtx != NULL)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) { newcert = 1; }
|
||||
if (hKeyNodeSpec != CERT_NCRYPT_KEY_SPEC) { newcert = 1; } // If this private key is not CNG, don't use it.
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
#else
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) { newcert = 1; }
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { CryptReleaseContext(hKeyNode, 0); }
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check if have a certificate already, or need to create a new one
|
||||
if (wincrypto_certCtx != NULL && newcert == 0) goto end;
|
||||
if (wincrypto_certCtx) { status = CertDeleteCertificateFromStore(wincrypto_certCtx); if (!status) goto error; wincrypto_certCtx = NULL; }
|
||||
|
||||
// Generate node RSA key-pair
|
||||
#ifdef _WIN64
|
||||
#ifdef _CONSOLE
|
||||
if (FAILED(status = NCryptCreatePersistedKey(wincrypto_hProv, &hKeyNode, BCRYPT_RSA_ALGORITHM, wincrypto_containername, 0, NCRYPT_OVERWRITE_KEY_FLAG))) goto error;
|
||||
#else
|
||||
if (FAILED(status = NCryptCreatePersistedKey(wincrypto_hProv, &hKeyNode, BCRYPT_RSA_ALGORITHM, wincrypto_containername, 0, NCRYPT_MACHINE_KEY_FLAG | NCRYPT_OVERWRITE_KEY_FLAG))) goto error;
|
||||
#endif
|
||||
if (FAILED(status = NCryptSetProperty(hKeyNode, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) {
|
||||
KeyLength = 2048; // If 3072 is not supported, go down to 2048.
|
||||
if (FAILED(status = NCryptSetProperty(hKeyNode, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) { goto error; }
|
||||
}
|
||||
if (FAILED(status = NCryptFinalizeKey(hKeyNode, NCRYPT_SILENT_FLAG))) { goto error; } // Ask for silent create, this will fail if not admin.
|
||||
|
||||
// Create self signed cert
|
||||
ZeroMemory(&kpi, sizeof(kpi));
|
||||
kpi.pwszContainerName = wincrypto_containername;
|
||||
kpi.pwszProvName = providerName;
|
||||
kpi.dwProvType = 0;
|
||||
kpi.dwFlags = 0;
|
||||
kpi.dwKeySpec = 0;
|
||||
#else
|
||||
if (!CryptGenKey(wincrypto_hProv, AT_KEYEXCHANGE, RSA3072BIT_KEY, &hKeyNode)) goto error;
|
||||
|
||||
// Create self signed cert
|
||||
ZeroMemory(&kpi, sizeof(kpi));
|
||||
kpi.pwszContainerName = wincrypto_containername;
|
||||
kpi.pwszProvName = providerName;
|
||||
kpi.dwProvType = providerType;
|
||||
kpi.dwKeySpec = AT_KEYEXCHANGE;
|
||||
#ifdef _CONSOLE
|
||||
ckp.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
|
||||
#else
|
||||
ckp.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID | CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ZeroMemory(&certExtension, sizeof(certExtension));
|
||||
|
||||
// Set key usage for root certificate
|
||||
BYTE ByteData = CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_OFFLINE_CRL_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE;
|
||||
ZeroMemory(&keyusage2, sizeof(keyusage2));
|
||||
keyusage2.cbData = 1;
|
||||
keyusage2.pbData = &ByteData;
|
||||
|
||||
// Encode key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo2 = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, pbPolicyInfo2, &pkSize)) goto end;
|
||||
certExtension[0].pszObjId = szOID_KEY_USAGE;
|
||||
certExtension[0].fCritical = FALSE;
|
||||
certExtension[0].Value.cbData = pkSize;
|
||||
certExtension[0].Value.pbData = pbPolicyInfo2;
|
||||
|
||||
// Encode Basic Constraints
|
||||
// --> Subject Type=CA
|
||||
// --> Path Length Constraint=None
|
||||
|
||||
ZeroMemory(&BasicConstraints, sizeof(BasicConstraints));
|
||||
BasicConstraints.fCA = TRUE;
|
||||
BasicConstraints.fPathLenConstraint = FALSE;
|
||||
BasicConstraints.dwPathLenConstraint = 0;
|
||||
|
||||
// Get Basic Constraints blob size*/
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, (LPVOID)&BasicConstraints, NULL, &pkSize)) goto end;
|
||||
if ((pbBasicConstraints = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, (LPVOID)&BasicConstraints, pbBasicConstraints, &pkSize)) goto end;
|
||||
|
||||
// Set Basic Constraints extension
|
||||
certExtension[1].pszObjId = szOID_BASIC_CONSTRAINTS2;
|
||||
certExtension[1].fCritical = FALSE;
|
||||
certExtension[1].Value.cbData = pkSize;
|
||||
certExtension[1].Value.pbData = pbBasicConstraints;
|
||||
|
||||
exts.cExtension = 2;
|
||||
exts.rgExtension = certExtension;
|
||||
|
||||
// Setup certificate expiration
|
||||
GetSystemTime(&st1);
|
||||
GetSystemTime(&st2);
|
||||
st1.wYear -= 1; // Starts a year ago
|
||||
st2.wYear += 30; // Expires 30 years from now
|
||||
|
||||
ZeroMemory(&sa, sizeof(sa));
|
||||
sa.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
|
||||
#ifdef _WIN64
|
||||
wincrypto_certCtx = CertCreateSelfSignCertificate(NULL, &sib, 0, &kpi, NULL, NULL, NULL, NULL);
|
||||
wincrypto_certCtx = CertCreateSelfSignCertificate(NULL, &sib, 0, &kpi, &sa, &st1, &st2, &exts);
|
||||
#else
|
||||
wincrypto_certCtx = CertCreateSelfSignCertificate(wincrypto_hProv, &sib, 0, &kpi, &sa, &st1, &st2, &exts);
|
||||
#endif
|
||||
if (!wincrypto_certCtx) { goto error; }
|
||||
|
||||
// Note this is a different context to certCtx, this ctx is the in-store ctx
|
||||
status = CertAddCertificateContextToStore(wincrypto_hCertStore, wincrypto_certCtx, CERT_STORE_ADD_REPLACE_EXISTING, &wincrypto_certCtx);
|
||||
if (!status || wincrypto_certCtx == NULL) goto error;
|
||||
|
||||
#ifndef _WIN64
|
||||
// Associate private key with cert in key store
|
||||
ZeroMemory(&ckp, sizeof(ckp));
|
||||
ckp.pwszContainerName = wincrypto_containername;
|
||||
ckp.pwszProvName = providerName;
|
||||
ckp.dwProvType = providerType;
|
||||
ckp.dwKeySpec = AT_KEYEXCHANGE;
|
||||
#ifdef _CONSOLE
|
||||
ckp.dwFlags = 0;
|
||||
#else
|
||||
ckp.dwFlags = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
status = CertSetCertificateContextProperty(wincrypto_certCtx, CERT_KEY_PROV_INFO_PROP_ID, 0, &ckp);
|
||||
if (!status) goto error;
|
||||
#endif
|
||||
|
||||
// Get the selected provider name and save it in the registry
|
||||
#ifdef _WIN64
|
||||
if (providerName != NULL) wincrypto_setregistry(L"KeyStore", providerName);
|
||||
#else
|
||||
if (!CryptGetProvParam(wincrypto_hProv, PP_NAME, NULL, &len, 0)) goto error;
|
||||
providerNameA = (char*)malloc(len);
|
||||
if (!CryptGetProvParam(wincrypto_hProv, PP_NAME, (BYTE*)providerNameA, &len, 0)) goto error;
|
||||
wincrypto_setregistryA("KeyStore", providerNameA);
|
||||
free(providerNameA);
|
||||
#endif
|
||||
|
||||
goto end;
|
||||
|
||||
error:
|
||||
// Clean up
|
||||
#ifdef _WIN64
|
||||
if (hKeyNode != NULL) NCryptFreeObject(hKeyNode);
|
||||
#else
|
||||
if (hKeyNode != NULL) CryptDestroyKey(hKeyNode);
|
||||
#endif
|
||||
wincrypto_close();
|
||||
return 1;
|
||||
|
||||
end:
|
||||
if (subjectEncoded != NULL) free(subjectEncoded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
void __fastcall wincrypto_random(int length, char* result)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
if(!NT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0))) goto Cleanup;
|
||||
if(!NT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)result, length, BCRYPT_RNG_USE_ENTROPY_IN_BUFFER))) goto Cleanup;
|
||||
Cleanup:
|
||||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||||
}
|
||||
|
||||
// Perform cryptographic hash
|
||||
int __fastcall wincrypto_hash(LPCWSTR alg, char* data, int datalen, char* result, DWORD resultlen)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
BCRYPT_HASH_HANDLE hHash = NULL;
|
||||
DWORD cbData = 0, cbHashObject = 0;
|
||||
PBYTE pbHashObject = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, alg, NULL, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0))) goto Cleanup;
|
||||
if ((pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject)) == NULL) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptCreateHash(hAlg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptHashData(hHash, (PBYTE)data, datalen, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptFinishHash(hHash, (PUCHAR)result, resultlen, 0))) goto Cleanup;
|
||||
r = resultlen;
|
||||
Cleanup:
|
||||
if (hHash) BCryptDestroyHash(hHash);
|
||||
if (pbHashObject) HeapFree(GetProcessHeap(), 0, pbHashObject);
|
||||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg,0);
|
||||
return r;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_md5(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_MD5_ALGORITHM, data, datalen, result, 16); }
|
||||
int __fastcall wincrypto_sha256(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_SHA256_ALGORITHM, data, datalen, result, 32); }
|
||||
int __fastcall wincrypto_sha385(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_SHA384_ALGORITHM, data, datalen, result, 32); }
|
||||
#else
|
||||
void __fastcall wincrypto_random(int length, char* result)
|
||||
{
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || !CryptGenRandom(wincrypto_hProv, length, (BYTE*)result)) ILIBCRITICALEXIT(252);
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_hash(ALG_ID calg, char* data, int datalen, char* result, int resultlen)
|
||||
{
|
||||
HCRYPTHASH hHash = NULL;
|
||||
DWORD hlen = resultlen;
|
||||
int r = 0;
|
||||
|
||||
// Add hash of the certificate to start of data
|
||||
if (wincrypto_hProv == NULL || CryptCreateHash(wincrypto_hProv, calg, 0, 0, &hHash) == 0) return r;
|
||||
if (CryptHashData(hHash, (BYTE*)data, datalen, 0) == 0) goto end;
|
||||
if (!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)result, &hlen, 0)) goto end;
|
||||
r = resultlen;
|
||||
end:
|
||||
if (hHash) CryptDestroyHash(hHash);
|
||||
return r;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_md5(char* data, int datalen, char* result) { return wincrypto_hash(CALG_MD5, data, datalen, result, 16); }
|
||||
int __fastcall wincrypto_sha256(char* data, int datalen, char* result) { return wincrypto_hash(CALG_SHA_256, data, datalen, result, 32); }
|
||||
int __fastcall wincrypto_sha384(char* data, int datalen, char* result) { return wincrypto_hash(CALG_SHA_384, data, datalen, result, 32); }
|
||||
#endif
|
||||
|
||||
// Sign the data with the Mesh Agent certificate and return a PKCS7 result.
|
||||
int __fastcall wincrypto_sign(char* data, int len, char** signature)
|
||||
{
|
||||
int signatureLen = 0;
|
||||
DWORD cbSignedBlob;
|
||||
HCRYPTMSG hMsg = NULL;
|
||||
DWORD HashAlgSize;
|
||||
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
|
||||
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
|
||||
CERT_BLOB SignerCertBlob;
|
||||
CERT_BLOB SignerCertBlobArray[1];
|
||||
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
|
||||
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
|
||||
#ifdef _WIN64
|
||||
int r = 0;
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
#endif
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
// Initialize the algorithm identifier structure.
|
||||
HashAlgSize = sizeof(HashAlgorithm);
|
||||
memset(&HashAlgorithm, 0, HashAlgSize); // Initialize to zero
|
||||
HashAlgorithm.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
|
||||
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
|
||||
memset(&SignerEncodeInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
|
||||
SignerEncodeInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
|
||||
#ifdef _WIN64
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) { r = 10; goto end; }
|
||||
SignerEncodeInfo.hNCryptKey = hKeyNode;
|
||||
#else
|
||||
SignerEncodeInfo.hCryptProv = wincrypto_hProv;
|
||||
#endif
|
||||
SignerEncodeInfo.pCertInfo = wincrypto_certCtx->pCertInfo;
|
||||
SignerEncodeInfo.dwKeySpec = AT_KEYEXCHANGE;
|
||||
SignerEncodeInfo.HashAlgorithm = HashAlgorithm;
|
||||
SignerEncodeInfo.pvHashAuxInfo = NULL;
|
||||
|
||||
// Create an array of one.
|
||||
SignerEncodeInfoArray[0] = SignerEncodeInfo;
|
||||
|
||||
// Initialize the CMSG_SIGNED_ENCODE_INFO structure.
|
||||
SignerCertBlob.cbData = wincrypto_certCtx->cbCertEncoded;
|
||||
SignerCertBlob.pbData = wincrypto_certCtx->pbCertEncoded;
|
||||
|
||||
// Initialize the array of one CertBlob.
|
||||
SignerCertBlobArray[0] = SignerCertBlob;
|
||||
memset(&SignedMsgEncodeInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO));
|
||||
SignedMsgEncodeInfo.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO);
|
||||
SignedMsgEncodeInfo.cSigners = 1;
|
||||
SignedMsgEncodeInfo.rgSigners = SignerEncodeInfoArray;
|
||||
SignedMsgEncodeInfo.cCertEncoded = 1;
|
||||
SignedMsgEncodeInfo.rgCertEncoded = SignerCertBlobArray;
|
||||
SignedMsgEncodeInfo.rgCrlEncoded = NULL;
|
||||
|
||||
// Get the size of the encoded, signed message BLOB.
|
||||
cbSignedBlob = CryptMsgCalculateEncodedLength(MY_ENCODING_TYPE, 0, CMSG_SIGNED, &SignedMsgEncodeInfo, NULL, len);
|
||||
if (cbSignedBlob == 0) goto end;
|
||||
|
||||
// Allocate memory for the encoded BLOB.
|
||||
if ((*signature = (char*)malloc(cbSignedBlob)) == NULL) ILIBCRITICALEXIT(254);
|
||||
|
||||
// Open a message to encode.
|
||||
hMsg = CryptMsgOpenToEncode(MY_ENCODING_TYPE, 0, CMSG_SIGNED, &SignedMsgEncodeInfo, NULL, NULL);
|
||||
|
||||
// Update the message with the data.
|
||||
if (!CryptMsgUpdate(hMsg, (BYTE*)data, len, TRUE)) goto end;
|
||||
|
||||
// Get the resulting message.
|
||||
if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, (BYTE*)*signature, &cbSignedBlob)) goto end;
|
||||
signatureLen = cbSignedBlob;
|
||||
|
||||
end:
|
||||
// Clean up
|
||||
if (hMsg) CryptMsgClose(hMsg);
|
||||
if (signatureLen == 0 && *signature != NULL) { free(*signature); *signature = NULL; }
|
||||
#ifdef _WIN64
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
#endif
|
||||
return signatureLen;
|
||||
}
|
||||
|
||||
struct wincrypto_stream {
|
||||
char* buf;
|
||||
int buflen;
|
||||
int ptr;
|
||||
};
|
||||
|
||||
static BOOL WINAPI wincrypto_CmsgStreamOutputCallback(IN const void *pvArg, IN BYTE *pbData, IN DWORD cbData, IN BOOL fFinal)
|
||||
{
|
||||
struct wincrypto_stream* StreamArg = (struct wincrypto_stream*)pvArg;
|
||||
UNREFERENCED_PARAMETER(fFinal);
|
||||
|
||||
if ((int)(StreamArg->ptr + cbData) > (int)StreamArg->buflen) return FALSE;
|
||||
memcpy(StreamArg->buf + StreamArg->ptr, pbData, cbData);
|
||||
StreamArg->ptr += cbData;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Decrypt the PKCS7 block and return the content.
|
||||
int __fastcall wincrypto_decrypt(char* encdata, int encdatalen, char** data)
|
||||
{
|
||||
int datalen = 0;
|
||||
HCRYPTMSG hMsg = NULL;
|
||||
CMSG_CTRL_DECRYPT_PARA decryptPara = { sizeof(CMSG_CTRL_DECRYPT_PARA) };
|
||||
BOOL flagHandle = FALSE;
|
||||
CMSG_STREAM_INFO StreamInfo;
|
||||
struct wincrypto_stream StreamArg;
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
// Perform setup
|
||||
if ((StreamArg.buf = (char*)malloc(encdatalen)) == NULL) ILIBCRITICALEXIT(254);
|
||||
StreamArg.buflen = encdatalen;
|
||||
StreamArg.ptr = 0;
|
||||
StreamInfo.cbContent= CMSG_INDEFINITE_LENGTH;
|
||||
StreamInfo.pfnStreamOutput = wincrypto_CmsgStreamOutputCallback;
|
||||
StreamInfo.pvArg = &StreamArg;
|
||||
if ((hMsg = CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, 0, NULL, NULL, &StreamInfo)) == NULL) goto end;
|
||||
if (!CryptMsgUpdate(hMsg, (BYTE*)encdata, encdatalen, TRUE)) goto end;
|
||||
|
||||
// Setup the certificate
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &decryptPara.hCryptProv, &decryptPara.dwKeySpec, &flagHandle)) goto end;
|
||||
decryptPara.dwRecipientIndex = 0;
|
||||
|
||||
// Perform the decrypt
|
||||
if (!CryptMsgControl(hMsg, 0, CMSG_CTRL_DECRYPT, &decryptPara)) goto end;
|
||||
datalen = StreamArg.ptr;
|
||||
*data = StreamArg.buf;
|
||||
StreamArg.buf = NULL;
|
||||
|
||||
end:
|
||||
// Clean up
|
||||
if (hMsg) CryptMsgClose(hMsg);
|
||||
if (StreamArg.buf != NULL) { free(StreamArg.buf); }
|
||||
return datalen;
|
||||
}
|
||||
|
||||
// Get the X509 certificate including the public key (Direct reference, no need to free this).
|
||||
int __fastcall wincrypto_getcert(char** data)
|
||||
{
|
||||
if (wincrypto_certCtx == NULL) { *data = NULL; return 0; }
|
||||
*data = (char*)wincrypto_certCtx->pbCertEncoded;
|
||||
return (int)wincrypto_certCtx->cbCertEncoded;
|
||||
}
|
||||
|
||||
// Create an X509, RSA 3027bit certificate with the MeshAgent certificate as signing root.
|
||||
int __fastcall wincrypto_mkCert(wchar_t* subject, int certtype, wchar_t* password, char** data)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
NCRYPT_KEY_HANDLE hNewKey = NULL;
|
||||
DWORD KeyLength = 2048;
|
||||
DWORD KeyPolicy = NCRYPT_ALLOW_EXPORT_FLAG;
|
||||
#endif
|
||||
|
||||
int len = 0;
|
||||
HCRYPTKEY hKey = NULL;
|
||||
PCCERT_CONTEXT certCtx = NULL;
|
||||
HCRYPTPROV hProv = NULL;
|
||||
CRYPT_KEY_PROV_INFO keyProviderInfo;
|
||||
|
||||
// Issuer and cert subject names
|
||||
CERT_NAME_BLOB sib1;
|
||||
CERT_NAME_BLOB sib2;
|
||||
PBYTE subject1Encoded = NULL;
|
||||
DWORD subject1EncodedSize;
|
||||
PBYTE subject2Encoded = NULL;
|
||||
DWORD subject2EncodedSize;
|
||||
|
||||
CRYPT_KEY_PROV_INFO kpi;
|
||||
BYTE* pbPolicyInfo = NULL;
|
||||
BYTE* pbPolicyInfo2 = NULL;
|
||||
|
||||
CERT_EXTENSION certExtension[2];
|
||||
CERT_ENHKEY_USAGE keyusage;
|
||||
CRYPT_BIT_BLOB keyusage2;
|
||||
//CERT_BASIC_CONSTRAINTS2_INFO BasicConstraints;
|
||||
//BYTE* pbBasicConstraints = NULL;
|
||||
|
||||
DWORD pkSize = 0;
|
||||
PCERT_PUBLIC_KEY_INFO pkInfo = NULL;
|
||||
|
||||
CERT_INFO certInfo;
|
||||
SYSTEMTIME systemTime;
|
||||
FILETIME notBefore;
|
||||
FILETIME notAfter;
|
||||
BYTE serialNumber[8];
|
||||
DWORD certSize = 0xFFFFFFFF;
|
||||
BYTE *certData = NULL;
|
||||
|
||||
HCERTSTORE hCertStore = NULL;
|
||||
PCCERT_CONTEXT certContext = NULL;
|
||||
CRYPT_DATA_BLOB pfxBlob;
|
||||
DWORD pfxExportFlags = EXPORT_PRIVATE_KEYS; // | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY;
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
*data = NULL;
|
||||
ZeroMemory(&kpi, sizeof(kpi));
|
||||
ZeroMemory(&certExtension, sizeof(certExtension));
|
||||
|
||||
// Generate node RSA key-pair
|
||||
#ifdef _WIN64
|
||||
if (FAILED(NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) goto end;
|
||||
#ifdef _CONSOLE
|
||||
if (FAILED(NCryptCreatePersistedKey(hProv, &hNewKey, BCRYPT_RSA_ALGORITHM, L"MeshDummy", AT_KEYEXCHANGE, NCRYPT_OVERWRITE_KEY_FLAG))) goto end;
|
||||
#else
|
||||
if (FAILED(NCryptCreatePersistedKey(hProv, &hNewKey, BCRYPT_RSA_ALGORITHM, L"MeshDummy", AT_KEYEXCHANGE, NCRYPT_MACHINE_KEY_FLAG | NCRYPT_OVERWRITE_KEY_FLAG))) goto end;
|
||||
#endif
|
||||
if (FAILED(NCryptSetProperty(hNewKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) goto end;
|
||||
if (FAILED(NCryptSetProperty(hNewKey, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&KeyPolicy, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) goto end;
|
||||
if (FAILED(NCryptFinalizeKey(hNewKey, NCRYPT_SILENT_FLAG))) goto end;
|
||||
if (!CryptExportPublicKeyInfo(hNewKey, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, &pkSize)) goto end;
|
||||
if ((pkInfo = (PCERT_PUBLIC_KEY_INFO)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptExportPublicKeyInfo(hNewKey, AT_KEYEXCHANGE, X509_ASN_ENCODING, pkInfo, &pkSize)) goto end;
|
||||
#else
|
||||
// Create a dummy container
|
||||
#ifdef _CONSOLE
|
||||
if (!CryptAcquireContextW(&hProv, L"MeshDummy", NULL, PROV_RSA_AES, 0) && !CryptAcquireContextW(&hProv, L"MeshDummy", NULL, PROV_RSA_AES, CRYPT_NEWKEYSET)) goto end;
|
||||
#else
|
||||
if (!CryptAcquireContextW(&hProv, L"MeshDummy", NULL, PROV_RSA_AES, CRYPT_MACHINE_KEYSET) && !CryptAcquireContextW(&hProv, L"MeshDummy", NULL, PROV_RSA_AES, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) goto end;
|
||||
#endif
|
||||
|
||||
// Generate 3072-bit RSA keypair and export the public key information
|
||||
if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | RSA3072BIT_KEY, &hKey)) goto end;
|
||||
if (!CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, &pkSize)) goto end;
|
||||
if ((pkInfo = (PCERT_PUBLIC_KEY_INFO)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, pkInfo, &pkSize)) goto end;
|
||||
#endif
|
||||
|
||||
// Create cert issuer string in format the CSP understands
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, NULL, &subject1EncodedSize, NULL)) goto end;
|
||||
if ((subject1Encoded = (PBYTE)malloc(subject1EncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, subject1Encoded, &subject1EncodedSize, NULL)) goto end;
|
||||
sib1.cbData = subject1EncodedSize;
|
||||
sib1.pbData = subject1Encoded;
|
||||
|
||||
// Create cert subject string in format the CSP understands
|
||||
if (!CertStrToNameW(X509_ASN_ENCODING, subject, CERT_X500_NAME_STR, NULL, NULL, &subject2EncodedSize, NULL)) goto end;
|
||||
if ((subject2Encoded = (PBYTE)malloc(subject2EncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToNameW(X509_ASN_ENCODING, subject, CERT_X500_NAME_STR, NULL, subject2Encoded, &subject2EncodedSize, NULL)) goto end;
|
||||
sib2.cbData = subject2EncodedSize;
|
||||
sib2.pbData = subject2Encoded;
|
||||
|
||||
// Setup the certificate validity period
|
||||
ZeroMemory(&certInfo, sizeof(certInfo));
|
||||
GetSystemTime(&systemTime);
|
||||
systemTime.wYear -= 1;
|
||||
SystemTimeToFileTime(&systemTime, ¬Before);
|
||||
systemTime.wYear += 30;
|
||||
SystemTimeToFileTime(&systemTime, ¬After);
|
||||
|
||||
// Generate a serial number.
|
||||
wincrypto_random(8, (char*)serialNumber);
|
||||
|
||||
// Setup all of the certificate information
|
||||
certInfo.dwVersion = CERT_V3;
|
||||
certInfo.SerialNumber.cbData = 8;
|
||||
certInfo.SerialNumber.pbData = serialNumber;
|
||||
certInfo.SignatureAlgorithm.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
certInfo.Issuer = sib1;
|
||||
certInfo.NotBefore = notBefore;
|
||||
certInfo.NotAfter = notAfter;
|
||||
certInfo.Subject = sib2;
|
||||
certInfo.SubjectPublicKeyInfo = *pkInfo;
|
||||
|
||||
// Setup key usage
|
||||
if (certtype == 1)
|
||||
{
|
||||
// TLS Server certificate
|
||||
keyusage.cUsageIdentifier = 1;
|
||||
keyusage.rgpszUsageIdentifier = (LPSTR*)wincrypto_ServerOids;
|
||||
}
|
||||
else if (certtype == 2)
|
||||
{
|
||||
// TLS Client certificate
|
||||
keyusage.cUsageIdentifier = 4;
|
||||
keyusage.rgpszUsageIdentifier = (LPSTR*)wincrypto_ClientOids;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root certificate
|
||||
keyusage.cUsageIdentifier = 0;
|
||||
keyusage.rgpszUsageIdentifier = NULL;
|
||||
}
|
||||
|
||||
// If we have key usages, add them to the certInfo here
|
||||
if (keyusage.cUsageIdentifier != 0)
|
||||
{
|
||||
// Set key usage. Only use (CERT_DIGITAL_SIGNATURE_KEY_USAGE| CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE) for TLS Server certificate, other usages will cause FireFox to error on the cert without an ignore button.
|
||||
BYTE ByteData = CERT_DIGITAL_SIGNATURE_KEY_USAGE| CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE; // | CERT_DATA_ENCIPHERMENT_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE;
|
||||
ZeroMemory(&keyusage2, sizeof(keyusage2));
|
||||
keyusage2.cbData = 1;
|
||||
keyusage2.pbData = &ByteData;
|
||||
|
||||
// Encode key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo2 = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, pbPolicyInfo2, &pkSize)) goto end;
|
||||
certExtension[0].pszObjId = szOID_KEY_USAGE;
|
||||
certExtension[0].fCritical = FALSE;
|
||||
certExtension[0].Value.cbData = pkSize;
|
||||
certExtension[0].Value.pbData = pbPolicyInfo2;
|
||||
|
||||
// Encode enhanced key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, (LPVOID)&keyusage, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, (LPVOID)&keyusage, pbPolicyInfo, &pkSize)) goto end;
|
||||
certExtension[1].pszObjId = szOID_ENHANCED_KEY_USAGE;
|
||||
certExtension[1].fCritical = FALSE;
|
||||
certExtension[1].Value.cbData = pkSize;
|
||||
certExtension[1].Value.pbData = pbPolicyInfo;
|
||||
|
||||
certInfo.cExtension = 2;
|
||||
certInfo.rgExtension = certExtension;
|
||||
}
|
||||
|
||||
// Sign the certificate with the MeshAgent private key
|
||||
#ifdef _WIN64
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) goto end;
|
||||
if (!CryptSignAndEncodeCertificate(hKeyNode, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, NULL, &certSize)) goto end;
|
||||
if ((certData = (BYTE*)malloc(certSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptSignAndEncodeCertificate(hKeyNode, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, certData, &certSize)) goto end;
|
||||
#else
|
||||
if (!CryptSignAndEncodeCertificate(wincrypto_hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, NULL, &certSize)) goto end;
|
||||
if ((certData = (BYTE*)malloc(certSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptSignAndEncodeCertificate(wincrypto_hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, certData, &certSize)) goto end;
|
||||
#endif
|
||||
|
||||
// Open a new temporary store.
|
||||
if ((hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL)) == NULL) goto end;
|
||||
|
||||
// Add to temporary store so we can use the PFX functions to export a store + private keys in PFX format.
|
||||
if (!CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, certData, certSize, CERT_STORE_ADD_NEW, &certContext)) goto end;
|
||||
|
||||
#ifdef _WIN64
|
||||
// Link keypair to certificate (without this the keypair gets "lost" on export).
|
||||
ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo));
|
||||
keyProviderInfo.pwszContainerName = L"MeshDummy";
|
||||
keyProviderInfo.pwszProvName = MS_KEY_STORAGE_PROVIDER;
|
||||
keyProviderInfo.dwProvType = 0;
|
||||
#ifdef _CONSOLE
|
||||
keyProviderInfo.dwFlags = 0;
|
||||
#else
|
||||
keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
keyProviderInfo.dwKeySpec = 0;
|
||||
if (!CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (LPVOID)&keyProviderInfo)) goto end;
|
||||
#else
|
||||
// Link keypair to certificate (without this the keypair gets "lost" on export).
|
||||
ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo));
|
||||
keyProviderInfo.pwszContainerName = L"MeshDummy";
|
||||
keyProviderInfo.pwszProvName = NULL;
|
||||
keyProviderInfo.dwProvType = PROV_RSA_AES;
|
||||
#ifdef _CONSOLE
|
||||
keyProviderInfo.dwFlags = 0;
|
||||
#else
|
||||
keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
keyProviderInfo.dwKeySpec = AT_KEYEXCHANGE;
|
||||
if (!CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (LPVOID)&keyProviderInfo)) goto end;
|
||||
#endif
|
||||
|
||||
// Calculate size required.
|
||||
ZeroMemory(&pfxBlob, sizeof(pfxBlob));
|
||||
if (!PFXExportCertStore(hCertStore, &pfxBlob, password, pfxExportFlags)) goto end;
|
||||
|
||||
// Export to PFX
|
||||
if ((pfxBlob.pbData = (BYTE*)malloc(pfxBlob.cbData)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!PFXExportCertStore(hCertStore, &pfxBlob, password, pfxExportFlags)) goto end;
|
||||
*data = (char*)pfxBlob.pbData;
|
||||
len = pfxBlob.cbData;
|
||||
|
||||
end:
|
||||
// Clean up everything
|
||||
#ifdef _WIN64
|
||||
if (hKey != NULL) NCryptFreeObject(hKey);
|
||||
if (hProv != NULL) NCryptFreeObject(hProv);
|
||||
#ifdef _WIN64
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
#endif
|
||||
#else
|
||||
if (hProv != NULL) CryptReleaseContext(hProv, 0);
|
||||
#endif
|
||||
if (pkInfo != NULL) free(pkInfo);
|
||||
if (hKey != NULL) CryptDestroyKey(hKey);
|
||||
if (subject1Encoded != NULL) free(subject1Encoded);
|
||||
if (subject2Encoded != NULL) free(subject2Encoded);
|
||||
if (certCtx != NULL) CertFreeCertificateContext(certCtx);
|
||||
if (hCertStore != NULL) CertCloseStore(hCertStore, 0);
|
||||
if (pbPolicyInfo != NULL) free(pbPolicyInfo);
|
||||
if (pbPolicyInfo2 != NULL) free(pbPolicyInfo2);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
696
meshcore/wincrypto.cpp
Normal file
696
meshcore/wincrypto.cpp
Normal file
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
Copyright 2006 - 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// When compiled in 32bit, this module uses Windows CAPI which is compatible with Windows XP.
|
||||
// When compiled in 64bit, this module uses Windows CNG (Crypto Next Gen) that is compatible and will use TPM modules.
|
||||
|
||||
#if !defined(_NOCAPI) && defined(WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "wincrypto.h"
|
||||
#include "../microstack/ILibParsers.h"
|
||||
HCRYPTPROV wincrypto_hProv = NULL;
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
||||
LPWSTR wincrypto_CngProviders[3] = { L"Microsoft Platform Crypto Provider", MS_KEY_STORAGE_PROVIDER, NULL };
|
||||
LPWSTR wincrypto_containername = L"MeshAgentContainer";
|
||||
LPCTSTR wincrypto_subject = "CN=MeshNodeCertificate";
|
||||
HANDLE wincrypto_hCertStore = NULL;
|
||||
PCCERT_CONTEXT wincrypto_certCtx = NULL;
|
||||
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
|
||||
|
||||
#define wincrypto_TlsServerOid "1.3.6.1.5.5.7.3.1" // TLS Server certificate
|
||||
#define wincrypto_TlsClientOid "1.3.6.1.5.5.7.3.2" // TLS Client certificate
|
||||
#define wincrypto_AmtConsoleOid "2.16.840.1.113741.1.2.1" // Intel AMT Remote Console
|
||||
#define wincrypto_AmtAgentOid "2.16.840.1.113741.1.2.2" // Intel AMT Remote Agent
|
||||
#define wincrypto_AmtSetupOid "2.16.840.1.113741.1.2.3" // Intel AMT Local Console
|
||||
const char* wincrypto_ServerOids[1] = { wincrypto_TlsServerOid };
|
||||
const char* wincrypto_ClientOids[4] = { wincrypto_TlsClientOid, wincrypto_AmtConsoleOid, wincrypto_AmtAgentOid, wincrypto_AmtSetupOid };
|
||||
|
||||
#ifndef RSA2048BIT_KEY
|
||||
#define RSA2048BIT_KEY 0x08000000
|
||||
#endif
|
||||
|
||||
#ifndef RSA3072BIT_KEY
|
||||
#define RSA3072BIT_KEY 0x0C000000
|
||||
#endif
|
||||
|
||||
void __fastcall wincrypto_setregistry(LPWSTR name, LPWSTR value)
|
||||
{
|
||||
HKEY hKey;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, save the key in LOCAL_MACHINE
|
||||
if(RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#endif
|
||||
{
|
||||
RegSetValueExW(hKey, name, 0, REG_SZ, (BYTE*)value, (DWORD)(wcslen(value) * 2));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall wincrypto_setregistryA(char* name, char* value)
|
||||
{
|
||||
HKEY hKey;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, save the key in LOCAL_MACHINE
|
||||
if(RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", &hKey) == ERROR_SUCCESS)
|
||||
#endif
|
||||
{
|
||||
RegSetValueExA(hKey, name, 0, REG_SZ, (BYTE*)value, (DWORD)strlen(value));
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_getregistry(LPCWSTR name, char** value)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD len = 0;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, open the key in LOCAL_MACHINE
|
||||
if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#endif
|
||||
if (RegQueryValueExW(hKey, name, NULL, NULL, NULL, &len ) != ERROR_SUCCESS || len == 0) { *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
if ((*value = (char*)malloc(len)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (RegQueryValueExW(hKey, name, NULL, NULL, (LPBYTE)(*value), &len ) != ERROR_SUCCESS || len == 0) { free(*value); *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
RegCloseKey(hKey);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_getregistryA(char* name, char** value)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD len = 0;
|
||||
#ifdef _WINSERVICE
|
||||
// If running as a Windows Service, open the key in LOCAL_MACHINE
|
||||
if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#else
|
||||
// If running in Console mode, save the key in CURRENT_USER
|
||||
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS ) { *value = NULL; return 0;}
|
||||
#endif
|
||||
if (RegQueryValueExA(hKey, name, NULL, NULL, NULL, &len ) != ERROR_SUCCESS || len == 0) { *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
if ((*value = (char*)malloc(len)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (RegQueryValueExA(hKey, name, NULL, NULL, (LPBYTE)(*value), &len ) != ERROR_SUCCESS || len == 0) { free(*value); *value = NULL; RegCloseKey(hKey); return 0;}
|
||||
RegCloseKey(hKey);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_isopen()
|
||||
{
|
||||
return (wincrypto_hProv != NULL && wincrypto_hCertStore != NULL && wincrypto_certCtx != NULL);
|
||||
}
|
||||
|
||||
void __fastcall wincrypto_close()
|
||||
{
|
||||
if (wincrypto_certCtx != NULL) { CertFreeCertificateContext(wincrypto_certCtx); wincrypto_certCtx = NULL; }
|
||||
if (wincrypto_hProv != NULL) { NCryptFreeObject(wincrypto_hProv); wincrypto_hProv = NULL; }
|
||||
if (wincrypto_hCertStore != NULL) { CertCloseStore(wincrypto_hCertStore, 0); wincrypto_hCertStore = NULL; }
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_open(int newcert)
|
||||
{
|
||||
DWORD KeyLength = 3072;
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
LPWSTR providerName = NULL;
|
||||
char* providerNameA = NULL;
|
||||
DWORD r = 0;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
BOOL status = FALSE;
|
||||
PBYTE subjectEncoded = NULL;
|
||||
DWORD subjectEncodedSize;
|
||||
CRYPT_KEY_PROV_INFO kpi;
|
||||
#ifdef _CONSOLE
|
||||
DWORD machineflag = 0;
|
||||
#else
|
||||
DWORD machineflag = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
|
||||
SYSTEMTIME st1;
|
||||
SYSTEMTIME st2;
|
||||
CERT_EXTENSIONS exts;
|
||||
CERT_NAME_BLOB sib;
|
||||
CRYPT_ALGORITHM_IDENTIFIER sa;
|
||||
|
||||
CERT_EXTENSION certExtension[2];
|
||||
BYTE* pbPolicyInfo2 = NULL;
|
||||
CERT_BASIC_CONSTRAINTS2_INFO BasicConstraints;
|
||||
BYTE* pbBasicConstraints = NULL;
|
||||
CRYPT_BIT_BLOB keyusage2;
|
||||
DWORD pkSize = 0;
|
||||
|
||||
ZeroMemory(&exts, sizeof(exts));
|
||||
wincrypto_close();
|
||||
|
||||
// Open the best CNG possible
|
||||
while (providerName == NULL && wincrypto_CngProviders[r] != NULL) {
|
||||
providerName = wincrypto_CngProviders[r];
|
||||
NCryptOpenStorageProvider(&wincrypto_hProv, providerName, 0);
|
||||
if (wincrypto_hProv == NULL) providerName = NULL;
|
||||
r++;
|
||||
}
|
||||
if (wincrypto_hProv == NULL) goto error;
|
||||
|
||||
// Create cert subject string in format csp understands
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, NULL, &subjectEncodedSize, NULL)) goto error;
|
||||
if ((subjectEncoded = (PBYTE)malloc(subjectEncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, subjectEncoded, &subjectEncodedSize, NULL)) goto error;
|
||||
sib.cbData = subjectEncodedSize;
|
||||
sib.pbData = subjectEncoded;
|
||||
|
||||
#ifdef _CONSOLE
|
||||
wincrypto_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, wincrypto_hProv, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG, L"MY"); // CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
||||
if (!wincrypto_hCertStore) goto error;
|
||||
#else
|
||||
wincrypto_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, wincrypto_hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG, L"MY"); // CERT_STORE_NO_CRYPT_RELEASE_FLAG // CERT_SYSTEM_STORE_LOCAL_MACHINE
|
||||
if (!wincrypto_hCertStore) goto error;
|
||||
#endif
|
||||
|
||||
// Look for cert and if exists, delete it
|
||||
wincrypto_certCtx = CertFindCertificateInStore(wincrypto_hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &sib, NULL );
|
||||
|
||||
// Check if we can get the private key
|
||||
if (wincrypto_certCtx != NULL)
|
||||
{
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) { newcert = 1; }
|
||||
if (hKeyNodeSpec != CERT_NCRYPT_KEY_SPEC) { newcert = 1; } // If this private key is not CNG, don't use it.
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
}
|
||||
|
||||
// Check if have a certificate already, or need to create a new one
|
||||
if (wincrypto_certCtx != NULL && newcert == 0) goto end;
|
||||
if (wincrypto_certCtx) { status = CertDeleteCertificateFromStore(wincrypto_certCtx); if (!status) goto error; wincrypto_certCtx = NULL; }
|
||||
|
||||
// Generate node RSA key-pair
|
||||
#ifdef _CONSOLE
|
||||
if (FAILED(status = NCryptCreatePersistedKey(wincrypto_hProv, &hKeyNode, BCRYPT_RSA_ALGORITHM, wincrypto_containername, 0, NCRYPT_OVERWRITE_KEY_FLAG))) goto error;
|
||||
#else
|
||||
if (FAILED(status = NCryptCreatePersistedKey(wincrypto_hProv, &hKeyNode, BCRYPT_RSA_ALGORITHM, wincrypto_containername, 0, NCRYPT_MACHINE_KEY_FLAG | NCRYPT_OVERWRITE_KEY_FLAG))) goto error;
|
||||
#endif
|
||||
if (FAILED(status = NCryptSetProperty(hKeyNode, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) {
|
||||
KeyLength = 2048; // If 3072 is not supported, go down to 2048.
|
||||
if (FAILED(status = NCryptSetProperty(hKeyNode, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) { goto error; }
|
||||
}
|
||||
if (FAILED(status = NCryptFinalizeKey(hKeyNode, NCRYPT_SILENT_FLAG))) { goto error; } // Ask for silent create, this will fail if not admin.
|
||||
|
||||
// Create self signed cert
|
||||
ZeroMemory(&kpi, sizeof(kpi));
|
||||
kpi.pwszContainerName = wincrypto_containername;
|
||||
kpi.pwszProvName = providerName;
|
||||
kpi.dwProvType = 0;
|
||||
kpi.dwFlags = 0;
|
||||
kpi.dwKeySpec = 0;
|
||||
|
||||
ZeroMemory(&certExtension, sizeof(certExtension));
|
||||
|
||||
// Set key usage for root certificate
|
||||
BYTE ByteData = CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_OFFLINE_CRL_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE;
|
||||
ZeroMemory(&keyusage2, sizeof(keyusage2));
|
||||
keyusage2.cbData = 1;
|
||||
keyusage2.pbData = &ByteData;
|
||||
|
||||
// Encode key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo2 = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, pbPolicyInfo2, &pkSize)) goto end;
|
||||
certExtension[0].pszObjId = szOID_KEY_USAGE;
|
||||
certExtension[0].fCritical = FALSE;
|
||||
certExtension[0].Value.cbData = pkSize;
|
||||
certExtension[0].Value.pbData = pbPolicyInfo2;
|
||||
|
||||
// Encode Basic Constraints
|
||||
// --> Subject Type=CA
|
||||
// --> Path Length Constraint=None
|
||||
|
||||
ZeroMemory(&BasicConstraints, sizeof(BasicConstraints));
|
||||
BasicConstraints.fCA = TRUE;
|
||||
BasicConstraints.fPathLenConstraint = FALSE;
|
||||
BasicConstraints.dwPathLenConstraint = 0;
|
||||
|
||||
// Get Basic Constraints blob size*/
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, (LPVOID)&BasicConstraints, NULL, &pkSize)) goto end;
|
||||
if ((pbBasicConstraints = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, (LPVOID)&BasicConstraints, pbBasicConstraints, &pkSize)) goto end;
|
||||
|
||||
// Set Basic Constraints extension
|
||||
certExtension[1].pszObjId = szOID_BASIC_CONSTRAINTS2;
|
||||
certExtension[1].fCritical = FALSE;
|
||||
certExtension[1].Value.cbData = pkSize;
|
||||
certExtension[1].Value.pbData = pbBasicConstraints;
|
||||
|
||||
exts.cExtension = 2;
|
||||
exts.rgExtension = certExtension;
|
||||
|
||||
// Setup certificate expiration
|
||||
GetSystemTime(&st1);
|
||||
GetSystemTime(&st2);
|
||||
st1.wYear -= 1; // Starts a year ago
|
||||
st2.wYear += 30; // Expires 30 years from now
|
||||
|
||||
ZeroMemory(&sa, sizeof(sa));
|
||||
sa.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
|
||||
wincrypto_certCtx = CertCreateSelfSignCertificate(NULL, &sib, 0, &kpi, &sa, &st1, &st2, &exts);
|
||||
if (!wincrypto_certCtx) { goto error; }
|
||||
|
||||
// Note this is a different context to certCtx, this ctx is the in-store ctx
|
||||
status = CertAddCertificateContextToStore(wincrypto_hCertStore, wincrypto_certCtx, CERT_STORE_ADD_REPLACE_EXISTING, &wincrypto_certCtx);
|
||||
if (!status || wincrypto_certCtx == NULL) goto error;
|
||||
|
||||
// Get the selected provider name and save it in the registry
|
||||
if (providerName != NULL) wincrypto_setregistry(L"KeyStore", providerName);
|
||||
|
||||
goto end;
|
||||
|
||||
error:
|
||||
// Clean up
|
||||
if (hKeyNode != NULL) NCryptFreeObject(hKeyNode);
|
||||
wincrypto_close();
|
||||
return 1;
|
||||
|
||||
end:
|
||||
if (subjectEncoded != NULL) free(subjectEncoded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __fastcall wincrypto_random(int length, char* result)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
if(!NT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0))) goto Cleanup;
|
||||
if(!NT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)result, length, BCRYPT_RNG_USE_ENTROPY_IN_BUFFER))) goto Cleanup;
|
||||
Cleanup:
|
||||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||||
}
|
||||
|
||||
// Perform cryptographic hash
|
||||
int __fastcall wincrypto_hash(LPCWSTR alg, char* data, int datalen, char* result, DWORD resultlen)
|
||||
{
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
BCRYPT_HASH_HANDLE hHash = NULL;
|
||||
DWORD cbData = 0, cbHashObject = 0;
|
||||
PBYTE pbHashObject = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, alg, NULL, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0))) goto Cleanup;
|
||||
if ((pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject)) == NULL) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptCreateHash(hAlg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptHashData(hHash, (PBYTE)data, datalen, 0))) goto Cleanup;
|
||||
if (!NT_SUCCESS(BCryptFinishHash(hHash, (PUCHAR)result, resultlen, 0))) goto Cleanup;
|
||||
r = resultlen;
|
||||
Cleanup:
|
||||
if (hHash) BCryptDestroyHash(hHash);
|
||||
if (pbHashObject) HeapFree(GetProcessHeap(), 0, pbHashObject);
|
||||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg,0);
|
||||
return r;
|
||||
}
|
||||
|
||||
int __fastcall wincrypto_md5(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_MD5_ALGORITHM, data, datalen, result, 16); }
|
||||
int __fastcall wincrypto_sha256(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_SHA256_ALGORITHM, data, datalen, result, 32); }
|
||||
int __fastcall wincrypto_sha384(char* data, int datalen, char* result) { return wincrypto_hash(BCRYPT_SHA384_ALGORITHM, data, datalen, result, 32); }
|
||||
|
||||
// Sign the data with the Mesh Agent certificate and return a PKCS7 result.
|
||||
int __fastcall wincrypto_sign(char* data, int len, char** signature)
|
||||
{
|
||||
int signatureLen = 0;
|
||||
DWORD cbSignedBlob;
|
||||
HCRYPTMSG hMsg = NULL;
|
||||
DWORD HashAlgSize;
|
||||
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
|
||||
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo;
|
||||
CERT_BLOB SignerCertBlob;
|
||||
CERT_BLOB SignerCertBlobArray[1];
|
||||
CMSG_SIGNER_ENCODE_INFO SignerEncodeInfoArray[1];
|
||||
CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo;
|
||||
int r = 0;
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
// Initialize the algorithm identifier structure.
|
||||
HashAlgSize = sizeof(HashAlgorithm);
|
||||
memset(&HashAlgorithm, 0, HashAlgSize); // Initialize to zero
|
||||
HashAlgorithm.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
|
||||
// Initialize the CMSG_SIGNER_ENCODE_INFO structure.
|
||||
memset(&SignerEncodeInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
|
||||
SignerEncodeInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) { r = 10; goto end; }
|
||||
SignerEncodeInfo.hNCryptKey = hKeyNode;
|
||||
SignerEncodeInfo.pCertInfo = wincrypto_certCtx->pCertInfo;
|
||||
SignerEncodeInfo.dwKeySpec = AT_KEYEXCHANGE;
|
||||
SignerEncodeInfo.HashAlgorithm = HashAlgorithm;
|
||||
SignerEncodeInfo.pvHashAuxInfo = NULL;
|
||||
|
||||
// Create an array of one.
|
||||
SignerEncodeInfoArray[0] = SignerEncodeInfo;
|
||||
|
||||
// Initialize the CMSG_SIGNED_ENCODE_INFO structure.
|
||||
SignerCertBlob.cbData = wincrypto_certCtx->cbCertEncoded;
|
||||
SignerCertBlob.pbData = wincrypto_certCtx->pbCertEncoded;
|
||||
|
||||
// Initialize the array of one CertBlob.
|
||||
SignerCertBlobArray[0] = SignerCertBlob;
|
||||
memset(&SignedMsgEncodeInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO));
|
||||
SignedMsgEncodeInfo.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO);
|
||||
SignedMsgEncodeInfo.cSigners = 1;
|
||||
SignedMsgEncodeInfo.rgSigners = SignerEncodeInfoArray;
|
||||
SignedMsgEncodeInfo.cCertEncoded = 1;
|
||||
SignedMsgEncodeInfo.rgCertEncoded = SignerCertBlobArray;
|
||||
SignedMsgEncodeInfo.rgCrlEncoded = NULL;
|
||||
|
||||
// Get the size of the encoded, signed message BLOB.
|
||||
cbSignedBlob = CryptMsgCalculateEncodedLength(MY_ENCODING_TYPE, 0, CMSG_SIGNED, &SignedMsgEncodeInfo, NULL, len);
|
||||
if (cbSignedBlob == 0) goto end;
|
||||
|
||||
// Allocate memory for the encoded BLOB.
|
||||
if ((*signature = (char*)malloc(cbSignedBlob)) == NULL) ILIBCRITICALEXIT(254);
|
||||
|
||||
// Open a message to encode.
|
||||
hMsg = CryptMsgOpenToEncode(MY_ENCODING_TYPE, 0, CMSG_SIGNED, &SignedMsgEncodeInfo, NULL, NULL);
|
||||
|
||||
// Update the message with the data.
|
||||
if (!CryptMsgUpdate(hMsg, (BYTE*)data, len, TRUE)) goto end;
|
||||
|
||||
// Get the resulting message.
|
||||
if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, (BYTE*)*signature, &cbSignedBlob)) goto end;
|
||||
signatureLen = cbSignedBlob;
|
||||
|
||||
end:
|
||||
// Clean up
|
||||
if (hMsg) CryptMsgClose(hMsg);
|
||||
if (signatureLen == 0 && *signature != NULL) { free(*signature); *signature = NULL; }
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
return signatureLen;
|
||||
}
|
||||
|
||||
struct wincrypto_stream {
|
||||
char* buf;
|
||||
int buflen;
|
||||
int ptr;
|
||||
};
|
||||
|
||||
static BOOL WINAPI wincrypto_CmsgStreamOutputCallback(IN const void *pvArg, IN BYTE *pbData, IN DWORD cbData, IN BOOL fFinal)
|
||||
{
|
||||
struct wincrypto_stream* StreamArg = (struct wincrypto_stream*)pvArg;
|
||||
UNREFERENCED_PARAMETER(fFinal);
|
||||
|
||||
if ((int)(StreamArg->ptr + cbData) > (int)StreamArg->buflen) return FALSE;
|
||||
memcpy(StreamArg->buf + StreamArg->ptr, pbData, cbData);
|
||||
StreamArg->ptr += cbData;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Decrypt the PKCS7 block and return the content.
|
||||
int __fastcall wincrypto_decrypt(char* encdata, int encdatalen, char** data)
|
||||
{
|
||||
int datalen = 0;
|
||||
HCRYPTMSG hMsg = NULL;
|
||||
CMSG_CTRL_DECRYPT_PARA decryptPara = { sizeof(CMSG_CTRL_DECRYPT_PARA) };
|
||||
BOOL flagHandle = FALSE;
|
||||
CMSG_STREAM_INFO StreamInfo;
|
||||
struct wincrypto_stream StreamArg;
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
// Perform setup
|
||||
if ((StreamArg.buf = (char*)malloc(encdatalen)) == NULL) ILIBCRITICALEXIT(254);
|
||||
StreamArg.buflen = encdatalen;
|
||||
StreamArg.ptr = 0;
|
||||
StreamInfo.cbContent= CMSG_INDEFINITE_LENGTH;
|
||||
StreamInfo.pfnStreamOutput = wincrypto_CmsgStreamOutputCallback;
|
||||
StreamInfo.pvArg = &StreamArg;
|
||||
if ((hMsg = CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, 0, NULL, NULL, &StreamInfo)) == NULL) goto end;
|
||||
if (!CryptMsgUpdate(hMsg, (BYTE*)encdata, encdatalen, TRUE)) goto end;
|
||||
|
||||
// Setup the certificate
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &decryptPara.hCryptProv, &decryptPara.dwKeySpec, &flagHandle)) goto end;
|
||||
decryptPara.dwRecipientIndex = 0;
|
||||
|
||||
// Perform the decrypt
|
||||
if (!CryptMsgControl(hMsg, 0, CMSG_CTRL_DECRYPT, &decryptPara)) goto end;
|
||||
datalen = StreamArg.ptr;
|
||||
*data = StreamArg.buf;
|
||||
StreamArg.buf = NULL;
|
||||
|
||||
end:
|
||||
// Clean up
|
||||
if (hMsg) CryptMsgClose(hMsg);
|
||||
if (StreamArg.buf != NULL) { free(StreamArg.buf); }
|
||||
return datalen;
|
||||
}
|
||||
|
||||
// Get the X509 certificate including the public key (Direct reference, no need to free this).
|
||||
int __fastcall wincrypto_getcert(char** data)
|
||||
{
|
||||
if (wincrypto_certCtx == NULL) { *data = NULL; return 0; }
|
||||
*data = (char*)wincrypto_certCtx->pbCertEncoded;
|
||||
return (int)wincrypto_certCtx->cbCertEncoded;
|
||||
}
|
||||
|
||||
// Create an X509, RSA 3027bit certificate with the MeshAgent certificate as signing root.
|
||||
int __fastcall wincrypto_mkCert(wchar_t* subject, int certtype, wchar_t* password, char** data)
|
||||
{
|
||||
NCRYPT_KEY_HANDLE hKeyNode = NULL;
|
||||
DWORD hKeyNodeSpec = 0;
|
||||
BOOL hFreeKeyNode = FALSE;
|
||||
NCRYPT_KEY_HANDLE hNewKey = NULL;
|
||||
DWORD KeyLength = 3072;
|
||||
DWORD KeyPolicy = NCRYPT_ALLOW_EXPORT_FLAG;
|
||||
int len = 0;
|
||||
HCRYPTKEY hKey = NULL;
|
||||
PCCERT_CONTEXT certCtx = NULL;
|
||||
HCRYPTPROV hProv = NULL;
|
||||
CRYPT_KEY_PROV_INFO keyProviderInfo;
|
||||
|
||||
// Issuer and cert subject names
|
||||
CERT_NAME_BLOB sib1;
|
||||
CERT_NAME_BLOB sib2;
|
||||
PBYTE subject1Encoded = NULL;
|
||||
DWORD subject1EncodedSize;
|
||||
PBYTE subject2Encoded = NULL;
|
||||
DWORD subject2EncodedSize;
|
||||
|
||||
CRYPT_KEY_PROV_INFO kpi;
|
||||
BYTE* pbPolicyInfo = NULL;
|
||||
BYTE* pbPolicyInfo2 = NULL;
|
||||
|
||||
CERT_EXTENSION certExtension[2];
|
||||
CERT_ENHKEY_USAGE keyusage;
|
||||
CRYPT_BIT_BLOB keyusage2;
|
||||
//CERT_BASIC_CONSTRAINTS2_INFO BasicConstraints;
|
||||
//BYTE* pbBasicConstraints = NULL;
|
||||
|
||||
DWORD pkSize = 0;
|
||||
PCERT_PUBLIC_KEY_INFO pkInfo = NULL;
|
||||
|
||||
CERT_INFO certInfo;
|
||||
SYSTEMTIME systemTime;
|
||||
FILETIME notBefore;
|
||||
FILETIME notAfter;
|
||||
BYTE serialNumber[8];
|
||||
DWORD certSize = 0xFFFFFFFF;
|
||||
BYTE *certData = NULL;
|
||||
|
||||
HCERTSTORE hCertStore = NULL;
|
||||
PCCERT_CONTEXT certContext = NULL;
|
||||
CRYPT_DATA_BLOB pfxBlob;
|
||||
DWORD pfxExportFlags = EXPORT_PRIVATE_KEYS; // | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY;
|
||||
|
||||
// Check that we have open context
|
||||
if (wincrypto_hProv == NULL || wincrypto_hCertStore == NULL || wincrypto_certCtx == NULL) return 0;
|
||||
|
||||
*data = NULL;
|
||||
ZeroMemory(&kpi, sizeof(kpi));
|
||||
ZeroMemory(&certExtension, sizeof(certExtension));
|
||||
|
||||
// Generate node RSA key-pair
|
||||
if (FAILED(NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) goto end;
|
||||
#ifdef _CONSOLE
|
||||
if (FAILED(NCryptCreatePersistedKey(hProv, &hNewKey, BCRYPT_RSA_ALGORITHM, L"MeshDummy", AT_KEYEXCHANGE, NCRYPT_OVERWRITE_KEY_FLAG))) goto end;
|
||||
#else
|
||||
if (FAILED(NCryptCreatePersistedKey(hProv, &hNewKey, BCRYPT_RSA_ALGORITHM, L"MeshDummy", AT_KEYEXCHANGE, NCRYPT_MACHINE_KEY_FLAG | NCRYPT_OVERWRITE_KEY_FLAG))) goto end;
|
||||
#endif
|
||||
if (FAILED(NCryptSetProperty(hNewKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) {
|
||||
KeyLength = 2048; // If 3072 is not supported, go down to 2048.
|
||||
if (FAILED(NCryptSetProperty(hNewKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&KeyLength, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) { goto end; }
|
||||
}
|
||||
if (FAILED(NCryptSetProperty(hNewKey, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&KeyPolicy, 4, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG))) goto end;
|
||||
if (FAILED(NCryptFinalizeKey(hNewKey, NCRYPT_SILENT_FLAG))) goto end;
|
||||
if (!CryptExportPublicKeyInfo(hNewKey, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, &pkSize)) goto end;
|
||||
if ((pkInfo = (PCERT_PUBLIC_KEY_INFO)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptExportPublicKeyInfo(hNewKey, AT_KEYEXCHANGE, X509_ASN_ENCODING, pkInfo, &pkSize)) goto end;
|
||||
|
||||
// Create cert issuer string in format the CSP understands
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, NULL, &subject1EncodedSize, NULL)) goto end;
|
||||
if ((subject1Encoded = (PBYTE)malloc(subject1EncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToName(X509_ASN_ENCODING, wincrypto_subject, CERT_X500_NAME_STR, NULL, subject1Encoded, &subject1EncodedSize, NULL)) goto end;
|
||||
sib1.cbData = subject1EncodedSize;
|
||||
sib1.pbData = subject1Encoded;
|
||||
|
||||
// Create cert subject string in format the CSP understands
|
||||
if (!CertStrToNameW(X509_ASN_ENCODING, subject, CERT_X500_NAME_STR, NULL, NULL, &subject2EncodedSize, NULL)) goto end;
|
||||
if ((subject2Encoded = (PBYTE)malloc(subject2EncodedSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CertStrToNameW(X509_ASN_ENCODING, subject, CERT_X500_NAME_STR, NULL, subject2Encoded, &subject2EncodedSize, NULL)) goto end;
|
||||
sib2.cbData = subject2EncodedSize;
|
||||
sib2.pbData = subject2Encoded;
|
||||
|
||||
// Setup the certificate validity period
|
||||
ZeroMemory(&certInfo, sizeof(certInfo));
|
||||
GetSystemTime(&systemTime);
|
||||
systemTime.wYear -= 1;
|
||||
SystemTimeToFileTime(&systemTime, ¬Before);
|
||||
systemTime.wYear += 30;
|
||||
SystemTimeToFileTime(&systemTime, ¬After);
|
||||
|
||||
// Generate a serial number.
|
||||
wincrypto_random(8, (char*)serialNumber);
|
||||
|
||||
// Setup all of the certificate information
|
||||
certInfo.dwVersion = CERT_V3;
|
||||
certInfo.SerialNumber.cbData = 8;
|
||||
certInfo.SerialNumber.pbData = serialNumber;
|
||||
certInfo.SignatureAlgorithm.pszObjId = szOID_RSA_SHA384RSA; // Using SHA384
|
||||
certInfo.Issuer = sib1;
|
||||
certInfo.NotBefore = notBefore;
|
||||
certInfo.NotAfter = notAfter;
|
||||
certInfo.Subject = sib2;
|
||||
certInfo.SubjectPublicKeyInfo = *pkInfo;
|
||||
|
||||
// Setup key usage
|
||||
if (certtype == 1)
|
||||
{
|
||||
// TLS Server certificate
|
||||
keyusage.cUsageIdentifier = 1;
|
||||
keyusage.rgpszUsageIdentifier = (LPSTR*)wincrypto_ServerOids;
|
||||
}
|
||||
else if (certtype == 2)
|
||||
{
|
||||
// TLS Client certificate
|
||||
keyusage.cUsageIdentifier = 4;
|
||||
keyusage.rgpszUsageIdentifier = (LPSTR*)wincrypto_ClientOids;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root certificate
|
||||
keyusage.cUsageIdentifier = 0;
|
||||
keyusage.rgpszUsageIdentifier = NULL;
|
||||
}
|
||||
|
||||
// If we have key usages, add them to the certInfo here
|
||||
if (keyusage.cUsageIdentifier != 0)
|
||||
{
|
||||
// Set key usage. Only use (CERT_DIGITAL_SIGNATURE_KEY_USAGE| CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE) for TLS Server certificate, other usages will cause FireFox to error on the cert without an ignore button.
|
||||
BYTE ByteData = CERT_DIGITAL_SIGNATURE_KEY_USAGE| CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE; // | CERT_DATA_ENCIPHERMENT_KEY_USAGE | CERT_NON_REPUDIATION_KEY_USAGE | CERT_KEY_CERT_SIGN_KEY_USAGE | CERT_CRL_SIGN_KEY_USAGE;
|
||||
ZeroMemory(&keyusage2, sizeof(keyusage2));
|
||||
keyusage2.cbData = 1;
|
||||
keyusage2.pbData = &ByteData;
|
||||
|
||||
// Encode key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo2 = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE, (LPVOID)&keyusage2, pbPolicyInfo2, &pkSize)) goto end;
|
||||
certExtension[0].pszObjId = szOID_KEY_USAGE;
|
||||
certExtension[0].fCritical = FALSE;
|
||||
certExtension[0].Value.cbData = pkSize;
|
||||
certExtension[0].Value.pbData = pbPolicyInfo2;
|
||||
|
||||
// Encode enhanced key usage
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, (LPVOID)&keyusage, NULL, &pkSize)) goto end;
|
||||
if ((pbPolicyInfo = (BYTE*)malloc(pkSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, (LPVOID)&keyusage, pbPolicyInfo, &pkSize)) goto end;
|
||||
certExtension[1].pszObjId = szOID_ENHANCED_KEY_USAGE;
|
||||
certExtension[1].fCritical = FALSE;
|
||||
certExtension[1].Value.cbData = pkSize;
|
||||
certExtension[1].Value.pbData = pbPolicyInfo;
|
||||
|
||||
certInfo.cExtension = 2;
|
||||
certInfo.rgExtension = certExtension;
|
||||
}
|
||||
|
||||
// Sign the certificate with the MeshAgent private key
|
||||
if (!CryptAcquireCertificatePrivateKey(wincrypto_certCtx, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hKeyNode, &hKeyNodeSpec, &hFreeKeyNode)) goto end;
|
||||
if (!CryptSignAndEncodeCertificate(hKeyNode, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, NULL, &certSize)) goto end;
|
||||
if ((certData = (BYTE*)malloc(certSize)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!CryptSignAndEncodeCertificate(hKeyNode, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, certData, &certSize)) goto end;
|
||||
|
||||
// Open a new temporary store.
|
||||
if ((hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL)) == NULL) goto end;
|
||||
|
||||
// Add to temporary store so we can use the PFX functions to export a store + private keys in PFX format.
|
||||
if (!CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, certData, certSize, CERT_STORE_ADD_NEW, &certContext)) goto end;
|
||||
|
||||
// Link keypair to certificate (without this the keypair gets "lost" on export).
|
||||
ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo));
|
||||
keyProviderInfo.pwszContainerName = L"MeshDummy";
|
||||
keyProviderInfo.pwszProvName = MS_KEY_STORAGE_PROVIDER;
|
||||
keyProviderInfo.dwProvType = 0;
|
||||
#ifdef _CONSOLE
|
||||
keyProviderInfo.dwFlags = 0;
|
||||
#else
|
||||
keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET;
|
||||
#endif
|
||||
keyProviderInfo.dwKeySpec = 0;
|
||||
if (!CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (LPVOID)&keyProviderInfo)) goto end;
|
||||
|
||||
// Calculate size required.
|
||||
ZeroMemory(&pfxBlob, sizeof(pfxBlob));
|
||||
if (!PFXExportCertStore(hCertStore, &pfxBlob, password, pfxExportFlags)) goto end;
|
||||
|
||||
// Export to PFX
|
||||
if ((pfxBlob.pbData = (BYTE*)malloc(pfxBlob.cbData)) == NULL) ILIBCRITICALEXIT(254);
|
||||
if (!PFXExportCertStore(hCertStore, &pfxBlob, password, pfxExportFlags)) goto end;
|
||||
*data = (char*)pfxBlob.pbData;
|
||||
len = pfxBlob.cbData;
|
||||
|
||||
end:
|
||||
// Clean up everything
|
||||
if (hKey != NULL) NCryptFreeObject(hKey);
|
||||
if (hProv != NULL) NCryptFreeObject(hProv);
|
||||
if (hFreeKeyNode && hKeyNode != NULL) { if (hKeyNodeSpec == CERT_NCRYPT_KEY_SPEC) NCryptFreeObject(hKeyNode); else CryptReleaseContext(hKeyNode, 0); }
|
||||
if (pkInfo != NULL) free(pkInfo);
|
||||
if (hKey != NULL) CryptDestroyKey(hKey);
|
||||
if (subject1Encoded != NULL) free(subject1Encoded);
|
||||
if (subject2Encoded != NULL) free(subject2Encoded);
|
||||
if (certCtx != NULL) CertFreeCertificateContext(certCtx);
|
||||
if (hCertStore != NULL) CertCloseStore(hCertStore, 0);
|
||||
if (pbPolicyInfo != NULL) free(pbPolicyInfo);
|
||||
if (pbPolicyInfo2 != NULL) free(pbPolicyInfo2);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
37
meshcore/wincrypto.h
Normal file
37
meshcore/wincrypto.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2006 - 2015 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void __fastcall wincrypto_setregistry(LPWSTR name, LPWSTR value);
|
||||
void __fastcall wincrypto_setregistryA(char* name, char* value);
|
||||
int __fastcall wincrypto_getregistry(LPCWSTR name, char** value);
|
||||
int __fastcall wincrypto_getregistryA(char* name, char** value);
|
||||
int __fastcall wincrypto_isopen();
|
||||
void __fastcall wincrypto_close();
|
||||
int __fastcall wincrypto_open(int newcert);
|
||||
void __fastcall wincrypto_random(int length, char* result);
|
||||
int __fastcall wincrypto_md5(char* data, int datalen, char* result);
|
||||
int __fastcall wincrypto_sha256(char* data, int datalen, char* result);
|
||||
int __fastcall wincrypto_sha384(char* data, int datalen, char* result);
|
||||
int __fastcall wincrypto_sign(char* data, int len, char** signature);
|
||||
int __fastcall wincrypto_decrypt(char* encdata, int encdatalen, char** data);
|
||||
int __fastcall wincrypto_getcert(char** data);
|
||||
int __fastcall wincrypto_mkCert(wchar_t* subject, int certtype, wchar_t* password, char** data); // certtype: 1=Root, 2=Server, 3=Client
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user