diff --git a/dlls/cryptui/cryptui_En.rc b/dlls/cryptui/cryptui_En.rc index 8cabea43c6e..c6d6559ba54 100644 --- a/dlls/cryptui/cryptui_En.rc +++ b/dlls/cryptui/cryptui_En.rc @@ -166,6 +166,8 @@ STRINGTABLE DISCARDABLE IDS_EXPORT_PASSWORD_TITLE "Enter Password" IDS_EXPORT_PASSWORD_SUBTITLE "You may password-protect a private key." IDS_EXPORT_PASSWORD_MISMATCH "The passwords do not match." + IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE "Note: The private key for this certificate could not be opened." + IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE "Note: The private key for this certificate is not exportable." } IDD_GENERAL DIALOG DISCARDABLE 0, 0, 255, 236 @@ -396,6 +398,7 @@ BEGIN IDC_EXPORT_PRIVATE_KEY_YES, 31,36,200,12, BS_AUTORADIOBUTTON|WS_TABSTOP AUTORADIOBUTTON "N&o, do not export the private key", IDC_EXPORT_PRIVATE_KEY_NO, 31,48,200,12, BS_AUTORADIOBUTTON + LTEXT "", IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE, 21,60,200,24 END IDD_EXPORT_PASSWORD DIALOG DISCARDABLE 0,0,317,143 diff --git a/dlls/cryptui/cryptuires.h b/dlls/cryptui/cryptuires.h index e16bb213960..b46ea4e128e 100644 --- a/dlls/cryptui/cryptuires.h +++ b/dlls/cryptui/cryptuires.h @@ -165,6 +165,8 @@ #define IDS_EXPORT_PASSWORD_TITLE 1222 #define IDS_EXPORT_PASSWORD_SUBTITLE 1223 #define IDS_EXPORT_PASSWORD_MISMATCH 1224 +#define IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE 1225 +#define IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE 1226 #define IDD_GENERAL 100 #define IDD_DETAIL 101 @@ -262,7 +264,8 @@ #define IDC_EXPORT_SETTINGS 2911 #define IDC_EXPORT_PRIVATE_KEY_YES 2912 #define IDC_EXPORT_PRIVATE_KEY_NO 2913 -#define IDC_EXPORT_PASSWORD 2914 -#define IDC_EXPORT_PASSWORD_CONFIRM 2915 +#define IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE 2914 +#define IDC_EXPORT_PASSWORD 2915 +#define IDC_EXPORT_PASSWORD_CONFIRM 2916 #endif /* ndef __CRYPTUIRES_H_ */ diff --git a/dlls/cryptui/main.c b/dlls/cryptui/main.c index 677bee4f3d7..620f53f72fe 100644 --- a/dlls/cryptui/main.c +++ b/dlls/cryptui/main.c @@ -5518,6 +5518,7 @@ struct ExportWizData CRYPTUI_WIZ_EXPORT_INFO exportInfo; CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO contextInfo; BOOL freePassword; + PCRYPT_KEY_PROV_INFO keyProvInfo; LPWSTR fileName; HANDLE file; BOOL success; @@ -5567,6 +5568,63 @@ static LRESULT CALLBACK export_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, return ret; } +static PCRYPT_KEY_PROV_INFO export_get_private_key_info(PCCERT_CONTEXT cert) +{ + PCRYPT_KEY_PROV_INFO info = NULL; + DWORD size; + + if (CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, + NULL, &size)) + { + info = HeapAlloc(GetProcessHeap(), 0, size); + if (info) + { + if (!CertGetCertificateContextProperty(cert, + CERT_KEY_PROV_INFO_PROP_ID, info, &size)) + { + HeapFree(GetProcessHeap(), 0, info); + info = NULL; + } + } + } + return info; +} + +static BOOL export_acquire_private_key(PCRYPT_KEY_PROV_INFO info, + HCRYPTPROV *phProv) +{ + BOOL ret; + + ret = CryptAcquireContextW(phProv, info->pwszContainerName, + info->pwszProvName, info->dwProvType, 0); + if (ret) + { + DWORD i; + + for (i = 0; i < info->cProvParam; i++) + CryptSetProvParam(*phProv, info->rgProvParam[i].dwParam, + info->rgProvParam[i].pbData, info->rgProvParam[i].dwFlags); + } + return ret; +} + +static BOOL export_is_key_exportable(HCRYPTPROV hProv, DWORD keySpec) +{ + BOOL ret; + HCRYPTKEY key; + + if ((ret = CryptGetUserKey(hProv, keySpec, &key))) + { + DWORD permissions, size = sizeof(permissions); + + if ((ret = CryptGetKeyParam(key, KP_PERMISSIONS, (BYTE *)&permissions, + &size, 0)) && !(permissions & CRYPT_EXPORT)) + ret = FALSE; + CryptDestroyKey(key); + } + return ret; +} + static LRESULT CALLBACK export_private_key_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { @@ -5578,9 +5636,36 @@ static LRESULT CALLBACK export_private_key_dlg_proc(HWND hwnd, UINT msg, case WM_INITDIALOG: { PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; + PCRYPT_KEY_PROV_INFO info; + HCRYPTPROV hProv = 0; + int errorID = 0; data = (struct ExportWizData *)page->lParam; SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); + /* Get enough information about a key to see whether it's exportable. + */ + if (!(info = export_get_private_key_info( + data->exportInfo.u.pCertContext))) + errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; + else if (!export_acquire_private_key(info, &hProv)) + errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; + else if (!export_is_key_exportable(hProv, info->dwKeySpec)) + errorID = IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE; + + if (errorID) + { + WCHAR error[MAX_STRING_LEN]; + + LoadStringW(hInstance, errorID, error, + sizeof(error) / sizeof(error[0])); + SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE), + WM_SETTEXT, 0, (LPARAM)error); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_YES), FALSE); + } + else + data->keyProvInfo = info; + if (hProv) + CryptReleaseContext(hProv, 0); SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_NO), BM_CLICK, 0, 0); break; @@ -6671,6 +6756,7 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, memcpy(&data.contextInfo, pvoid, min(((PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO)pvoid)->dwSize, sizeof(data.contextInfo))); + data.keyProvInfo = NULL; data.fileName = NULL; data.file = INVALID_HANDLE_VALUE; data.success = FALSE; @@ -6785,6 +6871,7 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, if (data.freePassword) HeapFree(GetProcessHeap(), 0, (LPWSTR)data.contextInfo.pwszPassword); + HeapFree(GetProcessHeap(), 0, data.keyProvInfo); CloseHandle(data.file); HeapFree(GetProcessHeap(), 0, data.fileName); if (l == 0)