Redireccionamento para o Localizador de Evidência

Após receber os dados e pedido do cidadão, o portal de serviços necessita de comunicar e enviar o pedido para o localizador de evidência. Com base nos requisitos técnicos OOTS (Once-only Technical System), além dos campos a enviar, ficou definido até ao momento da redação deste documento que:

  • O pedido é enviado por parâmetros de URL;

  • O localizador de evidência não requer autenticação do cidadão do pedido;

  • A informação enviada não é particularmente sensível, mas pode ser manipulada nos parâmetros do URL;

  • Gestão de chaves de encriptação com todos os portais com que o localizador irá comunicar pode ser uma tarefa complexa e a sua necessidade será revista após a fase de testes do OOTS Projectathon.

Este tema ainda está sob consulta e é possível que haja alterações se for determinado ser necessário um método mais seguro.

Com base nesta informação, uma vez que não requer autenticação, e de forma a não expor tão facilmente os campos do pedido que podem ser adulterados no endereço URL da página, foi pensada a seguinte solução de ofuscação que procura ser simples sem recurso a métodos de encriptação mais complexos:

  1. Criação de uma Mask Key (Chave de Ofuscação) única associada ao portal de serviços:

    • Esta chave é composta por uma string de 255 caracteres alfanuméricos;

    • A chave é gerada pela equipa responsável pelo localizador de evidência durante o processo de integração, e é fornecida ao portal de serviços através de um canal seguro (a ser determinado);

    • A chave é secreta no sentido em que nunca deve ser exposta ao público;

    • A chave é única para cada portal de serviços.

  2. O portal de serviços codifica e mascara parte do conteúdo do pedido de evidência com o auxílio da chave de codificação:

    • A codificação é realizada através de uma cifra de substituição XOR de forma a não ser demasiado simples como um único encoding em Base64, nem demasiado complexa como o recurso a criptografia;

    • O pedido de evidência é assumindo como partindo de um JSON string e depois convertido a Base64, seguido da codificação por uma cifra de substituição com a chave de ofuscação;

    • O resultado deverá ser um conjunto de caracteres alfanuméricos representados na constante MASK_CHARS;

    • É um método simples de encriptação simétrica e ofuscação, relativamente robusto a ataques de força bruta, cuja segurança está fortemente dependente do sigilo da chave.

  3. O pedido codificado deverá ser depois encoded para URL e enviado para a página do localizador de evidência como parâmetro URL (ev-request) juntamente com o código identificador do portal de serviços (portal-id).

  4. O localizador de evidência depois irá obter, internamente, a chave de codificação específica ao portal de serviços associado ao pedido através do identificador do portal e descodificar o pedido para obter o JSON string do pedido original.

Para aplicar os passos 2 e 3 acima, o portal de serviços deverá implementar o seguinte código. Para efeitos práticos e de apresentação, iremos apresentar o código na linguagem Java e os passos mais importantes em sequência:

  1. Obter a Mask Key que será fornecida ao portal de serviços durante o processo de integração e que estará especificamente associada ao portal de serviços que pretende integrar. Para este exemplo teremos a chave “42oszYRtoj3BgXu9” a título exemplificativo. Contudo, será fornecida mais tarde uma chave com 255 caracteres alfanuméricos durante o processo de integração.

  2. Implementar os seguintes métodos auxiliares de codificação associados à chave de codificação:

/** Caracteres que serão codificados **/
private static final String MASK_CHARS = 
 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZàáâãçéêíóôõú0123456789{}"/':-,”;
 
/** Conversão a Base64 **/
/** Importante converter o input a bytes para não haver problemas com nonASCII characters **/
public static String encodeToBase64(String input) {
 byte[] encodedBytes = Base64.getEncoder().encode(input.getBytes());
 return new String(encodedBytes);
}

/** Codificação do pedido com base na Mask Key **/
public static String mask(String base64Input, String maskKey) {
 StringBuilder maskedText = new StringBuilder();
 String repeatedMaskKey = repeatMaskKey(base64Input, maskKey);
 
 for (int i = 0; i < base64Input.length(); i++) {
  char currentChar = base64Input.charAt(i);
  char maskChar = repeatedMaskKey.charAt(i);
  int alphabetIndex = MASK_CHARS.indexOf(currentChar);
  
  if (alphabetIndex != -1) {
    int newIndex = (alphabetIndex + maskChar - 'a') % MASK_CHARS.length();
  
    if (newIndex < 0) {
      newIndex += MASK_CHARS.length();
    }
    
    maskedText.append(MASK_CHARS.charAt(newIndex));
  } else {
    maskedText.append(currentChar);
  }
}
 
 return maskedText.toString();
}

/** Mask Key é repetida para acompanhar a totalidade do input **/
private static String repeatMaskKey(String input, String maskKey) {
  StringBuilder repeatedMaskKey = new StringBuilder();
  int textLength = input.length();

  for (int i = 0; i < textLength; i++) {
    char maskChar = maskKey.charAt(i % maskKey.length());
    repeatedMaskKey.append(maskChar);
  }

  return repeatedMaskKey.toString();
}

/** Encoding para URL de modo a poder ser enviado como parâmetro **/
public static String encodeURLParameter(String text) {
  try {
    return URLEncoder.encode(text, StandardCharsets.UTF_8.toString());
  } catch (Exception e) {
    e.printStackTrace();
  }

  return "";
}
  1. A partir do JSON String do pedido, aplicar a seguinte sequência de codificações até obter o parâmetro a ser enviado. A demonstração abaixo incluirá um exemplo de um pedido JSON num formato já desatualizado, contudo pode ser aplicado a qualquer tipo de input desde que se trate de um JSON com a estrutura de dados correcta e tenha um encoding UTF-8.

String jsonInput =
"{\"requirementId\":\"12345678\",\"redirectUrl\":\"https://eportugal.gov.pt\",\
"personData\":{\"levelOfAssurance\":\"high\",\"id\":\"PT/NL/123456789\",\"famil
yName\":\"Smith\",\"givenName\":\"Jack\",\"dateOfBirth\":\"1990-01-01\"}}";

String maskKey = "42oszYRtoj3BgXu9";
String base64JsonInput = encodeToBase64(jsonInput);
// Resultado:
eyJyZXF1aXJlbWVudElkIjoiMTIzNDU2NzgiLCJyZWRpcmVjdFVybCI6Imh0dHBzOi8vZXBvcnR1Z2F
sLmdvdi5wdCIsInBlcnNvbkRhdGEiOnsibGV2ZWxPZkFzc3VyYW5jZSI6ImhpZ2giLCJpZCI6IlBUL0
5MLzEyMzQ1Njc4OSIsImZhbWlseU5hbWUiOiJTbWl0aCIsImdpdmVuTmFtZSI6IkphY2siLCJkYXRlT
2ZCaXJ0aCI6IjE5OTAtMDEtMDEifX0=

String maskedInput = mask(base64JsonInput, maskKey);
// Resultado:
DVXQgPqco67YhN:YC1zC7b,B026"Tu'o"WuA{uuR/5:2id:NC29QAuthWvFvjyV3/FeNgP
mOqw:w5TZW{JrNCaQPrL65OeVPBK1NAcCArP2VUeMMA39cgOi8/t3"iU:2fbbBgKthWvF25TAM{ZXHg
uthWuZpRRh-
{WSQ}rBc1sAzUJ2W7J/zAO'Ls3lUhN'M/FX}AO'boL65OdxTCJ9MaeqM/16BObJLfhGA{uuD"6:YZTb
6zcXazuthWs2AUKUX}0SL}vpBt6g=

String urlEncodedInput = encodeURLParameter(maskedInput);
// Resultado:
DVXQgPqco67YhN%3AYC1zC7b%2CB026%22Tu%27o%22WuA%7BuuR%2F5%3A2id%3ANC29Q
AuthWvFvjyV3%2FFeNgPmOqw%3Aw5TZW%7BJrNCaQPrL65OeVPBK1NAcCArP2VUeMMA39cgOi8%2Ft3
%22iU%3A2fbbBgKthWvF25TAM%7BZXHguthWuZpRRh-
%7BWSQ%7DrBc1sAzUJ2W7J%2FzAO%27Ls3lUhN%27M%2FFX%7DAO%27boL65OdxTCJ9MaeqM%2F16BO
bJLfhGA%7BuuD%226%3AYZTb6zcXazuthWs2AUKUX%7D0SL%7DvpBt6g%3D
  1. Uma vez obtida a JSON string codificada do pedido, enviar para a página do localizador de evidências como um parâmetro URL de acordo com a especificações abaixo.

Parâmetros URL

Parâmetro
Descrição
Observações

ev-request

O JSON String do pedido após codificação em base64, codificação com chave única, e codificação URL.

Conteúdo do pedido “EvidenceRequest” pode ser consultado abaixo.

portal-id

Código de identificação do portal de serviços.

O código é fornecido pelo localizador de evidência para identificar o portal de serviços localmente.

request-id

Código de identificação gerado pelo portal de serviços.

Utilizado para identificar todas as invocações de serviço relacionadas com o processo global de pedido de evidência.

Exemplo de URL de Redireccionamento

http://[domínio AutGov]/evidence-request?ev-request=DTCy7uuR%2F63wgO3PAb9MCuthWL71jy%7D0BWCN%7BVyRZvp8jy6YC39Bz uQEr678ixYYgc8NBeGQr5J%7D5NdPAK5R%7BrqE0M%7BySd2p%7BuR%22vxM1MaxStcqgGua%7BOqD%22sbY5N6m%7DPC%22atLQrYTOeV2Ah1DgzGR%2F16BOt3i%7D%3AWK1alzWwZYie7ZAFWg7zd3WLYTO t3Wgc%2FDAwUF46%7D%22jO3LAJ1D7b%2CzWvF25TAM%7BXCy7ulzWvJXOaIK7IP%22 %7BSQ5ZI2%7DSq%7Bn%22Gqe%2FKtLQrYTOtUMgJTLzOig7v365J2s7ZX%7DAO'boL65I %2CUK7ZOAgU'd%2F5lj4NdP7GCy7caA%22%7DQVRrIK7ZOy7eCArPbk5b3TBK5G7b%2 CzWs2AUKUX%7D0SL%7DvpBQrYTlJQ'7ZOABeGDo67Y4U%7DhBJKA%2FalBoQ%3Aviy 6s%7BVfDByURrQb04NQYghfc%7BfmbWrUfOtUMBJ9PCOGSrPb%7D1yF0g%3AWg7u ucqvkB4T%3AQ%7BJ9b%2FeQAp5b%22UdFOgbbazOKI%2F66BXLZf%2FGyf%22vJBZJM TOt32gcTbgPyb%2F67j4NdPBVWg7xd3WLYTOyM'7ZOy7ulzWvVUhdwM%2FFOA-MPBZJMTOtUK7ZOAAeqM%2F16BOt3cBJfNgamO%2FrZ%225N7ZAJ5zBf%2Fz%2F5%3A w4TZmzbfM7yCIqPV8hNYZfh9QCy'Fo5%7DUjx'M1FOy7umkQrYT3JQ'7ZOAgfGLpN3X5y 3PBi0A%2FalB8wbUOx%7DP711HAOpLWMazO%2CQ'7ZOAAyUC%226%3A8ib%7DPBhz FAeqbp%226VU%2CUM%22-uA%7Bs%2CzWL79hU7m%3AhfCgKthWL6vSu'0%7B4b%22atLQrYTOeVZBi5VzPCg7v365J2s7ZX8AVubpH65I%2CUK7JTCAO'N95l2jvRPCJ9K% 7DKthWL7l1t2W1FOy7eqDp5J71NhTC1LDCeGL0r6BOt2M%7BXCy7uuPp%22%7D%22gN 3TA3zaDMKOquZ%7D5ObTgcqA%2FambqwbYIec%3D&portal-id=PMC-TEST&request-id=bf08b6db-3edd-47c3-b48b-b9ae5f3eae92

EvidenceRequest

Campo
Descrição
Observações

requirement

Identificador do requerimento

procedure

Identificador do procedimento

redirectUrl

Endereço para o redireccionamento de volta ao portal de serviços após o localizador enviar a evidência no final do processo

Endereço deve ser URL encoded; Endereço será também utilizado em caso de erro que não permita proceder no processo.

+ person

Dados da pessoa (natural person).

++ levelOfAssurance

O nível mínimo de garantia eIDAS

“High”, “Medium”, ou “Low”. Case sensitive.

++ id

Identificador único eIDA

Opcional

++ familyName

Apelido da pessoa

++ givenName

Primeiro nome da pessoa

++ dateOfBirth

Data de nascimento da pessoa

Formato yyyy-MM-dd

requesterId

Identificador do portal

requesterType

Identificador do tipo de portal

+ requesterNames

Lista de nomes do portal em vários idioma

Lista

++ lang

Idioma

“EN”, “PT” …

++ name

Nome do portal no idioma

fullAddress

Morada da entidade solicitadora (portal)

Opcional

locatorDesignator

Nº da porta ou nº do edifício da entidade solicitadora

Opcional

postCode

Código postal da entidade solicitadora

Opcional

postCityName

Localidade da entidade solicitadora

Opcional

adminUnitLevel1

Código do nível mais alto da morada (quase sempre o código do país)

Opcional

adminUnitLevel2

Código do nível secundário da morada (costuma ser um código da cidade ou região)

Opcional

possibilityForPreview

Indica se é um pedido de evidência que com possibilidade de pré-visualização da evidência

Booleano

Exemplo de JSON do EvidenceRequest

{
 "requirement": "https://sr.oots.tech.europa.eu/requirements/f8a6a284-34e9-42c7-9733-63b5c4f4aa42",
 "procedure": "T1",
 "person": {
  "levelOfAssurance": "High",
   "id": "PT/NL/123456789",
   "familyName": "Smith",
   "givenName": "Jack",
   "dateOfBirth": "1990-01-01"
 },
 "redirectUrl": "https%3A%2F%2Fportaldeprocedimentos.pt",
 "requesterType": "urn:cef.eu:names:identifier:EAS:9946",
 "requesterId": "9988b2b6-a23b-41d7-b373-7160e2190b38",
 "requesterNames": [
  {
   "lang": "EN",
   "name": "Procedure Portal"
  },
  {
   "lang": "PT",
   "name": "Portal de Procedimentos"
  }
 ],
 "fullAddress": "Rua de Cima, 58",
 "locatorDesignator": "58",
 "postCode": "4050-456",
 "postCityName": "Porto",
 "adminUnitLevel1": "PT",
 "adminUnitLevel2": "",
 "possibilityForPreview": true
}

Last updated