Con la API REACH, TeamViewer ofrece una API para la integración con soluciones de RMM y MDM que permite iniciar sesiones de acceso no presencial y control remoto.
El propósito de este documento es explicar los conceptos fundamentales tras la API REACH, así como proporcionar instrucciones y directrices para integrarla en una solución de gestión.
Con este objetivo, el documento se ha estructurado en tres secciones diferenciadas que proporcionan información sobre los distintos temas.
El primer capítulo explica el enfoque conceptual básico y ofrece un resumen de las razones por las que se han aplicado ciertos mecanismos de seguridad.
A continuación, se presenta un ejemplo de implementación basada en un código fuente de ejemplo de una integración de ejemplo, a la que se denomina Aplicación De Ejemplo, desarrollada por TeamViewer.
En el último capítulo se describen las particularidades de la implementación de la integración en Android.
Recuerda que las integraciones no están incluidas en los paquetes de licencias básicas. Deben adquirirse por separado como complementos. Para más información, visita nuestra página web: http://www.teamviewer.com/es/integrations
📌Nota: Todos los ejemplos de código utilizados en este artículo están publicados bajo la licencia MIT:
Copyright (c) 2018 TeamViewer GmbH
Por la presente se otorga permiso, libre de costes, a cualquier persona que obtenga una copia de este software y su documentación relacionada (denominados conjuntamente «el Software»), para comerciar con el Software sin restricciones, incluyendo pero sin limitarse necesariamente al derecho a utilizar, copiar, modificar, fusionar, publicar, distribuir, emitir sublicencias y vender copias del Software, y a permitir a las personas a las que se suministre el Software hacer lo propio, de conformidad con las siguientes condiciones:
El aviso de copyright anterior, así como esta autorización, deben incluirse en todas las copias o partes significativas del Software.
ESTE SOFTWARE SE SUMINISTRA «EN LAS CONDICIONES EN LAS QUE SE ENCUENTRA», SIN GARANTÍA DE NINGÚN TIPO, YA SEA EXPLÍCITA O IMPLÍCITA, INCLUYENDO PERO SIN LIMITARSE NECESARIAMENTE A GARANTÍAS DE COMERCIALIZACIÓN, DE ADECUACIÓN A UN PROPÓSITO ESPECÍFICO O CONTRA INFRACCIONES. LOS AUTORES O PROPIETARIOS DEL COPYRIGHT NO ASUMEN RESPONSABILIDAD ALGUNA POR RECLAMACIONES, DAÑOS U OTROS, YA SEA EN UNA ACCIÓN DE CONTRATO, FRAUDULENTA O DE OTRO TIPO, DERIVADOS DE, INDEPENDIENTES DE O EN RELACIÓN CON EL SOFTWARE O SU USO, U OTRAS TRANSACCIONES RELACIONADAS CON ESTE.
Respete cualquier licencia de terceros del software incluido, si la hubiera, al reutilizar o redistribuir el software.
El concepto base de la API REACH se divide en tres casos de uso:
El despliegue se utiliza para intercambiar claves de cifrado entre un dispositivo y la solución de gestión. Estas claves de cifrado se utilizan para las fases de control y eliminación del registro y para garantizar que todas las comunicaciones con el dispositivo sean seguras.
El siguiente diagrama describe el flujo del registro:
El agente de la solución de gestión (MSA por sus siglas en inglés) es un componente que debe ejecutarse en el dispositivo. Este agente debe tener derechos de administrador en el dispositivo y recupera la información necesaria para completar con éxito la fase de despliegue.
Para iniciar el despliegue, el MSA lee un archivo especial (paso 1) que se escribe al iniciarse el cliente de TeamViewer y después de cada solicitud de despliegue al cliente de TeamViewer, ya sea exitosa o fallida.
📌Nota: El paso 1 puede ser distinto en cada plataforma. El método del archivo solo se aplica a plataformas Windows y macOS. No obstante, la información proporcionada para el despliegue es la misma en todas las plataformas. En el caso específico de Android, consulte el capítulo de Android.
Este archivo contiene la clave de despliegue (RolloutKey o ROK por sus siglas en inglés, válida para una sola solicitud, que se utiliza para desencriptar la respuesta de la API), un identificador único universal (GUID por sus siglas en inglés) y el ID de control remoto del dispositivo o RemotecontrolID (ID de TeamViewer).
Con el RemotecontrolID, el GUID de la información de despliegue y un conjunto de permisos, se efectúa una llamada de la API (POST /oem/devices/createdevicekey) para crear una nueva clave de dispositivo (paso 3).
Esta llamada inicia la comunicación desde el extremo de origen de TeamViewer al client objetivo de TeamViewer.
Si la información es válida, el client de TeamViewer creará un par de claves para futuras sesiones de control remoto.
El client de TeamViewer encripta la clave privada (=DeviceKey) del par recién creado con la RolloutKey y la envía junto con un identificador para el par de claves y un token de desinstalación como respuesta a la llamada de la API, pasando por el extremo de origen de TeamViewer (paso 4).
La solución de gestión debe utilizar la RolloutKey obtenida previamente para desencriptar la DeviceKey. Para simplificar el proceso de descifrado, la DeviceKey se encripta en formato PEM (correo con privacidad mejorada).
La DeviceKey y el ID de la clave del dispositivo (DeviceKeyID) se utilizan durante la fase de control, mientras que el token de desinstalación es necesario para eliminar el registro de la DeviceKey.
📌Nota: La DeviceKey desencriptada, el DeviceKeyID y el token de desinstalación deben guardarse de manera segura en el lado de la solución de gestión.
Una vez completado el primer paso, es posible iniciar sesiones de control remoto al dispositivo registrado. Para iniciar una sesión de control remoto, se aplica el siguiente proceso:
El control remoto solo es posible tras registrar un dispositivo con éxito, ya que la DeviceKey y el DeviceKeyID son necesarios para iniciar una sesión de control remoto.
El primer paso es una llamada de la WebAPI (POST /oem/devices/requestcontrol) con los parámetros RemotecontrolID, DeviceKeyID y tipo de control. Los parámetros se transferirán al client objetivo, que verifica si la DeviceKey tiene los permisos de control que se requieren en la llamada de la API.
Tras verificarse con éxito la solicitud, el client objetivo de TeamViewer genera un valor secreto temporal de un solo uso (un código de autenticación de mensajes en clave-hash, HMAC), similar a una contraseña de sesión.
El HMAC se calcula en base a la información recibida a través de la solicitud de control y otros datos (nonce objetivo, valor secreto del dispositivo o DeviceSecret) que genera el client de TeamViewer.
Tras calcular el HMAC, el client encripta el DeviceSecret con la DeviceKey que se ha especificado en la llamada de la API a través del DeviceKeyID.
El DeviceSecret se devuelve como parámetro de respuesta de la llamada de la API llamado EncryptedDeviceSecret junto con el nonce objetivo y una plantilla de URL de un protocolo de URL de TeamViewer («teamviewerapi://») que incluye el marcador de posición YOURMASTERSECRET.
La implementación del VIS debe desencriptar el EncryptedDeviceSecret con la DeviceKey y calcular el HMAC en base al valor secreto desencriptado, el nonce objetivo y la información enviada en la llamada de solicitud de control de la API.
Tras calcular el HMAC, el marcador de posición YOURMASTERSECRET debe sustituirse por el HMAC para crear un protocolo de URL de TeamViewer válido.
La URL completa que contiene el HMAC debe presentarse al client origen de TeamViewer para iniciar una conexión.
Esto puede ocurrir a través de un navegador o en forma de parámetro de línea de comandos (el protocolo de TeamViewer se registra en el sistema operativo cuando TeamViewer se instala y se asocia con la aplicación de TeamViewer (Classic)).
El client origen de TeamViewer utiliza la información de la URL, incluyendo el HMAC, para establecer una conexión con el cliente objetivo.
En caso de que una DeviceKey no vaya a volver a utilizarse, su registro puede eliminarse. El siguiente diagrama muestra el principio básico:
Para eliminar una DeviceKey, se utiliza la llamada de la WebAPI DELETE /oem/devices/unregister.
Para una ejecución exitosa, se requiere que el DeviceKeyID identifique la clave correcta, el RemotecontrolID del dispositivo y el token de desinstalación que se ha generado en la respuesta a la llamada de la API POST /oem/devices/createdevicekey durante la fase de despliegue.
Esta información será procesada por el client de TeamViewer en el dispositivo.
Si la información de la API coincide con la información del client, se eliminará la parte local del par de claves creado en la fase de despliegue y se devolverá una confirmación a través de la API.
A partir de este momento, no podrá establecerse ninguna sesión de control remoto utilizando esta DeviceKey.
Para seguir los pasos de esta guía y realizar la integración real, se necesitan ciertos datos a lo largo de las distintas fases de la integración:
El VIS ha recibido el ID del proveedor a través de un representante de TeamViewer.
A continuación, puede crearse la cuenta del inquilino con un token de script de la cuenta del proveedor con permisos de gestión de inquilino.
Por ejemplo, puede utilizar una herramienta como Postman para realizar las llamadas de la WebAPI.
Finalmente, se necesita un token de script de la cuenta del inquilino que tenga permisos para crear claves de dispositivo, solicitar control y eliminar claves de dispositivos.
Si necesitas instrucciones para crear un token de script, consulta https://www.teamviewer.com/es/for-developers/#create-script-section.
Para el ejemplo de integración, los ejemplos de código proporcionados emplean las siguientes tecnologías y bibliotecas:
Para Android se requiere adicionalmente la siguiente información:
La clave de aplicación (AppKey) es la firma del MSA del dispositivo Android y el ID de la clave de la aplicación (AppKeyID) se utiliza como índice junto con el ID de la cuenta. Ambos valores se devuelven con la respuesta a la llamada de la WebAPI para el registro de la AppKey en la consola web.
Consejo: Ten en cuenta que debes sustituir todos los valores empleados en la explicación por sus propios valores.
El siguiente diagrama muestra un breve resumen de las partes del lado del proveedor en las que debe realizarse la integración.
La llamada de la API puede realizarse con cualquier clase de cliente HTTP.
La precondición para esta llamada de la WebAPI es la lectura de la información del archivo del despliegue escrito por el cliente de TeamViewer.
Este archivo es legible en plataformas Windows y macOS con permisos de administrador.
En dispositivos Android, se requiere el registro de la aplicación del MSA antes de acceder a la información del despliegue. Esto se explica separadamente en el capítulo siguiente, que describe la integración con Android.
Para nuestro ejemplo, nos referimos al código tal como se muestra en la AplicaciónDeEjemplo, que está disponible en el código fuente. En primer lugar, debes crear el cuerpo de JSON, por ejemplo utilizando una clase de datos, como en el ejemplo siguiente de creación de clase de datos.
Ten en cuenta que la información del RemotecontrolID (ID de control remoto) y el RequestID (ID de solicitud) se origina en el archivo del despliegue:
Creación del cuerpo de la solicitud
var createDeviceKeyRequest = new CreateDeviceKeyRequest { key_permissions = unattended, remotecontrol_id = r124124124, request_id = {8c4fa1a7-9d1c-41e9-be17-70e95c289080}, tenant_id = t0001 };
Una vez creada esta clase de datos y rellenada con la información, la llamada de la WebAPI puede realizarse especificando la URL y el método, rellenando el encabezado de solicitud HTTP con la información de autenticación requerida y posteriormente adjuntando el cuerpo de JSON que contiene la clase de datos anterior.
Para añadir la autorización del encabezado HTTP es necesario crear un token de script desde la Management Console de TeamViewer (Classic).
Es necesario comprobar que este token tenga los permisos correctos para ejecutar las llamadas de la WebAPI.
Solo pueden otorgarse estos permisos al token de script si la cuenta a la que corresponde el token pertenece a un inquilino.
En el siguiente cuadro te mostramos un ejemplo de código para emitir la llamada que analiza la respuesta y devuelve la clase de datos sin serializar que contiene los valores de respuesta. El ejemplo de código se ha extraído de la aplicación del proveedor.
Solicitud de 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); }
Con la respuesta de la API, se obtiene la DeviceKey encriptada.
Para descifrar la DeviceKey encriptada en formato PEM, hemos escogido la biblioteca BouncyCastle , dado que facilita bastante el proceso.
BouncyCastle también está disponible para Java. No obstante, existen muchas bibliotecas para cada lenguaje capaces de leer archivos PEM.
El algoritmo de cifrado se indica directamente en el archivo PEM que se obtiene de la solicitud de la API.
Ejemplo:
-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,309AA16\n-----END RSA PRIVATE KEY-----\n
Por razones de seguridad, ES MUY RECOMENDABLE utilizar una biblioteca para gestionar el trabajo, ya que normalmente tienen la capacidad de identificar automáticamente el algoritmo correcto.
En caso de producirse un problema de seguridad con el algoritmo, lo modificaremos.
Si se utiliza una biblioteca adecuada, no se requerirán cambios en el lado del VIS, mientras que una implementación manual es menos flexible.
Utilizando Bouncy Castle, el descifrado de la clave PEM se reduce al código del ejemplo siguiente.
Para simplificar el proceso, guardamos la deviceKey en forma de cadena en formato PEM, ya que la biblioteca BouncyCastle funciona bien con él y es almacenable como cadena.
Descifrado de clave 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;
Después de este paso, es importante que guardes la DeviceKey junto con el DeviceKeyID y el RemotecontrolID devueltos en la respuesta de la llamada de la WebAPI en el contexto de tu solución de gestión.
Del mismo modo, debes guardar el token de desinstalación, ya que se requerirá más adelante para eliminar el registro de una DeviceKey.
Con los datos de la fase anterior, la DeviceKey y el DeviceKeyID, puede establecerse una sesión de control remoto en el dispositivo registrado.
Es esencial que en la solicitud de control remoto utilice el mismo tipo de control para el que ha solicitado permiso en la fase de despliegue.
Nuevamente, en primer lugar se rellena una instancia de la clase de datos con la información requerida para la llamada de la WebAPI.
Clase de datos de solicitud de control
var requestControlRequest = new RequestControlRequest() { control_type = attended, device_key_id = {114fa1a7-abab-41e9-be17-70e95c289080}, remotecontrol_id = r124124124, tenant_id = t0001, tenant_nonce = 243dd78d07324ab7befa41390d08d35f };
Con esta instancia de datos, la llamada de la WebAPI puede realizarse como en el siguiente código en C#.
De nuevo se añade en primer lugar la URL, después el encabezado de autorización HTTP con el token de script y finalmente la instancia de la clase de datos serializada como cuerpo de JSON.
Una vez que se devuelva la solicitud con una respuesta, los datos pueden deserializarse a una instancia de clase de datos propia que proporcione la plantilla para el enlace de TeamViewer.
Este enlace se completará con un código de autenticación de mensajes en clave-hash (HMAC).
Llamada de control remoto
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); }
Dado que la respuesta de la API solo devuelve una plantilla de enlace, debe completarse con el HMAC.
El valor secreto para crear el HMAC se devuelve encriptado en la respuesta de la API, por lo que el primer paso es descifrarlo.
La clave de descifrado es la DeviceKey recibida en la respuesta de la API durante la fase de despliegue.
Como la DeviceKey se almacena en forma de cadena en formato PEM, la primera parte del código convierte esta cadena en una estructura adecuada para BouncyCastle (la variable de tipo «AsymmetricCipherKeyPair»).
Descifrado del 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); }
Con el DeviceSecret desencriptado, el ID del inquilino (TenantID), el RemotecontrolID del objetivo y los nonces del objetivo y el inquilino, puede generarse el HMAC como en el ejemplo siguiente, que de nuevo se implementa utilizando la biblioteca de seguridad BouncyCastle.
Generación del Mastersecret
//---------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();
El resultado se incorpora a la plantilla de enlace de TeamViewer sustituyendo a la cadena YOURMASTERSECRET.
Con la cadena completada, la sesión de control remoto puede establecerse abriendo el enlace en el navegador web de un sistema en el que haya instalado un client de empresa de TeamViewer, o bien introduciendo el enlace en la línea de comandos de una llamada a TeamViewer.exe
La llamada de eliminación del registro del dispositivo requiere los siguientes parámetros para ejecutar la eliminación del registro con éxito:
Al igual que en las fases anteriores, hay que crear una instancia de clase de datos para esta llamada específica con los datos concretos para realizar la consiguiente llamada a la WebAPI:
Creación de estructura de datos
var unregisterDeviceRequest = new UnregisterDeviceRequest { tenant_id = t0001, remotecontrol_id = r124124124, device_key_id = {114fa1a7-abab-41e9-be17-70e95c289080}, decommission_token = _mState.LastDecommissionToken };
De nuevo se sigue el mismo patrón de implementación, tal y como se muestra anteriormente: se añade la URL de la llamada de la WebAPI, el método y el token de script de la aplicación al encabezado de autorización HTTP y, finalmente, se adjunta el contenido serializado de la clase de datos previamente creada en formato JSON.
Eliminación del registro
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; }
En este caso, el resultado devuelto no contendrá datos.
El éxito de la operación solo puede confirmarse comprobando el valor devuelto, que debe ser 204 (véase también Valores devueltos comunes de HTTP).
Debido a ciertas restricciones de plataforma en Android (ausencia de separación real de privilegios de administrador), el despliegue difiere del de otras plataformas. Es necesario realizar una verificación del MSA antes de que este pueda comunicarse con la aplicación de TeamViewer para Android y la RolloutKey se obtiene de forma diferente.
La función hash SHA-256 de la clave de firma del MSA (denominada AppKey de ahora en adelante) debe registrarse en TeamViewer como cadena hexadecimal utilizando la llamada POST de la WebAPI «/oem/appregistrations».
Este paso de registro solo se requiere una vez por cada AppKey y puede realizarse con cualquier client REST, como Postman.
La comunicación entre la aplicación de TeamViewer y el MSA emplea la interfaz Binder nativa de Android (https://developer.android.com/reference/android/os/Binder.html), tal como se describe en los archivos AIDL que recibiste al registrarse para la API REACH.
Antes de realizar cualquier llamada, debe invocarse el método de verificación. El método de verificación requiere el ID de la cuenta (AccountID) que se utilizó para registrar la AppKey del MSA y el ID de clave (KeyID) devuelto durante el registro. Si la verificación tiene éxito, puedes invocar cualquiera de los métodos del servicio del Binder. De lo contrario, se lanzará una excepción de seguridad.
La principal diferencia en Android, al margen del registro de la aplicación, es la forma de obtener la RolloutKey. El archivo que contiene la información del despliegue se escribe en el almacenamiento privado de la aplicación de TeamViewer. Solo la aplicación de TeamViewer es capaz de leer esta información. Por esta razón, la aplicación del MSA tiene que solicitar los datos del despliegue a través de la interfaz Binder. Tras obtener con éxito los datos del despliegue utilizando el método «requestPreKeyData» en la interfaz Binder, puedes proceder del mismo modo que se describe en el capítulo Despliegue de un dispositivo para la API REACH