657 lines
18 KiB
C
657 lines
18 KiB
C
#ifdef MOD_WINCRYPT
|
|
/*
|
|
* PuTTY wincrypt patch main file.
|
|
* Author: Ulf Frisk, puttywincrypt@ulffrisk.com
|
|
*/
|
|
#include "putty.h"
|
|
#include "ssh.h"
|
|
#include "mpint.h"
|
|
|
|
#include <windows.h>
|
|
#include <wincrypt.h>
|
|
#include "bcrypt.h"
|
|
#include "ncrypt.h"
|
|
#include "wincrypt/wincrypto.h"
|
|
|
|
/*
|
|
* Defines and declarations due to missing declarations in mingw32 and BCC55 headers.
|
|
*/
|
|
#ifndef CERT_SYSTEM_STORE_CURRENT_USER
|
|
#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
|
|
#endif /* CERT_SYSTEM_STORE_CURRENT_USER */
|
|
|
|
#ifndef CERT_STORE_PROV_MEMORY
|
|
#define CERT_STORE_PROV_MEMORY ((LPCSTR) 2)
|
|
#endif /* CERT_STORE_PROV_MEMORY */
|
|
|
|
#ifndef CRYPT_FIND_USER_KEYSET_FLAG
|
|
#define CRYPT_FIND_USER_KEYSET_FLAG 0x00000001
|
|
#endif /* CRYPT_FIND_USER_KEYSET_FLAG */
|
|
|
|
#ifndef CRYPT_FIND_SILENT_KEYSET_FLAG
|
|
#define CRYPT_FIND_SILENT_KEYSET_FLAG 0x00000040
|
|
#endif /* CRYPT_FIND_SILENT_KEYSET_FLAG */
|
|
|
|
#ifndef CERT_CLOSE_STORE_FORCE_FLAG
|
|
#define CERT_CLOSE_STORE_FORCE_FLAG 0x00000001
|
|
#endif /* CERT_CLOSE_STORE_FORCE_FLAG */
|
|
|
|
#ifndef CRYPT_ACQUIRE_NO_HEALING
|
|
#define CRYPT_ACQUIRE_NO_HEALING 0x00000008
|
|
#endif /* CRYPT_ACQUIRE_NO_HEALING */
|
|
|
|
#ifndef CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG
|
|
#define CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG 0x00010000
|
|
#endif /* CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG */
|
|
|
|
#ifndef CERT_NCRYPT_KEY_SPEC
|
|
#define CERT_NCRYPT_KEY_SPEC 0xFFFFFFFF
|
|
#endif /* CERT_NCRYPT_KEY_SPEC */
|
|
|
|
typedef ULONG_PTR HCRYPTPROV_OR_NCRYPT_KEY_HANDLE;
|
|
|
|
#ifndef __BCRYPT_H__
|
|
#define BCRYPT_PAD_PKCS1 0x00000002
|
|
#define BCRYPT_SHA1_ALGORITHM L"SHA1"
|
|
typedef struct _BCRYPT_PKCS1_PADDING_INFO
|
|
{
|
|
LPCWSTR pszAlgId;
|
|
} BCRYPT_PKCS1_PADDING_INFO;
|
|
#endif /* __BCRYPT_H__ */
|
|
|
|
#ifndef CRYPT_ACQUIRE_SILENT_FLAG
|
|
#define CRYPT_ACQUIRE_SILENT_FLAG 0x00000040
|
|
#endif /* CRYPT_ACQUIRE_SILENT_FLAG */
|
|
|
|
typedef PCCERT_CONTEXT(WINAPI *DFNCryptUIDlgSelectCertificateFromStore)(HCERTSTORE, HWND, LPCWSTR, LPCWSTR, DWORD, DWORD, PVOID);
|
|
|
|
/*
|
|
* Dynamically lookup NCryptSignHash to avoid link dependency to ncrypt.dll (not supported by Windows XP).
|
|
*/
|
|
typedef LONG(WINAPI *DFNNCryptSignHash)(ULONG_PTR, PVOID, PBYTE, DWORD, PBYTE, DWORD, PDWORD, DWORD);
|
|
|
|
/*
|
|
* convert sha1 string to binary data
|
|
*/
|
|
void capi_sha1_to_binary(PSTR szHex, PBYTE pbBin)
|
|
{
|
|
unsigned char i, h, l;
|
|
for (i = 0; i < 20; i++) {
|
|
h = szHex[i << 1];
|
|
l = szHex[(i << 1) + 1];
|
|
pbBin[i] =
|
|
(((h >= '0' && h <= '9') ? h - '0' : ((h >= 'a' && h <= 'f') ? h - 'a' + 10 : ((h >= 'A' && h <= 'F') ? h - 'A' + 10 : 0))) << 4) +
|
|
(((l >= '0' && l <= '9') ? l - '0' : ((l >= 'a' && l <= 'f') ? l - 'a' + 10 : ((l >= 'A' && l <= 'F') ? l - 'A' + 10 : 0))));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Windows XP do not support CryptBinaryToString with raw hex.
|
|
*/
|
|
PSTR capi_binary_to_hex(PBYTE pbBinary, DWORD cbBinary)
|
|
{
|
|
PSTR szHex;
|
|
DWORD idx;
|
|
BYTE b;
|
|
szHex = snewn((cbBinary << 1) + 1, char);
|
|
szHex[cbBinary << 1] = 0;
|
|
for (idx = 0; idx < cbBinary; idx++) {
|
|
b = (pbBinary[idx] >> 4) & 0x0F;
|
|
szHex[idx << 1] = (b < 10) ? b + '0' : b - 10 + 'a';
|
|
b = pbBinary[idx] & 0x0F;
|
|
szHex[(idx << 1) + 1] = (b < 10) ? b + '0' : b - 10 + 'a';
|
|
}
|
|
return szHex;
|
|
}
|
|
|
|
/*
|
|
* Reverse a byte array.
|
|
*/
|
|
void capi_reverse_array(PBYTE pb, DWORD cb)
|
|
{
|
|
DWORD i;
|
|
BYTE t;
|
|
for (i = 0; i < cb >> 1; i++) {
|
|
t = pb[i];
|
|
pb[i] = pb[cb - i - 1];
|
|
pb[cb - i - 1] = t;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Select a certificate given the criteria provided.
|
|
* If a criterion is absent it will be disregarded.
|
|
*/
|
|
void capi_select_cert_2(PBYTE pbSHA1, LPWSTR wszCN, PCCERT_CONTEXT *ppCertCtx, HCERTSTORE *phStore)
|
|
{
|
|
HCERTSTORE hStoreMY = NULL, hStoreTMP = NULL;
|
|
PCCERT_CONTEXT pCertCtx = NULL;
|
|
HMODULE hCryptUIDLL = NULL;
|
|
DFNCryptUIDlgSelectCertificateFromStore dfnCryptUIDlgSelectCertificateFromStore;
|
|
CRYPT_HASH_BLOB cryptHashBlob;
|
|
DWORD dwCertCount = 0;
|
|
if (!(hStoreMY = CertOpenStore((LPCSTR)CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"))) {
|
|
goto error;
|
|
}
|
|
if (pbSHA1) {
|
|
cryptHashBlob.cbData = 20;
|
|
cryptHashBlob.pbData = pbSHA1;
|
|
if ((*ppCertCtx = CertFindCertificateInStore(hStoreMY, X509_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &cryptHashBlob, pCertCtx))) {
|
|
*phStore = hStoreMY;
|
|
return;
|
|
} else {
|
|
goto error;
|
|
}
|
|
}
|
|
if (!(hStoreTMP = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, NULL))) {
|
|
goto error;
|
|
}
|
|
while (TRUE) {
|
|
if (wszCN) {
|
|
pCertCtx = CertFindCertificateInStore(hStoreMY, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, wszCN, pCertCtx);
|
|
} else {
|
|
pCertCtx = CertEnumCertificatesInStore(hStoreMY, pCertCtx);
|
|
}
|
|
if (!pCertCtx) {
|
|
break;
|
|
}
|
|
/*
|
|
if (!CryptAcquireCertificatePrivateKey(pCertCtx, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_NO_HEALING | CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hCryptProvOrNCryptKey, &dwKeySpec, &fCallerFreeProvAlwaysFalse)) {
|
|
continue;
|
|
}
|
|
*/
|
|
dwCertCount++;
|
|
CertAddCertificateContextToStore(hStoreTMP, pCertCtx, CERT_STORE_ADD_ALWAYS, NULL);
|
|
}
|
|
CertCloseStore(hStoreMY, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
hStoreMY = NULL;
|
|
if (dwCertCount == 1) {
|
|
*ppCertCtx = CertEnumCertificatesInStore(hStoreTMP, NULL);
|
|
*phStore = hStoreTMP;
|
|
return;
|
|
} else if ((dwCertCount > 1) &&
|
|
(hCryptUIDLL = LoadLibrary("cryptui.dll")) &&
|
|
(dfnCryptUIDlgSelectCertificateFromStore = (DFNCryptUIDlgSelectCertificateFromStore)GetProcAddress(hCryptUIDLL, "CryptUIDlgSelectCertificateFromStore")) &&
|
|
(*ppCertCtx = dfnCryptUIDlgSelectCertificateFromStore(hStoreTMP, NULL, NULL, NULL, 0, 0, NULL))) {
|
|
*phStore = hStoreTMP;
|
|
FreeLibrary(hCryptUIDLL);
|
|
return;
|
|
}
|
|
error:
|
|
if (hCryptUIDLL) { FreeLibrary(hCryptUIDLL); }
|
|
if (hStoreTMP) { CertCloseStore(hStoreTMP, CERT_CLOSE_STORE_FORCE_FLAG); }
|
|
if (hStoreMY) { CertCloseStore(hStoreMY, CERT_CLOSE_STORE_FORCE_FLAG); }
|
|
*ppCertCtx = NULL;
|
|
*phStore = NULL;
|
|
}
|
|
|
|
/*
|
|
* Return a malloc'ed string containing the requested subitem.
|
|
*/
|
|
PSTR capi_select_cert_finditem(PSTR szCert, PCSTR szStart)
|
|
{
|
|
PSTR ptrStart, ptrEnd, szResult;
|
|
ptrStart = strstr(szCert, szStart);
|
|
ptrEnd = strstr(szCert, ",");
|
|
if (!ptrEnd || ptrEnd < ptrStart) {
|
|
ptrEnd = szCert + strlen(szCert);
|
|
}
|
|
if (!ptrStart || ptrStart > ptrEnd) {
|
|
return NULL;
|
|
}
|
|
ptrStart += strlen(szStart);
|
|
szResult = (PSTR)calloc(ptrEnd - ptrStart + 1, sizeof(char));
|
|
memcpy(szResult, ptrStart, ptrEnd - ptrStart);
|
|
return szResult;
|
|
}
|
|
|
|
/*
|
|
* Select a certificate given the definition string.
|
|
*/
|
|
void capi_select_cert(PSTR szCert, PCCERT_CONTEXT *ppCertCtx, HCERTSTORE *phStore)
|
|
{
|
|
PSTR szCN = NULL, szThumb, ptrStart, ptrStartAll;
|
|
LPWSTR wszCN = NULL;
|
|
DWORD i, len;
|
|
PBYTE pbThumb = snewn(20, BYTE);
|
|
ptrStart = strstr(szCert, "cert://");
|
|
ptrStartAll = strstr(szCert, "cert://*");
|
|
if (ptrStart != szCert) {
|
|
ptrStart = strstr(szCert, "x509://");
|
|
ptrStartAll = strstr(szCert, "x509://*");
|
|
if (ptrStart != szCert) {
|
|
*ppCertCtx = NULL;
|
|
*phStore = NULL;
|
|
return;
|
|
}
|
|
}
|
|
if (ptrStartAll) {
|
|
capi_select_cert_2(NULL, NULL, ppCertCtx, phStore);
|
|
return;
|
|
}
|
|
szThumb = capi_select_cert_finditem(szCert, "thumbprint=");
|
|
if (szThumb && 40 == strlen(szThumb)) {
|
|
capi_sha1_to_binary(szThumb, pbThumb);
|
|
capi_select_cert_2(pbThumb, NULL, ppCertCtx, phStore);
|
|
} else {
|
|
szCN = capi_select_cert_finditem(szCert, "cn=");
|
|
if (szCN) {
|
|
len = strlen(szCN);
|
|
wszCN = (LPWSTR)calloc(len + 1, sizeof(wchar_t));
|
|
for (i = 0; i < len; i++) {
|
|
wszCN[i] = szCN[i];
|
|
}
|
|
}
|
|
capi_select_cert_2(NULL, wszCN, ppCertCtx, phStore);
|
|
}
|
|
if (szCN) { free(szCN); }
|
|
if (wszCN) { free(wszCN); }
|
|
sfree(pbThumb);
|
|
}
|
|
|
|
/*
|
|
* Get rsa key comment on the form "cert://cn=<cn>,thumbprint=<sha1>".
|
|
*/
|
|
static PSTR capi_get_description(PSTR file, PCCERT_CONTEXT pCertContext)
|
|
{
|
|
DWORD thumbPrintSize = 20;
|
|
BYTE thumbPrint[20];
|
|
if (!CryptHashCertificate(0, CALG_SHA1, 0, pCertContext->pbCertEncoded,
|
|
pCertContext->cbCertEncoded, (PBYTE)&thumbPrint, &thumbPrintSize)) {
|
|
}
|
|
PSTR tp = capi_binary_to_hex((PBYTE)&thumbPrint, thumbPrintSize);
|
|
return dupcat(file, "thumbprint=", tp, NULL);
|
|
}
|
|
|
|
char *wincrypto_invalid(ssh_key *key, unsigned flags)
|
|
{
|
|
RSAKey *rsa = container_of(key, RSAKey, sshk);
|
|
HCERTSTORE hCertStore;
|
|
PCCERT_CONTEXT pCertCtx;
|
|
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = 0;
|
|
DWORD dwSpec, cbSig = 0;
|
|
BOOL fCallerFreeProvAlwaysFalse = TRUE;
|
|
|
|
capi_select_cert(rsa->comment, &pCertCtx, &hCertStore);
|
|
if (pCertCtx)
|
|
{
|
|
if (CryptAcquireCertificatePrivateKey(pCertCtx, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, 0, &hCryptProvOrNCryptKey, &dwSpec, &fCallerFreeProvAlwaysFalse)) {
|
|
return NULL;
|
|
}
|
|
CertFreeCertificateContext(pCertCtx);
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
}
|
|
return dupstr("Could not acquire private key..");
|
|
}
|
|
|
|
static void wincrypto_public_blob(ssh_key *key, BinarySink *bs)
|
|
{
|
|
DWORD cbPublicKeyBlob = 8192;
|
|
PBYTE pbPublicKeyBlob = NULL;
|
|
bool isX509 = false;
|
|
uintmax_t size = 0, i = 0;
|
|
|
|
RSAKey *rsa = container_of(key, RSAKey, sshk);
|
|
|
|
if (0 == strncmp("x509://", rsa->comment, 7)) {
|
|
isX509 = true;
|
|
}
|
|
|
|
if (isX509) {
|
|
size = mp_get_integer(rsa->private_exponent);
|
|
for (i = 0; i < size; i++) {
|
|
BYTE f = mp_get_byte(rsa->iqmp, (size_t)(size - i - 1));
|
|
put_byte(bs, f);
|
|
}
|
|
} else {
|
|
put_stringz(bs, "ssh-rsa");
|
|
put_mp_ssh2(bs, rsa->exponent);
|
|
put_mp_ssh2(bs, rsa->modulus);
|
|
}
|
|
|
|
}
|
|
|
|
static ssh_key *wincrypto_new_priv(const ssh_keyalg *self,
|
|
ptrlen pub, ptrlen priv)
|
|
{
|
|
RSAKey *rsa;
|
|
BOOL result;
|
|
PCCERT_CONTEXT pCertContext;
|
|
HCERTSTORE hCertStore;
|
|
DWORD cbPublicKeyBlob = 8192;
|
|
PBYTE pbPublicKeyBlob = NULL;
|
|
RSAPUBKEY *pRSAPubKey;
|
|
bool isX509 = false;
|
|
|
|
int len = strlen(pub.ptr);
|
|
if ((len < 7)
|
|
|| !(0 == strncmp("cert://", pub.ptr, 7)
|
|
|| (isX509 = (0 == strncmp("x509://", pub.ptr, 7))))) {
|
|
return NULL;
|
|
}
|
|
|
|
capi_select_cert((PSTR)pub.ptr, &pCertContext, &hCertStore);
|
|
if (!pCertContext) {
|
|
return NULL;
|
|
}
|
|
|
|
rsa = snew(RSAKey);
|
|
rsa->p = mp_from_integer(0);
|
|
rsa->q = mp_from_integer(0);
|
|
if (isX509)
|
|
rsa->sshk.vt = &ssh_x509_wincrypt;
|
|
else
|
|
rsa->sshk.vt = &ssh_rsa_wincrypt;
|
|
rsa->comment = dupstr(pub.ptr);
|
|
|
|
result = CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
RSA_CSP_PUBLICKEYBLOB,
|
|
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
|
|
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
|
|
0,
|
|
(void*)(pbPublicKeyBlob = snewn(cbPublicKeyBlob, BYTE)),
|
|
&cbPublicKeyBlob);
|
|
if (!result) {
|
|
CertFreeCertificateContext(pCertContext);
|
|
sfree(pbPublicKeyBlob);
|
|
return NULL;
|
|
}
|
|
|
|
pRSAPubKey = (RSAPUBKEY*)(pbPublicKeyBlob + sizeof(BLOBHEADER));
|
|
capi_reverse_array(pbPublicKeyBlob + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), pRSAPubKey->bitlen / 8);
|
|
rsa->exponent = mp_from_integer(pRSAPubKey->pubexp);
|
|
rsa->modulus = mp_from_bytes_be(make_ptrlen(pbPublicKeyBlob + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), pRSAPubKey->bitlen / 8));
|
|
rsa->iqmp = mp_from_bytes_be(make_ptrlen(pCertContext->pbCertEncoded, pCertContext->cbCertEncoded));
|
|
rsa->private_exponent = mp_from_integer(pCertContext->cbCertEncoded);
|
|
|
|
// cleanup
|
|
sfree(pbPublicKeyBlob);
|
|
CertFreeCertificateContext(pCertContext);
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
|
|
return &rsa->sshk;
|
|
}
|
|
|
|
static void wincrypto_sign(ssh_key *key, ptrlen data,
|
|
unsigned flags, BinarySink *bs)
|
|
{
|
|
HCERTSTORE hCertStore;
|
|
PCCERT_CONTEXT pCertCtx;
|
|
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = 0;
|
|
HCRYPTHASH hHash = 0;
|
|
PBYTE pbSig = NULL;
|
|
DWORD dwSpec, cbSig = 0;
|
|
BOOL fCallerFreeProvAlwaysFalse = TRUE;
|
|
bool isX509 = false;
|
|
BCRYPT_PKCS1_PADDING_INFO padInfo;
|
|
BCRYPT_ALG_HANDLE hHashAlg = NULL;
|
|
BCRYPT_HASH_HANDLE hHashBcrypt = NULL;
|
|
NTSTATUS status = 0;
|
|
DWORD cbData = 0, cbHash = 0, cbHashObject = 0;
|
|
PBYTE pbHashObject = NULL;
|
|
PBYTE pbHash = NULL;
|
|
|
|
unsigned short* bcrypt_alg = NULL;
|
|
ALG_ID alg = CALG_SHA1;
|
|
const char *sign_alg_name;
|
|
RSAKey *rsa = container_of(key, RSAKey, sshk);
|
|
|
|
int len = strlen(rsa->comment);
|
|
if ((len < 7)
|
|
|| !(0 == strncmp("cert://", rsa->comment, 7)
|
|
|| (isX509 = (0 == strncmp("x509://", rsa->comment, 7))))) {
|
|
return;
|
|
}
|
|
|
|
if (isX509) {
|
|
alg = CALG_SHA1;
|
|
bcrypt_alg = BCRYPT_SHA1_ALGORITHM;
|
|
padInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
|
|
sign_alg_name = "x509v3-sign-rsa";
|
|
} else {
|
|
if (flags & SSH_AGENT_RSA_SHA2_256) {
|
|
alg = CALG_SHA_256;
|
|
bcrypt_alg = BCRYPT_SHA256_ALGORITHM;
|
|
padInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
|
|
sign_alg_name = "rsa-sha2-256";
|
|
} else if (flags & SSH_AGENT_RSA_SHA2_512) {
|
|
alg = CALG_SHA_512;
|
|
bcrypt_alg = BCRYPT_SHA512_ALGORITHM;
|
|
padInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM;
|
|
sign_alg_name = "rsa-sha2-512";
|
|
} else {
|
|
alg = CALG_SHA1;
|
|
bcrypt_alg = BCRYPT_SHA1_ALGORITHM;
|
|
padInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
|
|
sign_alg_name = "ssh-rsa";
|
|
}
|
|
}
|
|
|
|
capi_select_cert(rsa->comment, &pCertCtx, &hCertStore);
|
|
if (pCertCtx)
|
|
{
|
|
if (CryptAcquireCertificatePrivateKey(pCertCtx, CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, 0, &hCryptProvOrNCryptKey, &dwSpec, &fCallerFreeProvAlwaysFalse)) {
|
|
if (dwSpec == AT_KEYEXCHANGE || dwSpec == AT_SIGNATURE) {
|
|
/* A lot faster for smartcards because CryptSignHash for asking buffersize is querying sc already */
|
|
cbSig = 2048;
|
|
pbSig = snewn(cbSig, BYTE);
|
|
|
|
/* CSP implementation */
|
|
if (!CryptCreateHash((HCRYPTPROV)hCryptProvOrNCryptKey, alg, 0, 0, &hHash)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!CryptHashData(hHash, data.ptr, data.len, 0)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!CryptSignHash(hHash, dwSpec, NULL, 0, pbSig, &cbSig)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
capi_reverse_array(pbSig, cbSig);
|
|
put_stringz(bs, sign_alg_name);
|
|
put_uint32(bs, cbSig);
|
|
put_data(bs, pbSig, cbSig);
|
|
} else if (dwSpec == CERT_NCRYPT_KEY_SPEC) {
|
|
/* KSP/CNG implementation */
|
|
|
|
if ((status = BCryptOpenAlgorithmProvider(
|
|
&hHashAlg, bcrypt_alg, NULL, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = BCryptGetProperty(
|
|
hHashAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
pbHashObject = snewn(cbHashObject, BYTE);
|
|
if (NULL == pbHashObject) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = BCryptGetProperty(
|
|
hHashAlg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHash, sizeof(DWORD), &cbData, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
pbHash = snewn(cbHash, BYTE);
|
|
if (NULL == pbHash) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = BCryptCreateHash(
|
|
hHashAlg, &hHashBcrypt, pbHashObject, cbHashObject, NULL, 0, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = BCryptHashData(
|
|
hHashBcrypt, (PUCHAR)data.ptr, data.len, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = BCryptFinishHash(
|
|
hHashBcrypt, pbHash, cbHash, 0)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = NCryptSignHash(
|
|
hCryptProvOrNCryptKey, &padInfo, pbHash, cbHash, NULL, 0, &cbSig, BCRYPT_PAD_PKCS1)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
pbSig = snewn(cbSig, BYTE);
|
|
if (NULL == pbSig) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((status = NCryptSignHash(
|
|
hCryptProvOrNCryptKey, &padInfo, pbHash, cbHash, pbSig, cbSig, &cbSig, BCRYPT_PAD_PKCS1)) != 0) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
put_stringz(bs, sign_alg_name);
|
|
put_uint32(bs, cbSig);
|
|
put_data(bs, pbSig, cbSig);
|
|
}
|
|
}
|
|
}
|
|
Cleanup:
|
|
if (hHashAlg)
|
|
BCryptCloseAlgorithmProvider(hHashAlg, 0);
|
|
if (hHash)
|
|
BCryptDestroyHash(hHashBcrypt);
|
|
if (pbHashObject)
|
|
sfree(pbHashObject);
|
|
if (pbHash)
|
|
sfree(pbHash);
|
|
if (pbSig)
|
|
sfree(pbSig);
|
|
if (hHash)
|
|
CryptDestroyHash(hHash);
|
|
if (pCertCtx)
|
|
CertFreeCertificateContext(pCertCtx);
|
|
if (hCertStore)
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
}
|
|
|
|
static void wincrypto_freekey(ssh_key *key)
|
|
{
|
|
RSAKey *rsa = container_of(key, RSAKey, sshk);
|
|
freersakey(rsa);
|
|
sfree(rsa);
|
|
}
|
|
|
|
/*
|
|
* Load a rsa key from a certificate in windows certificate personal store.
|
|
*/
|
|
BOOL capi_load_key(const Filename **filename, BinarySink *bs)
|
|
{
|
|
BOOL result;
|
|
PCCERT_CONTEXT pCertContext;
|
|
HCERTSTORE hCertStore;
|
|
DWORD cbPublicKeyBlob = 8192;
|
|
PBYTE pbPublicKeyBlob = NULL;
|
|
RSAPUBKEY *pRSAPubKey;
|
|
bool isX509 = false;
|
|
|
|
int len = strlen((*filename)->path);
|
|
if ((len < 7)
|
|
|| !(0 == strncmp("cert://", (*filename)->path, 7)
|
|
|| (isX509 = (0 == strncmp("x509://", (*filename)->path, 7))))) {
|
|
return false;
|
|
}
|
|
capi_select_cert((*filename)->path, &pCertContext, &hCertStore);
|
|
if (!pCertContext) {
|
|
return false;
|
|
}
|
|
|
|
if ((*filename)->path[7] == '*') {
|
|
(*filename)->path[7] = '\0';
|
|
(*filename) = filename_from_str(capi_get_description((*filename)->path, pCertContext));
|
|
}
|
|
|
|
result = CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
RSA_CSP_PUBLICKEYBLOB,
|
|
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
|
|
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
|
|
0,
|
|
(void*)(pbPublicKeyBlob = snewn(cbPublicKeyBlob, BYTE)),
|
|
&cbPublicKeyBlob);
|
|
if (!result) {
|
|
CertFreeCertificateContext(pCertContext);
|
|
sfree(pbPublicKeyBlob);
|
|
return false;
|
|
}
|
|
|
|
pRSAPubKey = (RSAPUBKEY*)(pbPublicKeyBlob + sizeof(BLOBHEADER));
|
|
if (isX509) {
|
|
put_data(bs, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
|
|
} else {
|
|
put_stringz(bs, "ssh-rsa");
|
|
put_uint32(bs, 3);
|
|
capi_reverse_array((PBYTE)&(pRSAPubKey->pubexp), 4);
|
|
put_uint32(bs, pRSAPubKey->pubexp);
|
|
capi_reverse_array(pbPublicKeyBlob + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), pRSAPubKey->bitlen / 8);
|
|
put_uint16(bs, 1);
|
|
put_uint16(bs, pRSAPubKey->bitlen / 8);
|
|
put_data(bs, pbPublicKeyBlob + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), pRSAPubKey->bitlen / 8);
|
|
}
|
|
|
|
/* cleanup */
|
|
sfree(pbPublicKeyBlob);
|
|
CertFreeCertificateContext(pCertContext);
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
return true;
|
|
}
|
|
|
|
const ssh_keyalg ssh_rsa_wincrypt = {
|
|
NULL /*rsa2_new_pub*/,
|
|
wincrypto_new_priv /*rsa2_new_priv*/,
|
|
NULL /*rsa2_new_priv_openssh*/,
|
|
|
|
wincrypto_freekey /*rsa2_freekey*/,
|
|
wincrypto_invalid,
|
|
wincrypto_sign /*rsa2_sign*/,
|
|
NULL /*rsa2_verify*/,
|
|
wincrypto_public_blob /*rsa2_public_blob*/,
|
|
NULL /*rsa2_private_blob*/,
|
|
NULL /*rsa2_openssh_blob*/,
|
|
NULL /*rsa2_cache_str*/,
|
|
|
|
NULL /*rsa2_pubkey_bits*/,
|
|
|
|
"ssh-rsa",
|
|
"rsa2",
|
|
NULL,
|
|
SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512,
|
|
};
|
|
|
|
|
|
const ssh_keyalg ssh_x509_wincrypt = {
|
|
NULL /*rsa2_new_pub*/,
|
|
wincrypto_new_priv /*rsa2_new_priv*/,
|
|
NULL /*rsa2_new_priv_openssh*/,
|
|
|
|
wincrypto_freekey /*rsa2_freekey*/,
|
|
wincrypto_invalid,
|
|
wincrypto_sign /*rsa2_sign*/,
|
|
NULL /*rsa2_verify*/,
|
|
wincrypto_public_blob /*rsa2_public_blob*/,
|
|
NULL /*rsa2_private_blob*/,
|
|
NULL /*rsa2_openssh_blob*/,
|
|
NULL /*rsa2_cache_str*/,
|
|
|
|
NULL /*rsa2_pubkey_bits*/,
|
|
|
|
"x509v3-sign-rsa",
|
|
"rsa2",
|
|
NULL,
|
|
SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512,
|
|
};
|
|
|
|
#endif
|