TeamViewer API集成

2023-12-20 64

一、概述

TeamViewer (Classic)提供了一个REACH API,用于集成到RMM或MDM解决方案中。通过这个API,客户可以方便地启动无人值守和有人值守的远程控制会话,从而提高工作效率。本教程解释了REACH API的基本概念,并说明和指导如何将API集成到管理解决方案中。

之后,我们用由TeamViewer (Classic)开发集成示例源代码(也称为VendorExampleApp)来解释如何利用API实现集成。

版权所有 (c) 2018 TeamViewer GmbH

TeamViewer特此授予任何获得本软件和相关文档文件(“软件”)副本的人免费许可,无限制地交易本软件,包括但不限于使用,复制,修改,合并的权利,出版,分发,再许可和/或出售本软件的副本,并根据以下条件允许被授权人员从事以下事项:

上述版权声明和本许可声明应包含在本软件的所有副本或实质部分。

本软件按“原样”提供,不提供任何形式的明示或暗示保证和担保,包括但不限于适销性,特定用途的适用性的保证。在任何情况下,作者或版权所有者均不对任何索赔,损害或其他债承担任何责任,无论这些责任是否由使用本软件而产生的,或者与使用本软件相关的,或者与交易本软件而产生的合同缔结,侵权或其他方面的行为。

再次使用或再次发表软件时,请尊重所包含的第三方软件的许可。

二、概念和安全性

REACH API所包含的概念分为三种类型:

  • 注册REACH API管理的设备,也称为部署阶段;
  • 取消注册设备,通过REACH API删除访问权限;
  • 控制阶段允许启动无人职守(连接到远程设备时没有用户交互)和有人值守(连接远程设备时用户手动接受连接)连接到已注册的设备。两种连接模式都可以是全功能远程控制会话,或仅限于纯视图连接(仅支持在特定平台上)

1、设备注册

部署用于在设备和管理解决方案之间交换加密密钥。 这些加密密钥用于控制和注销阶段,并确保与设备的所有通信都是安全的。

该图解释了注册流程:

TeamViewer API集成

Management Solution Agent(MSA)是必须在设备上运行的组件。运行此代理必须具有设备的管理权限,并能获取完成部署而所需的数据。开始部署时,MSA会读取一个特殊文件(步骤1),该文件在TeamViewer (Classic)客户端启动时以及每次成功或不成功的部署请求发送到TeamViewer (Classic)客户端之后生成。

注意:每个平台上的步骤1可能不同。文件方法仅适用于Windows和macOS平台。但是,为所有平台提供的部署数据是相同的。

此文件包含RolloutKey(ROK仅对一个请求有效并用于解密API响应),全局唯一标识符(GUID)以及设备的RemotecontrolID(TeamViewer (Classic) ID)。

API可用RemotecontrolID、部署数据的GUID和一系列权限调用(POST / oem / devices / createdevicekey)以创建新的设备密钥(步骤3)。API调用能够启动TeamViewer (Classic)后端至目标客户端的通信。如果数据有效,TeamViewer (Classic)客户端将为以后的远程控制会话创建密钥对。

通过TeamViewer (Classic) 的后端,TeamViewer (Classic)客户端对具有RolloutKey的新创建的密钥对的私钥(= DeviceKey)进行加密。同时将与密钥对的标识符和API调用的解除授权令牌一起作为回复发送给API调用行。(步骤4)管理解决方案必须使用先前获得的RolloutKey来解密DeviceKey。为了简化解码过程,使用标准PEM格式加密DeviceKey。DeviceKey和DeviceKeyID是在控制阶段使用的,而解除授权令牌是在需要注销DeviceKey的时候使用的。

注意:解密的DeviceKey,DeviceKeyID以及解除授权令牌必须安全存储在管理解决方案端。

2、控制阶段

完成第一步后,从现在开始,可以启动到已注册设备的远程控制会话。启动远程控制会话按以下过程:

TeamViewer API集成

由于DeviceKey和DeviceKeyID是启动远程控制会话必须的前提,所以只有在设备成功注册以后远程连接才变为可能。

第一步是WebAPI调用(POST / oem / devices / requestcontrol),其参数包括RemotecontrolID,DeviceKeyID和控制类型。这些参数将传递给目标客户端,目标客户端验证DeviceKey是否拥有API调用请求的控制权限。

成功验证请求后,目标TeamViewer (Classic)客户端会生成一次性临时密钥(一个Hash身份验证消息,HMAC),该秘钥与会话密码同等效果。HMAC的确定是用控制请求所接收的数据和TeamViewer (Classic)客户端生成的数据(Target nonce,DeviceSecret)进行计算。在计算HMAC之后,客户端使用通过DeviceKeyID在API调用中指定的DeviceKey加密DeviceSecret。

DeviceSecret作为API的调用响应参数EncryptedDeviceSecret与目标随机数以及TeamViewer (Classic)协议URL(“teamviewerapi://”)的URL模板一起返回,该URL模板包括占位符YOURMASTERSECRET。ISV执行必须使用DeviceKey解密EncryptedDeviceSecret,并根据解密的设备密钥,目标随机数和API调用请求中发送的其他信息计算HMAC。

计算出HMAC后,就必须用HMAC替换占位符YOURMASTERSECRET以构建有效的TeamViewer (Classic)协议URL。必须将包含HMAC的完整URL提供给源TeamViewer (Classic)客户端以启动连接。这可能会在浏览器或命令行参数中发生(当TeamViewer (Classic)安装并与TeamViewer (Classic)应用程序关联时,TeamViewer (Classic)协议已在操作系统中注册)。

TeamViewer (Classic)客户端现在使用URL中的信息(包括HMAC)建立与目标客户端的连接。

3、注销阶段

如果不再使用DeviceKey,则可以注销DeviceKey。该图显示了基本原理:

TeamViewer API集成

注销DeviceKey需要WebAPI调用DELETE / oem / devices / unregister。要执行注销,需要DeviceKeyID识别正确的密钥、设备的RemotecontrolID以及部署阶段在POST / oem / devices / createdevicekey API调用所发出的解除授权令牌。之后,此信息将由设备上的TeamViewer (Classic)客户端处理。

如果API中的信息与客户端上的信息匹配,则部署阶段所创建的密钥对的本地部分将会被删除,并通过WebAPI返回确认信息。从此时起,不能再使用此DeviceKey建立进一步的远程控制会话。

三、集成实例VendorExampleApp

1、要求

按照本说明实际执行集成时,需要在继承的不同阶段中提供一些信息,包括:

  • 供应商ID;
  • 租户帐户;
  • 具有远程控制权限的脚本令牌。

供应商ID已由TeamViewer (Classic)发送给ISV。之后,可以使用具有租户管理权限的供应商帐户的脚本令牌创建租户帐户。可以使用类似Postman的工具来发布WebAPI调用。最后,需要来自租户帐户的脚本令牌,该令牌需要有创建设备密钥,请求控制和删除设备密钥的权限。

本集成实例所提供的代码使用以下技术和语言库:

  • C# and .NET runtime
  • BouncyCastle security library

Android系统还需要以下信息:

  • APK签名值为MSA;
  • APK签名的ID(由TeamViewer (Classic)的WebAPI发布;
  • TeamViewer (Classic)帐户的ID。

AppKey是Android设备上MSA的签名,AppKeyID与帐户ID可一起用作索引。这两个值都与WebAPI调用一起返回,用于在Web控制台中注册AppKey。

提示:请注意,解释过程中使用的所有值都可替换具体值。

2、供应商端集成实施概述

下图简要介绍了供应商方面的组件,需要进行集成。

TeamViewer API集成

3、REACH API设备的部署

API调用可以使用任何HTTP客户端类完成,此WebAPI调用的前提条件是从TeamViewer (Classic)客户端编写的部署文件中读取信息。此文件在Windows和macOs平台上需要管理员权限才可读。

在Android设备上,访问部署数据需要注册MSA应用程序。这将在下面的Android集成一章中单独描述。对于我们这里的示例,我们引用的代码是VendorExampleApp中源代码的一部分。首先,必须创建JSON主体,可以选用数据类进行创建,例如下列示例中的方法创建此类数据类。

请注意,RemotecontrolID和RequestID的信息来自部署文件:

(1)创建请求的正文

var createDeviceKeyRequest = new CreateDeviceKeyRequest
{
    key_permissions = "unattended",
    remotecontrol_id = "r124124124",
    request_id = "{8c4fa1a7-9d1c-41e9-be17-70e95c289080}",
    tenant_id = "t0001"
};

创建数据类并用信息填写后,指定具体的url和方法,在HTTP请求标头上填写所需的身份验证就可以发出WepAPI调用,然后将包含上面的序列化数据类的JSON主体附上。将授权添加到HTTP标头必须通过TeamViewer (Classic)管理控制台创建应用程序脚本标记。

必须确保此脚本标记具有执行WebAPI调用的正确权限。如果该脚本令牌的帐户是租户,则这些权限只能授予给该脚本令牌。请参阅以下框中的示例代码,该代码发出调用用以解析响应,并返回包含响应值的反序列化数据类。此示例代码来源于供应商应用程序。

(2)请求DeviceSecretKey

HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(apiUrl + "/api/v1/oem/devices/createdevicekey");

webReq.Method = "POST";
webReq.ContentType = "application/json";
webReq.Headers.Add("Authorization", "Bearer "+accessToken);

using (var streamWriter = new StreamWriter(webReq.GetRequestStream()))
{
    var createSecretKeyRequestJson = JsonConvert.SerializeObject(createDeivceKeyRequest);

    streamWriter.Write(createDeviceKeyRequestJson);
    streamWriter.Flush();
    streamWriter.Close();
}

var httpResponse = (HttpWebResponse) await webReq.GetResponseAsync();
if (httpResponse == null)
{
    throw new InvalidDataException("No response received");
}

var responseStream = httpResponse.GetResponseStream();
if (responseStream == null)
{
    throw new InvalidDataException("No response stream received.");
}

using (var streamReader = new StreamReader(responseStream))
{
    var result = streamReader.ReadToEnd();
    return JsonConvert.DeserializeObject<CreateDeviceKeyResponse>(result);
}

API响应后,您现在就拥有加密的DeviceKey。

为了解码PEM格式的加密DeviceKey,我们在这里选择了BouncyCastle作为库,这是很多操作更容易。

BouncyCastle也可用于Java。但是每种语言都有很多库可以使用PEM文件。

加密算法会在您从API请求获得的PEM文件中直接声明。

示例:

"-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,309AA16\n-----END RSA PRIVATE KEY-----\n"

出于安全考虑,我们强烈建议您使用库来处理工作,因为库通常能够自动识别正确的算法

如果算法本身存在安全问题,我们将会更改它。

如果使用适当的库,则ISV端不需要进行任何更改,而手动实现则不会那么灵活。

PEM密钥使用Bouncy Castle解密需要以下代码,如下例所示。

为简单起见,我们将deviceKey存储为已解密的PEM格式字符串,因为这可以很好地由BouncyCastle库处理并且可以作为字符串存储。

解密PEM密钥

TextReader textReader = new StringReader(encryptedDeviceKey);
PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
object deviceKeyObject = pemReader.ReadObject();

AsymmetricCipherKeyPair rsaPrivatekey = (AsymmetricCipherKeyPair)deviceKeyObject;

TextWriter tw = new StringWriter();
var pemWriter = new PemWriter(tw);
pemWriter.WriteObject(rsaPrivatekey.Private);
pemWriter.Writer.Flush();
string deviceKey = tw.ToString();
return deviceKey;

3、发起远程控制会话

用上一阶段得到DeviceKey和DeviceKeyID的数据,可以向注册的设备发起远程控制会话。在请求远程控制期间,需要使用与部署阶段期间请求权限相同控件类型。同样需要首先将WebAPI调用所需的信息填充到数据类实例中。

(1)请求控制数据类

var requestControlRequest = new RequestControlRequest()
{
    control_type = "attended",
    device_key_id = "{114fa1a7-abab-41e9-be17-70e95c289080}",
    remotecontrol_id = "r124124124",
    tenant_id = "t0001",
    tenant_nonce = "243dd78d07324ab7befa41390d08d35f"                      
};

有了这个数据实例,就可以下面的C#代码方式发出WebAPI调用。首先添加URL,然后添加带有脚本令牌的HTTP authorization-header,最后添加序列化数据类实例作为JSON主体。一旦请求返回并带有响应,就可以将数据反序列化为自己的数据类实例,该实例为TeamViewer (Classic)链接提供模板。之后,将使用Hash验证消息(HMAC)完成此链接。

(2)远程控制呼叫

HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(apiUrl + "/api/v1/oem/devices/requestcontrol");

webReq.Method = "POST";
webReq.ContentType = "application/json";
webReq.Headers.Add("Authorization", "Bearer " + accessToken);


using (var streamWriter = new StreamWriter(webReq.GetRequestStream()))
{
    var requestControlRequestJson = JsonConvert.SerializeObject(requestControlRequest);

    streamWriter.Write(requestControlRequestJson);
    streamWriter.Flush();
    streamWriter.Close();
}

var httpResponse =  (HttpWebResponse)(await webReq.GetResponseAsync());

if (httpResponse == null)
{
    throw new InvalidDataException("No response received");
}

var responseStream = httpResponse.GetResponseStream();

if (responseStream == null)
{
    throw new InvalidDataException("No response stream received.");
}

using (var streamReader = new StreamReader(responseStream))
{
    var result = streamReader.ReadToEnd();

    return JsonConvert.DeserializeObject<RequestControlResponse>(result);

}
  • 由于API响应仅返回链接模板,因此必须使用HMAC完成链接;
  • 创建HMAC的方法在API响应中以加密方式返回,因此第一步是解密;
  • 用于解密的密钥是DeviceKey,这在部署阶段通过API响应接收;
  • 由于DeviceKey存储为PEM格式的字符串,代码的第一部分将此字符串转换为适合BouncyCastle的结构(“AsymmetricCipherKeyPair”类型变量)。

(3)解密PreMasterSecret

using (var reader = new StringReader(DeviceKey))
{
    //Convert to right format
    var bytesToDecrypt = encryptedDeviceSecret.FromBase64SafeUrl();

    //--------DECRYPT WITH PEM DEVICE KEY-------------//
    AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
    var decryptEngine = new OaepEncoding(new RsaEngine());
    decryptEngine.Init(false, keyPair.Private);
    var decryptedDeviceSecret = decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
}

使用已解密的DeviceSecret,TenantID,目标的RemotecontrolID以及目标和租户nonce,可以按下面示例的方法创建HMAC,这也可以使用安全库BouncyCastle实现的。

(4)生成YOURMASTERSECRET

//---------HMAC Values to be hashed, recombined in a string ------------//
var hashVerifier = $"{tenantNonce}{targetNonce}{tenantId.Substring(1)}{remotecontrolId.Substring(1)}";


HMACSHA512 hmacsha512 = new HMACSHA512(decryptedDeviceSecret);
byte[] hashmessage = hmacsha512.ComputeHash(Encoding.UTF8.GetBytes(hashVerifier));
var masterSecret =  hashmessage.ToBase64SafeUrl();

结果将放入TeamViewer (Classic)链接模板而不是字符串YOURMASTERSECRET。字符串完成后,打开有TeamViewer (Classic)企业客户端的系统的Internet浏览器链接,或者通将此链接传递给TeamViewer (Classic).exe调用的命令行来建立远程控制会话。

4、取消注册设备

取消注册设备调用需要以下参数才能成功执行:

  • 具有正确权限的有效脚本令牌(删除设备密钥)
  • 解除授权令牌
  • 设备密钥ID
  • 遥控器ID
  • 租户ID

与前一的阶段一样,创建此特定调用的数据类实例,并填充用于调用WebAPI的具体数据:

(1)数据结构创建

var unregisterDeviceRequest = new UnregisterDeviceRequest
{
tenant_id = "t0001",
remotecontrol_id = "r124124124",
device_key_id = "{114fa1a7-abab-41e9-be17-70e95c289080}",
decommission_token = _mState.LastDecommissionToken
};

如前所示用相同的实现模式,将WebAPI调用URL,方式,应用程序脚本标记添加至HTTP授权标头,最后以JSON格式附加先前创建的数据类的序列化内容。

(2)注销

HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(apiUrl + "/api/v1/oem/devices");

webReq.Method = "DELETE";
webReq.ContentType = "application/json";
webReq.Headers.Add("Authorization", "Bearer " + accessToken);

using (var streamWriter = new StreamWriter(webReq.GetRequestStream()))
{
    var unregisterDeviceRequestJson = JsonConvert.SerializeObject(unregisterDeviceRequest);

    streamWriter.Write(unregisterDeviceRequestJson);
    streamWriter.Flush();
    streamWriter.Close();
}

var httpResponse = (HttpWebResponse)await webReq.GetResponseAsync();
if (httpResponse == null)
{
    throw new InvalidDataException("No response received");
}

var responseStream = httpResponse.GetResponseStream();
if (responseStream == null)
{
    throw new InvalidDataException("No response stream received.");
}

using (var streamReader = new StreamReader(responseStream))
{
    var result = streamReader.ReadToEnd();
    return;
}

在这种情况下,返回的结果将不包含任何数据。操作是否成功只能通过检查返回值是否为204来验证(也可以参见HTTP公共返回值)。

四、Android集成的细节

由于Android平台限制(没有适当的管理权限分离),部署阶段与其他平台不同。需要先验证MSA才能与TeamViewer (Classic) Android应用程序通信,并以不同的方式获取RolloutKey。

MSA签名密钥的SHA-256 Hash值(此处称为AppKey)必须在TeamViewer (Classic)中注册为十六进制字符串,并使用WebAPI调用POST“/ oem / appregistrations”。此注册步骤对于每个AppKey仅需要一次,并且可以使用任何REST客户端(如Postman)完成。

TeamViewer (Classic)应用程序和MSA之间的通信使用本地Android Binder界面,在注册REACH API时得到的AIDL文件中有描述。

在进行任何其他调用之前,验证方法需要首先被验证。验证方法需要用到注册MSA AppKey帐户时的AccountID和注册时返回的KeyID。如果验证成功,则可以任意调用Binder服务的方法,否则会出现SecurityException。

除了应用程序注册以外,Android上的主要区别还在于如何获取RolloutKey。含有转出数据的文件将写入TeamViewer (Classic)软件中的私有应用程序中存储,且只能用于读取TeamViewer (Classic)应用程序的信息。因此,MSA应用程序必须通过Binder界面请求部署数据。成功获取部署数据后,使用Binder接口上的requestPreKeyData,可以按照《REACH API设备的部署》中所述的方式继续操作。

  • 广告合作

  • QQ群号:707632017

温馨提示:
1、本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。 2、本站原创内容未经允许不得转裁,转载请注明出处“站长百科”和原文地址。
TeamViewer API集成
上一篇: TeamViewer API介绍
TeamViewer API集成
下一篇: TeamViewer使用API