├── NGINX
├── server.js
├── index.php
└── site.nginx
├── NODEJS
└── server.js
├── LICENSE
└── README.md
/NGINX/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | app.get('/nodejs', function (req, res) {
5 | let ou = req.headers.dn.split(':');
6 | res.send(ou[0]);
7 | });
8 |
9 | app.listen(3000, function () {
10 | console.log('Estamos utilizando a porta 3000!');
11 | });
12 |
--------------------------------------------------------------------------------
/NGINX/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Title
7 |
8 |
9 |
10 | Cerificado Digital ICP-Brasil
11 |
12 |
27 |
28 |
--------------------------------------------------------------------------------
/NODEJS/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const fs = require('fs')
3 | const https = require('https')
4 | const app = express()
5 |
6 | const options = {
7 | key: fs.readFileSync('/etc/letsencrypt/live/meusite.com.br/privkey.pem'),
8 | cert: fs.readFileSync('/etc/letsencrypt/live/meusite.com.br/fullchain.pem'),
9 | ca: fs.readFileSync('/sites/CA/bundle.crt'),
10 | requestCert: true,
11 | rejectUnauthorized: false
12 | }
13 |
14 | app.use(function (req, res, next) {
15 | res.writeHead(200)
16 | res.end(req.socket.getPeerCertificate().subject.CN + "\n")
17 | // console.log(req.socket.getPeerCertificate().subject.CN)
18 | next()
19 | })
20 |
21 | var listener = https.createServer(options, app).listen(433, function () {
22 | console.log('Express HTTPS server listening on port ' + listener.address().port);
23 | })
24 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 c0h1b4
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Autenticação-ICP-Brasil
2 | Instruções para autenticar um token/certificado A1/A3 no site, apresentando o e-CPF ou e-CNPJ
3 |
4 | Instruções para os seguintes cenários:
5 |
6 | 1. **Utilizando o servidor NGINX** como autenticador para o certificado, e obtendo os dados de e-CPF e/ou e-CNPJ em aplicativos PHP ou Node.JS
7 | 2. **Utilizando o servidor Node.JS** como autenticador para o certificado através do módulo Express para autenticar o certificado e obter os dados de e-CPF e/ou e-CNPJ para utilização no Node.JS
8 | 3. **Utilizando um servidor APACHE** como autenticador para o certificado (TODO)
9 | 4. **Utilizando o API GATEWAY do AWS** no caso de aplicações serverless (AWS Lambda) (TODO)
10 |
11 | Na pasta CA, temos o bundle completo de chaves para autenticação, obtido através do gist https://gist.github.com/skarllot/9663935
12 |
13 | Os exemplos estão com documentação incluída nos próprios arquivos.
14 |
15 | ~~Obs1:
16 | Tanto na opção de NGINX quanto na do NodeJS, a solicitação de certificado não funciona com o FireFox. Ele pergunta a senha do token mas não solicita o certificado do token. No Edge ele pergunta qual certificado do token será utilizado mas depois não lê o certificado. No Chrome, Internet Explorer e Opera, funcionou perfeito.~~ Com o novo bundle de certificados, agora funciona em todos os navegadores. O novo bundle foi criado através do gist do skarllot/make-icpbrasil-bundle.sh (https://gist.github.com/skarllot/9663935)
17 |
18 | Obs2:
19 | Os dados do CPF ou CNPJ estão na variável CN, no formato XXX:NNN, onde o XXX é o nome do titular do CPF ou razão social da empresa e o NNN é o numero do CPF ou CNPJ. Então o ideal é usar um comando split com o caracter ':' como divisor e você terá um array onde o item 0 é o nome/razão social e o item 1 é o número do cpf/cnpj. Remova os 3 primeiros caracteres do item 0, pois estes tem sempre o 'CN='
--------------------------------------------------------------------------------
/NGINX/site.nginx:
--------------------------------------------------------------------------------
1 | # Instrução para redirecionar acessos pela porta 80 (HTTP) para a porta HTTPS (443)
2 | server {
3 | listen 80;
4 | server_name meusite.com.br;
5 | return 301 https://$server_name$request_uri;
6 | }
7 |
8 | # Neste caso, estou utilizando chaves SSL do letsencrypt que ficam normalmente armazenadas no diretório /etc/letsencrypt/live/
9 |
10 | server {
11 | listen 443 ssl http2;
12 | server_name meusite.com.br;
13 | ssl on;
14 | ssl_certificate /etc/letsencrypt/live/meusite.com.br/fullchain.pem;
15 | ssl_certificate_key /etc/letsencrypt/live/meusite.com.br/privkey.pem;
16 | ssl_session_cache shared:SSL:20m;
17 | ssl_session_timeout 180m;
18 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
19 | ssl_prefer_server_ciphers on;
20 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
21 | ssl_dhparam /sites/keys/dhparam.pem;
22 |
23 | #A mágica acontece aqui:
24 | #no próximo bloco, fazemos as instruções paraque o NGINX solicite o certificado do cliente.
25 | #existem diversas opções para o ssl_verify_client, mas as 2 que você poderá utilizar são:
26 | # optional -> verifica se tem o certificado. Se não tiver, continua com o site funcionando sem apresentar tela de erro. Você poderá verificar na página PHP ou JS (nodejs) se o certificado foi validado e quais os dados do certificado e apresentar uma mensagem ao usuário informando se não foi possível validar o certificado.
27 | # on -> força o usuário a utilizar o certificado, apresentado a página de erro 400 Bad Request, com o texto 'No required SSL certificate was sent'. Você poderá fazer uma página custom mas acho que a opção 'optional' dá uma melhor experiência ao usuário.
28 |
29 | ssl_client_certificate /sites/CA/bundle.crt;
30 | ssl_verify_client optional;
31 | ssl_verify_depth 10;
32 |
33 | #localização dos arquivos do site + página inicial (index.php)
34 | root /sites/meusite.com.br/public;
35 | index index.php index.html index.htm;
36 | charset utf-8;
37 |
38 | #local onde é gravado os logs de erros
39 | error_log /var/log/nginx/meusite.com.br.error;
40 |
41 | #o meu NGINX tem o compactador brotli incluído na compilação custom. Se o seu NGINX não tem o brotli, remova a linha abaixo
42 | brotli_types text/plain text/css application/javascript application/json image/svg+xml application/xml+rss;
43 |
44 | #linha para processamento de arquivos PHP de sites tipo Laravel (que são o que estou utilizando no momento)
45 | location / {
46 | try_files $uri $uri/ /index.php$is_args$args;
47 | }
48 |
49 | error_page 404 /index.php;
50 |
51 | #as páginas PHP são processadas pelo PHP ou HipHop através da porta 9000. Se estiver utilizando o método de socket, troque o fastcgi_pass
52 | location ~ \.(php)$ {
53 | fastcgi_param HTTP_ACCEPT_ENCODING "";
54 | fastcgi_keep_conn on;
55 | # fastcgi_pass unix:/var/run/php7.0-fpm.sock;
56 | fastcgi_pass 127.0.0.1:9000;
57 | fastcgi_index index.php;
58 | # aqui passamos os dados do certificado para o interpretador PHP
59 | fastcgi_param SSL_CLIENT_S_DN $ssl_client_s_dn if_not_empty;
60 | fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify if_not_empty;
61 |
62 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
63 | include fastcgi_params;
64 | }
65 |
66 | #se estiver utilizando um servidor nodejs, na url 'meusite.com.br/nodejs', aqui está a configuração:
67 | location /nodejs {
68 | proxy_set_header X-Real-IP $remote_addr;
69 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
70 | proxy_set_header Host $http_host;
71 | proxy_set_header X-NginX-Proxy true;
72 | proxy_set_header VERIFIED $ssl_client_verify;
73 | proxy_set_header DN $ssl_client_s_dn;
74 |
75 | proxy_pass http://127.0.0.1:3000;
76 | proxy_redirect off;
77 | }
78 | }
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------