Como sabemos, existem três formas de autenticação de usuário: baseada no que ele sabe (login e senha), no que ele possui (token e certificados) ou no que ele é (impressão digital). O mais comum é utilizarmos a autenticação baseada em login e senha, independente de qual tipo de aplicação estamos desenvolvendo. Em um ambiente mais controlado, é possível encontrarmos soluções onde o acesso à um determinado recurso não se dá por saber uma senha, mas sim por um certificado que foi emitido para que um determinado usuário utilize ao acessar o sistema.
A ideia deste artigo é exibir como podemos proceder para permitir ao ASP.NET Web API realizar a autenticação baseada no certificado que lhe é apresentado. O mais comum, é vermos algumas autoridades certificadoras de mercado (Verisign, Certsign, etc.) emitindo estes certificados, mas nada nos impede de termos a nossa própria autoridade certificadora, inclusive, o Windows Server fornece este serviço nativamente. A utilização deste serviço está fora do escopo deste artigo. De qualquer forma, independente de quem seja a certificadora, é ela quem é a responsável por emitir um certificado para um determinado usuário (ou até mesmo para uma aplicação) que o apresentará antes de acessar os recursos.
Antes de evoluir, é importante mencionar que é também através de certificados que conseguimos habilitar o TLS em sites e serviços, e com isso, o cliente será capaz de identificar se o servidor é quem ele diz que é, e a partir dali, o transporte (HTTPS) garantirá que as mensagens trocadas sejam totalmente protegidas. Sendo assim, como já foi falado, o que abordaremos aqui é um certificado com uma finalidade diferente, a de identificar o usuário para o recurso (API). Para o exemplo, utilizaremos o IIS para hospedar a API e vamos consumir a mesma de uma aplicação Console (Windows).
Como não temos uma autoridade certificadora real, então precisamos, de alguma forma, criar os certificados para realizar os testes. O .NET Framework fornece um utilitário chamado makecert.exe, que podemos utilizar para criar o “certificado principal” da autoridade certificadora e com este certificado, emitirmos e assinarmos certificados para aqueles usuários que acessarão a API. Abaixo temos o passo à passo para gerar os mesmos (estou quebrando a linha por questões de espaço):
makecert
-r
-n “CN=ServidorCA”
-pe
-sv ServidorCA.pvk
-a sha256
-cy authority
ServidorCA.cer
makecert
-iv ServidorCA.pvk
-ic ServidorCA.cer
-n “CN=IsraelAece”
-pe
-sv IsraelAece.pvk
-a sha256
-sky exchange
IsraelAece.cer
-eku 1.3.6.1.5.5.7.3.2
O primeiro comando é responsável por criar o certificado da nossa raiz e autoridade certificadora. Basicamente estamos colocando no arquivo *.pvk a chave privada (que deve ser armazenada de forma segura) e no arquivo *.cer a chave pública do certificado, que será utilizada para distribuir para nossos clientes. Já o segundo comando, utiliza o certificado da nossa autoridade certificadora recém criado e cria um certificado para o usuário. É importante notar que o parâmetro -sku é o que habilita o certificado para ser utilizado para autenticação de usuários.
Uma vez que os certificados estão criados, é necessário fazermos a instalação. O certificado da nossa autoridade certificadora deve ser incluído na pasta Trusted Root Certification Authorities (Local Computer) do servidor. Isso pode ser realizado através do MMC (Microsoft Management Console) do Windows, clicando na opção Importar, assim como podemos ver na imagem abaixo:
Nós, como autoridade certificadora, uma vez que emitimos um certificado para um alguém, é necessário empacotar e mandar para que o mesmo possa instalar e fazer uso do mesmo. É aqui que entra um segundo utilitário, chamado de pv2pfx.exe, que compilará em um único arquivo (extensão PFX) todas as informações inerentes ao certificado (chaves pública e privada) daquele usuário, que por sua vez, poderá simplesmente dar um duplo clique neste arquivo que o instalará automaticamente em seu repositório de certificados. É importante também enviar o arquivo ServidorCA.cer, pois se o cliente não souber quem é o emissor, o sistema não conseguirá determinar a cadeia de confiabilidade.
Finalmente, quando o cliente instala o certificado (clicando no arquivo PFX) o mesmo é colocado no repositório do usuário local, assim como se pode visualizar na imagem abaixo:
Depois de todos estes passos, é necessário ajustar a API para interceptar as requisições e validar o certificado para assegurar que o cliente pode acessar o recurso em questão. Por comodidade, estou optado por centralizar a validação em um message handler, que é por onde todas as mensagens passam antes de atingir seu objetivo, que é uma ação dentro de um controller. Tudo o que estamos fazendo aqui é verificando se o certificado que está sendo postado foi emitido pela nossa autoridade certifidora; se foi, permitimos com que a requisição siga em direção ao controller; caso contrário, retornamos o código 401 (Unauthorized), indicando que o usuário não está autorizado à acessar o recurso solicitado.
public class ClientCertificateHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var certificate = request.GetClientCertificate();
if (certificate != null && certificate.Issuer.Contains(“ServidorCA”))
return await base.SendAsync(request, cancellationToken);
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
Como sabemos, a classe por si só não funciona. É necessário adicionar à coleção de MessageHandlers para que o ASP.NET possa invocá-lo a cada requisição que chega para os controllers.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//outras configurações
config.MessageHandlers.Add(new ClientCertificateHandler());
}
}
Uma vez que a API está no ar, é o momento de consumirmos através do cliente, que no nosso caso, será uma aplicação Console (Windows). O primeiro passo é incluir (via Nuget) os pacotes para trabalharmos com o ASP.NET Web API (parte cliente). Uma vez que essas bibliotecas estão adicionadas, então podemos recorrer ao seguinte código para consumirmos a API:
public static async void Executar()
{
using (var handler = new WebRequestHandler())
{
handler.ClientCertificates.Add(ExtrairCertificado());
using (var client = new HttpClient(handler))
{
var response = await client.GetAsync(“https://localhost/Servidor/api/teste/ping?valor=123”);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
}
Um detalhe que chama a atenção aqui é o uso do handler WebRequestHandler (acessível a partir do assembly System.Net.Http.WebRequest.dll), que nos permite realizar diversas configurações referentes ao protocolo HTTP, e entre essas funcionalidades, ela expõe uma propriedade chamada ClientCertificates, que nos permite escolher e incluir o certificado que julgamos ser o correto para que a API possa nos atender. Aqui temos duas opções para extrair o certificado.
O certificado no .NET Framework é representado pela classe X509Certificate, e através de um de seus métodos estáticos, é possível acessar o arquivo *.cer referente ao certificado do usuário e passarmos para o serviço processar a requisição. Note que neste caso não há qualquer interação com o usuário. O sistema é o responsável por escolher e enviar o certificado.
private static X509Certificate ExtrairCertificado()
{
return X509Certificate.CreateFromCertFile(@”C:TempIsraelAece.cer”);
}
Caso queira deixar o cliente decidir qual certificado ele deseja enviar à API, então podemos chegar até o repositório de certificados do cliente, e através de uma classe chamada X509Certificate2UI (que está contida no assembly System.Security.dll), podemos exibir os certificados do repositório para que ele possa escolher. Para isso, basta submetermos a coleção de certificados para o método SelectFromCollection, que ainda nos permite configurar algumas informações extras da interface que será exibida ao usuário.
private static X509Certificate ExtrairCertificado()
{
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
return X509Certificate2UI.SelectFromCollection(
new X509Certificate2Collection(store.Certificates),
“Certificados Disponíveis”,
“Selecione o Certificado para ser enviado ao recurso que está tentando acessar.”,
X509SelectionFlag.SingleSelection)[0];
}
finally
{
if (store != null) store.Close();
}
}
E, finalmente, ao selecionar o certificado a requisição é encaminhado ao servidor que processará e dará o resultado conforme esperamos.
Boa Israel, valeu por compartilhar…
Estou precisando desse recurso para emitir NF utilizando certificados do tipo A3… Com certificados A1 eu já estou conseguindo, agora estou buscando conseguir também com o A3.. Com o conteúdo acima, consegui chamar a bendita telinha que solicita o certificado do cliente, agora, eu preciso também uma maneira para solicitar a senha (PIN) do certificado ..
Preciso de um esquema semelhante ao que ocorre quando você clica em certificado digital no site https://cav.receita.fazenda.gov.br/eCAC/publico/login.aspx
tem ideia de como posso fazer isso?
Valeu!!
Boa tarde Fernando, eu também precisava dessa mesma resposta…
Bom dia Israel,
É necessário fazer alguma configuração no IIS? Meu cliente diz que está enviando o certificado… mas quando executo o comando request.GetClientCertificate() não retorna o certificado.
Ele está me enviando pelo SoapUI.
Sim, você precisa ir até a seção SSL Settings e aceitar os certificados do cliente.