HTTPS no Kestrel

Sempre quando pensamos em hospedar uma aplicação desenvolvida em ASP.NET logo nos vem a mente o IIS. Apesar de uma infinidade de recursos que ele possui, o novo ASP.NET traz nativamente uma forma de hospedar as aplicações em um processo fora do IIS (self-hosting), chamado de Kestrel, qual foi construído com a finalidade de ser multi-plataforma e vem ganhando bastante evidência com alguns testes que demonstram grandes resultados em termos de performance.

Aos poucos a Microsoft vem adicionado nele algumas funcionalidades para que ele incorpore tudo que é necessário para utilizarmos em ambientes de produção. A finalidade deste artigo é demonstrar como configurar a aplicação para suportar o uso do protocolo HTTPS e, consequentemente, proteger os recursos que serão expostos pela aplicação.

Como sabemos, independentemente da tecnologia ou servidor que utilizamos para as nossas aplicações, para o uso do HTTPS se faz necessário a compra de um certificado de alguma entidade publicamente conhecida, para que possamos instalar em nosso servidor e configurar a nossa aplicação para utiliza-lo para estabelecer um túnel seguro de comunicação entre o cliente e o servidor e vice-versa. Como o foco é demonstrar a configuração, vou utilizar um certificado de testes emitido por uma ferramenta, sem valor para uso em ambiente de produção.

O primeiro passa em nossa aplicação é indicar ao runtime que o hosting a ser utilizado é o Kestrel, e para isso, recorremos ao método UseKestrel. Em um dos seus overloads é possível acessar algumas configurações e entre elas a possibilidade de indicar que queremos que o HTTPS seja utilizado, que é expressado através do método de estensão UseHttps. Para fazer uso deste método é necessário a instalação de um pacote NUGET chamado Microsoft.AspNetCore.Server.Kestrel.Https.

Uma vez que o certificado está criado e armazenado em um arquivo PFX (que inclui a sua chave privada) e o pacote acima mencionado esteja instalado na aplicação, utilizamos o código abaixo para indicar o local onde o arquivo PFX referente ao certificado está localizado no disco; note também que o “12345” é a senha para que o ASP.NET possa acessar o certificado.

public class Program
{
    public static void Main(string[] args)
    {
        new WebHostBuilder()
            .UseKestrel(opts => opts.UseHttps(@”C:tempAppTesteMeuWebSite.pfx”, “12345”))
            .UseUrls(“http://localhost:5000/”, “https://localhost:5001/”)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .Build()
            .Run();
    }
}

Ao rodar, a aplicação estará disponível para acesso tanto através do HTTP quanto HTTPS, porém em portas diferentes.

Consumindo Serviços da NF-e com WCF

Todas as empresas, quando vendem uma mercadoria são obrigadas a emitir nota fiscal. Até pouco tempo atrás, as notas não eram padronizadas e cada empresa utilizava em modelo diferente. O governo decidiu então criar um mecanismo para que fosse possível a emissão de forma eletrônica.

Com isso cada estado brasileiro criou uma porção de web services que permite a emissão, autorização, cancelamento, consulta de status, etc. Como é possível perceber nas URLs dos serviços, eles utilizam o ASP.NET Web Services (ASMX) para expor estas funcionalidades.

Como sabemos, o WCF é o pilar de comunicação dentro do .NET Framework, e mantém compatibilidade com os antigos serviços desenvolvidos em ASMX. Isso quer dizer que é possível consumir estes serviços com a API do WCF. Para que isso seja possível, alguns cuidados precisam ser tomados para garantir que o acesso será permitido.

Os serviços são protegidos e para acessá-los é necessário apresentar um certificado digital válido. Os certificados podem ser adquiridos por entidades certificadores, tais como Serasa, Certsign, VeriSign, etc. E vale dizer que até mesmo o WSDL, documento que descreve as funcionalidades do serviço, também está protegido, e precisamos também apresentar um certificado para simplesmente adicionar a referência no Visual Studio.

Vale lembrar que existem alguns tipos de certificados, e no meu caso, estou utilizando um certifcado de cartão A3 (e-CNPJ) que está protegido por um PIN (senha) que é necessário informá-la sempre que o certificado está prestes a ser utilizado. Como estou com ele devidamente instalado, ao acessar a URL do serviço no navegador, já aparece uma lista com os possíveis certificados, assim como podemos notar na imagem abaixo. Uma vez que o certificado é escolhido, então a senha será exigida e, consequentemente, teremos acesso à página padrão do ASP.NET que descreve o serviço.

Da mesma forma que acontece acima, o Visual Studio também exigirá o certificado ao realizar o “Add Service Reference“, e informando corretamente, o download do WSDL será realizado e a referência para o serviço será configurada. Atente-se aqui que não estamos utilizando a opção “Add Web Reference“, pois ela recorre a API antiga (SoapHttpClientProtocol) para acesso e não utiliza o WCF.

Apesar da ferramenta já criar uma configuração padrão, ela não é válida para o acesso. Precisamos ajustar para informar ao WCF que ele precisa utilizar um certificado antes de fazer a requisição ao serviço. Estamos optando por utilizar um binding customizado para que seja possível adequar a segurança e a serialização de acordo com as exigências da SEFAZ.

Entre as customizações, temos que utilizar a versão 1.2 do SOAP. Além disso, informar que o transporte será baseado em HTTPS e que é necessário um certificado do cliente para apresentar ao serviço para ter acesso ao recurso. A configuração do certificado é feita através de um behavior e associado ao endpoint do serviço. Abaixo temos o resumo do arquivo de configuração válido para realizar o acesso. Por questões de simplicidade, estou utilizando o serviço que retorna o status (operante ou não) dos demais serviços da SEFAZ.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name=”NFe”>
          <textMessageEncoding messageVersion=”Soap12″ />
          <httpsTransport requireClientCertificate=”true” />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name=”NFe”>
          <clientCredentials>
            <clientCertificate storeName=”My”
                               storeLocation=”CurrentUser”
                               x509FindType=”FindBySubjectName”
                               findValue=”ISRAEL AECE ME:………”/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint address=”https://nfe.fazenda.sp.gov.br/ws/nfestatusservico2.asmx&#8221;
                binding=”customBinding”
                bindingConfiguration=”NFe”
                behaviorConfiguration=”NFe”
                contract=”ServiceReference1.NfeStatusServico2Soap12″
                name=”ServiceReference1.NfeStatusServico2Soap12″ />
    </client>
  </system.serviceModel>
</configuration>

Uma vez que a configuração está concluída, podemos acessar o serviço e visualizarmos o retorno. Note que o código C# nada sabe sobre as configurações de certificado. É importante ressaltar que como este certificado tem um PIN, um prompt irá aparecer automaticamente para a digitação do mesmo. Na sequencia é possível visualizar o resultado que foi devolvido pelo serviço.

using (var client = new NfeStatusServico2Soap12Client())
{
    var corpo = new XmlDocument();
    var mensagem = “<?xml version=”1.0″ encoding=”utf-8″ ?>” +
                “<consStatServ xmlns=”http://www.portalfiscal.inf.br/nfe&#8221; versao=”3.10″>” +
                “<tpAmb>1</tpAmb>” +
                “<cUF>35</cUF>” +
                “<xServ>STATUS</xServ>” +
                “</consStatServ>”;

    corpo.LoadXml(mensagem);

    var resultado = client.nfeStatusServicoNF2(new nfeStatusServicoNF2Request()
    {
        nfeCabecMsg = new nfeCabecMsg()
        {
            cUF = “35”,
            versaoDados = “3.10”
        },
        nfeDadosMsg = corpo
    });

    Console.WriteLine(resultado.nfeStatusServicoNF2Result.OuterXml);
}

<retConsStatServ versao=”3.10″ xmlns=”http://www.portalfiscal.inf.br/nfe”><tpAmb
>1</tpAmb><verAplic>SP_NFE_PL_008f</verAplic><cStat>107</cStat><xMotivo>Serviço
em Operaçao</xMotivo><cUF>35</cUF><dhRecbto>2015-03-19T08:31:31-03:00</dhRecbto>
<tMed>1</tMed></retConsStatServ>

Validando a Assinatura Digital da NF-e

Atualmente as empresas emitem as notas fiscais em formato eletrônico. Para isso, foi desenvolvido um projeto que especifica e rege a emissão destas notas em ambiente nacional, que é conhecido como Nota Fiscal Eletrônica ou, simplesmente, NF-e. Este projeto faz uso de diversas tecnologias para garantir a segurança e, principalmente, a integridade da mesma.

A integridade é a garantia de que o arquivo XML que a representa não tenha sido alterado desde a sua emissão. Quando uma empresa emite uma nota fiscal, a SEFAZ gera um arquivo XML digitalmente assinado, utilizando o certificado digital da empresa emitente. Como sabemos, esse arquivo XML é a nota fiscal em formato eletrônico, e essa assinatura está embutida nele. A assinatura digital nada mais é que um cálculo matemático que é realizado sobre todo o arquivo XML, e se qualquer mudança ocorrer neste depois da assinatura, a mesma se tornará inválida.

Se você recebe este arquivo e quer garantir que ele não tenha sido alterado, então podemos recorrer à classes que estão dentro do .NET Framework para realizar esta validação. Basicamente devemos carregar o conteúdo XML para o objeto XmlDocument, e em seguida, procurar pelo elemento <Signature /> que é armazena o hash calculado e o certificado serializado que foi utilizado para realizar a assinatura.

var xml = new XmlDocument();
xml.Load(“00000000000000000000000000000000000000000000.xml”);

var nfe = (XmlElement)xml.GetElementsByTagName(“NFe”)[0];
var signature = (XmlElement)nfe.GetElementsByTagName(“Signature”)[0];
var signer = new SignedXml();

signer.LoadXml(signature);
Console.WriteLine(signer.CheckSignature());

Depois de carregado e separado os elementos que serão avaliados, recorrermos a classe SignedXml para validar a assinatura, onde devemos submeter o elemento que contém a assinatura para o método LoadXml, e em seguida, invocar o método CheckSignature, que retorna um valor boleano indicando se ela está ou não válida.

O overload do método CheckSignature utilizado acima (sem parâmetros) analisa o elemento <Signature /> tentando extrair os certificados de forma automática. Caso queira ter um maior controle sobre os certificados utilizados para analisar a assinatura, podemos utilizar um código mais rebuscado para atingir este objetivo:

private static bool ValidateWithCertificate(SignedXml signer)
{
    var keyInfoEnumerator = signer.KeyInfo.GetEnumerator();

    while (keyInfoEnumerator.MoveNext())
    {
        var x509Data = keyInfoEnumerator.Current as KeyInfoX509Data;

        if (x509Data != null && x509Data.Certificates.Count > 0)
            foreach (var cert in x509Data.Certificates)
                if (signer.CheckSignature((X509Certificate2)cert, true))
                    return true;
    }

    return false;
}

Como o próprio nome sugere, a propriedade KeyInfo contém informações referentes à chave que foi utilizada para realizar a assinatura. E, utilizando um enumerador, identificamos se trata-se de um certificado, e ao iterar pela coleção, invocamos o método CheckSignature, e através de um segundo overload, passamos o certificado que queremos que ele considere para realizer a validação. Apesar de ter o mesmo resultado, aqui é possível extrair informações do certificado utilizado, caso seja necessário.

Importante: um erro bastante comum é que alguns sistemas incluem novas informações no XML depois que ele foi assinado. Ao fazer isso, a assinatura digital é corrompida, e ao submetermos o XML para o código que escrevemos acima, a assinatura será dada como inválida. Entre essas informações, é comum ver namespaces como os que são mostrados abaixo (em negrito), e segundo a Nota Técnica 2010/009 da SEFAZ, eles não devem ser incluídos, nem antes, e muito menos depois, da assinatura.

<?xml version=”1.0″ encoding=”UTF-8″?>
<nfeProc xmlns=”http://www.portalfiscal.inf.br/nfe&#8221;
         xmlns:ds=”http://www.w3.org/2000/09/xmldsig#&#8221;
         xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;

         versao=”2.00″>
  <NFe xmlns=”http://www.portalfiscal.inf.br/nfe”&gt;
    <infNFe Id=”NFe…” versao=”2.00″>

Utilizando o HTTPS no ASP.NET Web API

Há algum tempo já é possível criar certificados para testarmos em nossas aplicações. Entre os tipos de aplicações que utilizamos certificados, temos aplicações Web (HTML ou APIs) que são expostas através de HTTP e HTTPS. Apesar dos certificados que são considerados “válidos” para o Mercado, devem ser comprados de uma autoridade certificadora (VeriSign, por exemplo), em tempo de desenvolvimento não necessitamos ter todo esse luxo.

Entre as várias opções que temos para criarmos certificados para testes, uma delas é recorrer ao próprio IIS, que possibita a criação de certificados autoassinados, e que podemos perfeitamente utilizar em um ambiente de desenvolvimento. A imagem abaixo ilustra a criação do mesmo e já podemos ver um site configurado com este certificado recém criado.

Se hospedarmos neste site uma Web API, poderemos acessá-la via HTTP ou via HTTPS, afinal, o site dá suporte para estes dois protocolos (bindings). Se quisermos, via programação, forçar com que a requisição seja, obrigatoriamente, realizada através do HTTPS, podemos avaliar antes de executar efetivamente a ação que estão sendo solicitada. Para dar mais flexibilidade, vamos criar um filtro que pode (e deve) ser aplicado naquelas ações que queremos que a mesma esteja sendo acessada através de HTTPS.

public class ValidacaoDeHttps : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var request = actionContext.Request;

        if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response =
request.CreateResponse(
HttpStatusCode.Forbidden,
new StringContent(“É necessário que a requisição seja HTTPS.”));
}
}
}

Com esta classe criada, podemos decorar as ações que devem ser protegidas pelo transporte. O código abaixo ilustra a utilização deste atributo:

[ValidacaoDeHttps]
public string Get()
{
return “XPTO”;
}

IMPORTANTE: Ao acessar o serviço no navegador, podemos receber um aviso dizendo que o certificado não é confiável. Quando acessamos um recurso via HTTPS, uma das validações que é realizada é identificar se o nome domínio do certificado corresponde ao domínio da URL que estamos acessando. No meu caso, minha máquina chama IsraelAeceNB1; se acessar o serviço como localhost, então um aviso indicando a inconsistência será apresentado. Se utilizar o nome da máquina, então esse aviso não será exibido.

Vale lembrar que esse problema não ocorre somente no navegador. Se estivermos acessando o recurso através do HttpClient, uma exceção será disparada para indicar esse problema. Existe um recurso presente no .NET Framework, que é a possibilidade de interceptar essa validação e, consequentemente, tomar a decisão se podemos ou não aceitar o certificado, mesmo com esta falha. Essa validação deve ser configurada através do delegate ServerCertificateValidationCallback, assim como foi já comentado neste outro artigo.

No exemplo abaixo, estamos acessando o serviço via HTTPS com a URL definida como localhost (o que é suficiente para apresentar o erro), e como estamos dando um by-pass, a requisição é realizada com sucesso. Apesar disso ser útil em um ambiente de testes, é importante dizer que raramente deverá utilizar isso em produção, pois devemos sempre utilizar um “certificado válido”.

ServicePointManager.ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true;

using (var client = new HttpClient())
{
var r = client.GetAsync(“https://localhost/api/Informacoes&#8221;).Result;

    Console.WriteLine(r.StatusCode);
Console.WriteLine(r.Content.ReadAsStringAsync().Result);
}