远程访问API集成指南

无评分

 本文适用于使用TeamViewer进行集成的合作商。 

通用

通过REACH API,TeamViewer提供了一个用于集成到RMM或MDM解决方案的API,可以让客户方便启动无人值守和有人值守的远程控制会话。

本文件的目的是解释REACH API的基本概念,并说明和指导如何将API集成到管理解决方案中。

因此,本文被分为多个部分,并提供相关多个主题的信息。

第一章解释了基本概念方法,并概述了为何需要应用了某些安全机制。

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

在最后一章中,概述了如何在Android上实现集成。

注意:本文中的所有代码示例都是在MIT许可证下发布的:

版权所有 (c) 2018 TeamViewer GmbH

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

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

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

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

 

REACH API - 概念和安全性

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

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

设备注册

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

该图解释了注册流程:

REACH API_1.pngManagement Solution Agent(MSA)是必须在设备上运行的组件。运行此代理必须具有设备的管理权限,并能获取完成部署而所需的数据。

开始部署时,MSA会读取一个特殊文件(步骤1),该文件在TeamViewer客户端启动时以及每次成功或不成功的部署请求发送到TeamViewer客户端之后生成。

注意:每个平台上的步骤1可能不同。文件方法仅适用于Windows和macOS平台。但是,为所有平台提供的部署数据是相同的。有关Android的详细信息,请参阅Android章节。

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

API可用RemotecontrolID、部署数据的GUID和一系列权限调用(POST / oem / devices / createdevicekey)以创建新的设备密钥(步骤3)。

API调用能够启动TeamViewer后端至目标客户端的通信。

如果数据有效,TeamViewer客户端将为以后的远程控制会话创建密钥对。

通过TeamViewer 的后端,TeamViewer客户端对具有RolloutKey的新创建的密钥对的私钥(= DeviceKey)进行加密。同时将与密钥对的标识符和API调用的解除授权令牌一起作为回复发送给API调用行。(Step 4)

管理解决方案必须使用先前获得的RolloutKey来解密DeviceKey。为了简化解码过程,使用标准PEM格式加密DeviceKey。

DeviceKey和DeviceKeyID是在控制阶段使用的,而解除授权令牌是在需要注销DeviceKey的时候使用的。

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

控制阶段

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

REACH API_2.png

 

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

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

成功验证请求后,目标TeamViewer客户端会生成一次性临时密钥(一个Hash身份验证消息,HMAC),该秘钥与会话密码同等效果。

HMAC的确定是用控制请求所接收的数据和TeamViewer客户端生成的数据(Target nonce,DeviceSecret)进行计算。

在计算HMAC之后,客户端使用通过DeviceKeyID在API调用中指定的DeviceKey加密DeviceSecret。

DeviceSecret作为API的调用响应参数EncryptedDeviceSecret与目标随机数以及TeamViewer协议URL(“teamviewerapi://”)的URL模板一起返回,该URL模板包括占位符YOURMASTERSECRET

ISV执行必须使用DeviceKey解密EncryptedDeviceSecret,并根据解密的设备密钥,目标随机数和API调用请求中发送的其他信息计算HMAC。

计算出HMAC后,就必须用HMAC替换占位符YOURMASTERSECRET以构建有效的TeamViewer协议URL。

必须将包含HMAC的完整URL提供给源TeamViewer客户端以启动连接。

这可能会在浏览器或命令行参数中发生(当TeamViewer安装并与TeamViewer应用程序关联时,TeamViewer协议已在操作系统中注册)。

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

注销阶段

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

REACH API_3.png注销DeviceKey需要WebAPI调用DELETE / oem / devices / unregister。

要执行注销,需要DeviceKeyID识别正确的密钥、设备的RemotecontrolID以及部署阶段在POST / oem / devices / createdevicekey API调用所发出的解除授权令牌。

之后,此信息将由设备上的TeamViewer客户端处理。

如果API中的信息与客户端上的信息匹配,则部署阶段所创建的密钥对的本地部分将会被删除,并通过WebAPI返回确认信息。

从此时起,不能再使用此DeviceKey建立进一步的远程控制会话。

集成实例VendorExampleApp

要求

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

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

供应商ID已由TeamViewer发送给ISV。

之后,可以使用具有租户管理权限的供应商帐户的脚本令牌创建租户帐户。

您可以使用类似Postman的工具来发布WebAPI调用。

最后,需要来自租户帐户的脚本令牌,该令牌需要有创建设备密钥,请求控制和删除设备密钥的权限。

有关如何创建脚本令牌的说明,请参阅 TeamViewer API创建脚本 。

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

Android系统还需要以下信息::

  • APK签名值为您的MSA
  • APK签名的ID(由TeamViewer的WebAPI发布
  • TeamViewer帐户的ID

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

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

供应商端集成实施概述

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

REACH API_4.pngREACH API设备的部署

API调用可以使用任何HTTP客户端类完成

此WebAPI调用的前提条件是从TeamViewer客户端编写的部署文件中读取信息。

此文件在Windows和macOs平台上需要管理员权限才可读。

在Android设备上,访问部署数据需要注册MSA应用程序。这将在下面的Android集成一章中单独描述。

对于我们这里的示例,我们引用的代码是VendorExampleApp中源代码的一部分。首先,您必须创建JSON主体,可以选用数据类进行创建,例如下列示例中的方法创建此类数据类。

请注意,RemotecontrolIDRequestID的信息来自部署文件:

 

创建请求的正文

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管理控制台创建应用程序脚本标记。

必须确保此脚本标记具有执行WebAPI调用的正确权限。

如果该脚本令牌的帐户是租户,则这些权限只能授予给该脚本令牌。

请参阅以下框中的示例代码,该代码发出调用用以解析响应,并返回包含响应值的反序列化数据类。此示例代码来源于供应商应用程序。

请求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;

在此步骤之后,您必须在管理解决方案中存储DeviceKey以及在WebAPI调用中所返回的DeviceKeyID和RemotecontrolID,这很重要。

此外,您还需要保留再次注销DeviceKey所需的解除授权令牌。

发起远程控制会话

用上一阶段得到DeviceKey和DeviceKeyID的数据,可以向注册的设备发起远程控制会话。

在请求远程控制期间,您需要使用与部署阶段期间请求权限相同控件类型。

同样需要首先将WebAPI调用所需的信息填充到数据类实例中。

请求控制数据类

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链接提供模板。

之后,将使用Hash验证消息(HMAC)完成此链接。

远程控制呼叫

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”类型变量)。

解密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实现的。

生成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链接模板而不是字符串YOURMASTERSECRET

字符串完成后,打开有TeamViewer企业客户端的系统的Internet浏览器链接,或者通将此链接传递给TeamViewer.exe调用的命令行来建立远程控制会话。

取消注册设备

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

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

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

数据结构创建

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格式附加先前创建的数据类的序列化内容。

注销

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 Android应用程序通信,并以不同的方式获取RolloutKey。

MSA签名密钥的SHA-256 Hash值(此处称为AppKey)必须在TeamViewer中注册为十六进制字符串,并使用WebAPI调用POST“/ oem / appregistrations”。

此注册步骤对于每个AppKey仅需要一次,并且可以使用任何REST客户端(如Postman)完成。

TeamViewer应用程序和MSA之间的通信使用本地Android Binder界面(https://developer.android.com/reference/android/os/Binder.html),您在注册REACH API时得到的AIDL文件中有描述。

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

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

版本历史
修订号
2 / 2
上次更新时间:
‎28 八月 2018, 3:37 PM
更新依据:
 
贡献者