Merge pull request #3550 from fufesou/feat/install_page_cert_option

feat/install page cert option
This commit is contained in:
RustDesk 2023-03-08 16:58:51 +08:00 committed by GitHub
commit 9a1950ad4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 361 additions and 30 deletions

View File

@ -86,7 +86,7 @@ jobs:
shell: bash
- name: Build rustdesk
run: python3 .\build.py --portable --hwcodec --flutter
run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver
- name: Sign rustdesk files
uses: GermanBluefox/code-sign-action@v7

1
Cargo.lock generated
View File

@ -4943,6 +4943,7 @@ dependencies = [
"flutter_rust_bridge_codegen",
"fruitbasket",
"hbb_common",
"hex",
"hound",
"image 0.24.5",
"impersonate_system",

View File

@ -64,6 +64,7 @@ errno = "0.2.8"
rdev = { git = "https://github.com/fufesou/rdev" }
url = { version = "2.1", features = ["serde"] }
dlopen = "0.1"
hex = "0.4.3"
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
chrono = "0.4.23"
@ -87,7 +88,7 @@ system_shutdown = "3.0.0"
[target.'cfg(target_os = "windows")'.dependencies]
trayicon = { git = "https://github.com/open-trade/trayicon-rs", features = ["winit"] }
winit = "0.26"
winapi = { version = "0.3", features = ["winuser"] }
winapi = { version = "0.3", features = ["winuser", "wincrypt"] }
winreg = "0.10"
windows-service = "0.4"
virtual_display = { path = "libs/virtual_display" }

View File

@ -37,7 +37,7 @@ def parse_rc_features(feature):
'IddDriver': {
'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/RustDeskIddDriver_x64.zip',
'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/checksum_md5',
'exclude': ['README.md'],
'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'],
},
'PrivacyMode': {
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1'

View File

@ -946,7 +946,6 @@ Widget msgboxContent(String type, String title, String text) {
void msgBoxCommon(OverlayDialogManager dialogManager, String title,
Widget content, List<Widget> buttons,
{bool hasCancel = true}) {
dialogManager.dismissAll();
dialogManager.show((setState, close) => CustomAlertDialog(
title: Text(
translate(title),

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
@ -63,6 +65,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
late final TextEditingController controller;
final RxBool startmenu = true.obs;
final RxBool desktopicon = true.obs;
final RxBool driverCert = true.obs;
final RxBool showProgress = false.obs;
final RxBool btnEnabled = true.obs;
@ -145,26 +148,63 @@ class _InstallPageBodyState extends State<_InstallPageBody>
.marginOnly(left: em))
],
).marginSymmetric(vertical: 2 * em),
Row(
TextButton(
onPressed: () => startmenu.value = !startmenu.value,
child: Row(
children: [
Obx(() => Checkbox(
value: startmenu.value,
onChanged: (b) {
if (b != null) startmenu.value = b;
})),
Text(translate('Create start menu shortcuts'))
RichText(
text: TextSpan(
text: translate('Create start menu shortcuts'),
style: DefaultTextStyle.of(context).style,
),
),
],
),
Row(
),
TextButton(
onPressed: () => desktopicon.value = !desktopicon.value,
child: Row(
children: [
Obx(() => Checkbox(
value: desktopicon.value,
onChanged: (b) {
if (b != null) desktopicon.value = b;
})),
Text(translate('Create desktop icon'))
RichText(
text: TextSpan(
text: translate('Create desktop icon'),
style: DefaultTextStyle.of(context).style,
),
),
],
),
),
Offstage(
offstage: !Platform.isWindows,
child: TextButton(
onPressed: () => driverCert.value = !driverCert.value,
child: Row(
children: [
Obx(() => Checkbox(
value: driverCert.value,
onChanged: (b) {
if (b != null) driverCert.value = b;
})),
RichText(
text: TextSpan(
text: translate('idd_driver_tip'),
style: DefaultTextStyle.of(context).style,
),
),
],
),
),
),
GestureDetector(
onTap: () => launchUrlString('http://rustdesk.com/privacy'),
child: Row(
@ -225,14 +265,49 @@ class _InstallPageBodyState extends State<_InstallPageBody>
}
void install() {
do_install() {
btnEnabled.value = false;
showProgress.value = true;
String args = '';
if (startmenu.value) args += ' startmenu';
if (desktopicon.value) args += ' desktopicon';
if (driverCert.value) args += ' driverCert';
bind.installInstallMe(options: args, path: controller.text);
}
if (driverCert.isTrue) {
final tag = 'install-info-install-cert-confirm';
final btns = [
dialogButton(
'Cancel',
onPressed: () => gFFI.dialogManager.dismissByTag(tag),
isOutline: true,
),
dialogButton(
'OK',
onPressed: () {
gFFI.dialogManager.dismissByTag(tag);
do_install();
},
isOutline: false,
),
];
gFFI.dialogManager.show(
(setState, close) => CustomAlertDialog(
title: null,
content: SelectionArea(
child:
msgboxContent('info', 'Warning', 'confirm_idd_driver_tip')),
actions: btns,
onCancel: close,
),
tag: tag,
);
} else {
do_install();
}
}
void selectInstallPath() async {
String? install_path = await FilePicker.platform
.getDirectoryPath(initialDirectory: controller.text);

View File

@ -143,6 +143,10 @@ pub fn core_main() -> Option<Vec<String>> {
#[cfg(feature = "with_rc")]
hbb_common::allow_err!(crate::rc::extract_resources(&args[1]));
return None;
} else if args[0] == "--install-cert" {
#[cfg(windows)]
hbb_common::allow_err!(crate::platform::windows::install_cert(&args[1]));
return None;
} else if args[0] == "--portable-service" {
crate::platform::elevate_or_run_as_system(
click_setup,

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "分辨率"),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", "安装虚拟显示器驱动,以便在没有连接显示器的情况下启动虚拟显示器进行控制。"),
("confirm_idd_driver_tip", "安装虚拟显示器驱动的选项已勾选。请注意测试证书将被安装以信任虚拟显示器驱动。测试证书仅会用于信任Rustdesk的驱动。")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Auflösung"),
("No transfers in progress", "Keine Übertragungen im Gange"),
("Set one-time password length", "Länge des Einmalpassworts festlegen"),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Ανάλυση"),
("No transfers in progress", "Δεν υπάρχει μεταφορά σε εξέλιξη"),
("Set one-time password length", "Μέγεθος κωδικού μιας χρήσης"),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -45,5 +45,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("config_microphone", "In order to speak remotely, you need to grant RustDesk \"Record Audio\" permissions."),
("relay_hint_tip", "It may not be possible to connect directly, you can try to connect via relay. \nIn addition, if you want to use relay on your first try, you can add the \"/r\" suffix to the ID, or select the option \"Always connect via relay\" in the peer card."),
("No transfers in progress", ""),
("idd_driver_tip", "Install virtual display driver which is used when you have no physical displays."),
("confirm_idd_driver_tip", "The option to install the virtual display driver is checked. Note that a test certificate will be installed to trust the virtual display driver. This test certificate will only be used to trust Rustdesk drivers.")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Resolución"),
("No transfers in progress", "No hay transferencias en curso"),
("Set one-time password length", "Establecer contraseña de un solo uso"),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "وضوح"),
("No transfers in progress", "هیچ انتقالی در حال انجام نیست"),
("Set one-time password length", "طول رمز یکبار مصرف را تعیین کنید"),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Risoluzione"),
("No transfers in progress", "Nessun trasferimento in corso"),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Resolutie"),
("No transfers in progress", "Geen overdrachten in uitvoering"),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Rozdzielczość"),
("No transfers in progress", "Brak transferów w toku"),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "Разрешение"),
("No transfers in progress", "Передача не осуществляется"),
("Set one-time password length", "Установить длину одноразового пароля"),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", "分辨率"),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -461,5 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Resolution", ""),
("No transfers in progress", ""),
("Set one-time password length", ""),
("idd_driver_tip", ""),
("confirm_idd_driver_tip", "")
].iter().cloned().collect();
}

View File

@ -1108,6 +1108,12 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name}
);
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string();
let install_cert = if options.contains("driverCert") {
format!("\"{}\" --install-cert \"RustDeskIddDriver.cer\"", src_exe)
} else {
"".to_owned()
};
let cmds = format!(
"
{uninstall_str}
@ -1139,6 +1145,7 @@ sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\
sc start {app_name}
sc stop {app_name}
sc delete {app_name}
{install_cert}
{after_install}
{sleep}
",
@ -1159,6 +1166,7 @@ sc delete {app_name}
shortcuts=shortcuts,
config_path=Config::file().to_str().unwrap_or(""),
lic=register_licence(),
install_cert=install_cert,
after_install=get_after_install(&exe),
sleep=if debug {
"timeout 300"
@ -1236,6 +1244,7 @@ fn get_uninstall(kill_self: bool) -> String {
}
pub fn uninstall_me(kill_self: bool) -> ResultType<()> {
allow_err!(cert::uninstall_certs());
run_cmds(get_uninstall(kill_self), true, "uninstall")
}
@ -1902,3 +1911,177 @@ pub fn user_accessible_folder() -> ResultType<PathBuf> {
}
Ok(dir)
}
#[inline]
pub fn install_cert(cert_file: &str) -> ResultType<()> {
let exe_file = std::env::current_exe()?;
if let Some(cur_dir) = exe_file.parent() {
allow_err!(cert::install_cert(cur_dir.join(cert_file)));
} else {
bail!(
"Invalid exe parent for {}",
exe_file.to_string_lossy().as_ref()
);
}
Ok(())
}
mod cert {
use hbb_common::{allow_err, bail, log, ResultType};
use std::{path::Path, str::from_utf8};
use winapi::shared::{
minwindef::{BYTE, DWORD, TRUE},
ntdef::NULL,
};
use winapi::um::{
errhandlingapi::GetLastError,
wincrypt::{
CertCloseStore, CertEnumCertificatesInStore, CertNameToStrA, CertOpenSystemStoreA,
CryptHashCertificate, ALG_ID, CALG_SHA1, CERT_ID_SHA1_HASH, CERT_X500_NAME_STR,
PCCERT_CONTEXT,
},
winreg::HKEY_LOCAL_MACHINE,
};
use winreg::{
enums::{KEY_WRITE, REG_BINARY},
RegKey,
};
const ROOT_CERT_STORE_PATH: &str = "SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\";
const THUMBPRINT_ALG: ALG_ID = CALG_SHA1;
const THUMBPRINT_LEN: DWORD = 20;
#[inline]
unsafe fn compute_thumbprint(pb_encoded: *const BYTE, cb_encoded: DWORD) -> (Vec<u8>, String) {
let mut size = THUMBPRINT_LEN;
let mut thumbprint = [0u8; THUMBPRINT_LEN as usize];
if CryptHashCertificate(
0,
THUMBPRINT_ALG,
0,
pb_encoded,
cb_encoded,
thumbprint.as_mut_ptr(),
&mut size,
) == TRUE
{
(thumbprint.to_vec(), hex::encode(thumbprint).to_ascii_uppercase())
} else {
(thumbprint.to_vec(), "".to_owned())
}
}
#[inline]
unsafe fn open_reg_cert_store() -> ResultType<RegKey> {
let hklm = winreg::RegKey::predef(HKEY_LOCAL_MACHINE);
Ok(hklm.open_subkey_with_flags(ROOT_CERT_STORE_PATH, KEY_WRITE)?)
}
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpef/6a9e35fa-2ac7-4c10-81e1-eabe8d2472f1
fn create_cert_blob(thumbprint: Vec<u8>, encoded: Vec<u8>) -> Vec<u8> {
let mut blob = Vec::new();
let mut property_id = (CERT_ID_SHA1_HASH as u32).to_le_bytes().to_vec();
let mut pro_reserved = [0x01, 0x00, 0x00, 0x00].to_vec();
let mut pro_length = (THUMBPRINT_LEN as u32).to_le_bytes().to_vec();
let mut pro_val = thumbprint;
blob.append(&mut property_id);
blob.append(&mut pro_reserved);
blob.append(&mut pro_length);
blob.append(&mut pro_val);
let mut blob_reserved = [0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00].to_vec();
let mut blob_length = (encoded.len() as u32).to_le_bytes().to_vec();
let mut blob_val = encoded;
blob.append(&mut blob_reserved);
blob.append(&mut blob_length);
blob.append(&mut blob_val);
blob
}
pub fn install_cert<P: AsRef<Path>>(path: P) -> ResultType<()> {
let mut cert_bytes = std::fs::read(path)?;
unsafe {
let thumbprint = compute_thumbprint(cert_bytes.as_mut_ptr(), cert_bytes.len() as _);
log::debug!("Thumbprint of cert {}", &thumbprint.1);
let reg_cert_key = open_reg_cert_store()?;
let (cert_key, _) = reg_cert_key.create_subkey(&thumbprint.1)?;
let data = winreg::RegValue {
vtype: REG_BINARY,
bytes: create_cert_blob(thumbprint.0, cert_bytes),
};
cert_key.set_raw_value("Blob", &data)?;
}
Ok(())
}
fn get_thumbprints_to_rm() -> ResultType<Vec<String>> {
let issuers_to_rm = ["CN=\"WDKTestCert admin,133225435702113567\""];
let mut thumbprints = Vec::new();
let mut buf = [0u8; 1024];
unsafe {
let store_handle = CertOpenSystemStoreA(0 as _, "ROOT\0".as_ptr() as _);
if store_handle.is_null() {
bail!("Error opening certificate store: {}", GetLastError());
}
let mut cert_ctx: PCCERT_CONTEXT = CertEnumCertificatesInStore(store_handle, NULL as _);
while !cert_ctx.is_null() {
// https://stackoverflow.com/a/66432736
let cb_size = CertNameToStrA(
(*cert_ctx).dwCertEncodingType,
&mut ((*(*cert_ctx).pCertInfo).Issuer) as _,
CERT_X500_NAME_STR,
buf.as_mut_ptr() as _,
buf.len() as _,
);
if cb_size != 1 {
if let Ok(issuer) = from_utf8(&buf[..cb_size as _]) {
for iss in issuers_to_rm.iter() {
if issuer.contains(iss) {
let (_, thumbprint) = compute_thumbprint(
(*cert_ctx).pbCertEncoded,
(*cert_ctx).cbCertEncoded,
);
if !thumbprint.is_empty() {
thumbprints.push(thumbprint);
}
}
}
}
}
cert_ctx = CertEnumCertificatesInStore(store_handle, cert_ctx);
}
CertCloseStore(store_handle, 0);
}
Ok(thumbprints)
}
pub fn uninstall_certs() -> ResultType<()> {
let thumbprints = get_thumbprints_to_rm()?;
let reg_cert_key = unsafe { open_reg_cert_store()? };
for thumbprint in thumbprints.iter() {
allow_err!(reg_cert_key.delete_subkey(thumbprint));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_install_cert() {
println!("install driver cert: {:?}", cert::install_cert("RustDeskIddDriver.cer"));
}
#[test]
fn test_uninstall_cert() {
println!("uninstall driver certs: {:?}", cert::uninstall_certs());
}
}