Nginx support client certificates, that means Nginx can ask for a certificate to the client browser. The user certificate is checked against the CA certificate configured in Nginx service.
In Brazil, the root authority signing certificates is called ICP-Brasil. There is an intermediate signer, called Instituto Nacional de Tecnologia da Informação (ITI).
All brazilians people and business can buy an always expensive certificate to get access to brazilian government services, to pay abusive taxes and report annual income.
It is possible to use the CA certificates provided by ITI to authenticate users holding brazilian certificates.
Getting CA certs from ICP-Brazil
# Download the current certificates curl http://acraiz.icpbrasil.gov.br/credenciadas/CertificadosAC-ICP-Brasil/ACcompactado.zip -o /tmp/ACcompactado.zip # Create a folder to decompress the certs mkdir /etc/ssl/icp-brasil # Decompress the files unzip -d /etc/ssl/icp-brasil /tmp/ACcompactado.zip # Remove the zip file rm /tmp/ACcompactado.zip
Picking correct CA files to Nginx
As of April 2021, 3 files from the downloaded zip are needed to work with Nginx:
- /etc/ssl/certs/icp-brasil/AC_Certisign_RFB_G5.crt
- /etc/ssl/certs/icp-brasil/AC_Secretaria_da_Receita_Federal_do_Brasil_v4.crt
- /etc/ssl/certs/icp-brasil/ICP-Brasilv5.crt
You need to concatenate the 3 files above into a single one Nginx will read. Something like below
{ \ cat /etc/ssl/certs/icp-brasil/AC_Certisign_RFB_G5.crt; \ cat /etc/ssl/certs/icp-brasil/AC_Secretaria_da_Receita_Federal_do_Brasil_v4.crt; \ cat /etc/ssl/certs/icp-brasil/ICP-Brasilv5.crt; \ } > /etc/ssl/certs/icp-brasil/ICP-Brasil-bundle.crt
Setting up Nginx
The 3 directives below are needed by Nginx to start requesting the client certificate from ICP-Brasil certificate holders.
ssl_client_certificate /etc/ssl/certs/icp-brasil/ICP-Brasil-bundle.crt; ssl_verify_client on; ssl_verify_depth 2;
Nginx has many variables containing information about the matched certificate. The proxied application probably needs to have access on these variable in order to authenticate and/or register the user. This information can be forwarded to the application like below.
fastcgi_param SSL_CIPHER $ssl_cipher if_not_empty; fastcgi_param SSL_CIPHERS $ssl_ciphers if_not_empty; fastcgi_param SSL_CLIENT_CERT $ssl_client_cert if_not_empty; fastcgi_param SSL_CLIENT_ESCAPED_CERT $ssl_client_escaped_cert if_not_empty; fastcgi_param SSL_CLIENT_FINGERPRINT $ssl_client_fingerprint if_not_empty; fastcgi_param SSL_CLIENT_I_DN $ssl_client_i_dn if_not_empty; fastcgi_param SSL_CLIENT_I_DN_LEGACY $ssl_client_i_dn_legacy if_not_empty; fastcgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert if_not_empty; fastcgi_param SSL_CLIENT_S_DN $ssl_client_s_dn if_not_empty; fastcgi_param SSL_CLIENT_S_DN_LEGACY $ssl_client_s_dn_legacy if_not_empty; fastcgi_param SSL_CLIENT_SERIAL $ssl_client_serial if_not_empty; fastcgi_param SSL_CLIENT_V_END $ssl_client_v_end if_not_empty; fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify if_not_empty; fastcgi_param SSL_CLIENT_V_REMAIN $ssl_client_v_remain if_not_empty; fastcgi_param SSL_CLIENT_V_START $ssl_client_v_start if_not_empty; fastcgi_param SSL_CURVES $ssl_curves if_not_empty; fastcgi_param SSL_EARLY_DATA $ssl_early_data if_not_empty; fastcgi_param SSL_PROTOCOL $ssl_protocol if_not_empty; fastcgi_param SSL_SERVER_NAME $ssl_server_name if_not_empty; fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty; fastcgi_param SSL_SESSION_REUSED $ssl_session_reused if_not_empty;
Sample Nginx config
server { listen 80; listen 443 ssl; server_name testcert.montefuscolo.com.br; root /var/www/html; access_log /var/log/nginx/testcert.montefuscolo.com.br_access_log; error_log /var/log/nginx/testcert.montefuscolo.com.br_error_log; ssl_certificate /etc/letsencrypt/live/testcert.montefuscolo.com.br/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/testcert.montefuscolo.com.br/privkey.pem; ssl_client_certificate /etc/ssl/certs/icp-brasil/ICP-Brasil-bundle.crt; ssl_verify_client on; ssl_verify_depth 2; index index.html index.htm index.php; location ~ \.php(/|$) { include fastcgi.conf; fastcgi_param SSL_CIPHER $ssl_cipher if_not_empty; fastcgi_param SSL_CIPHERS $ssl_ciphers if_not_empty; fastcgi_param SSL_CLIENT_CERT $ssl_client_cert if_not_empty; fastcgi_param SSL_CLIENT_ESCAPED_CERT $ssl_client_escaped_cert if_not_empty; fastcgi_param SSL_CLIENT_FINGERPRINT $ssl_client_fingerprint if_not_empty; fastcgi_param SSL_CLIENT_I_DN $ssl_client_i_dn if_not_empty; fastcgi_param SSL_CLIENT_I_DN_LEGACY $ssl_client_i_dn_legacy if_not_empty; fastcgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert if_not_empty; fastcgi_param SSL_CLIENT_S_DN $ssl_client_s_dn if_not_empty; fastcgi_param SSL_CLIENT_S_DN_LEGACY $ssl_client_s_dn_legacy if_not_empty; fastcgi_param SSL_CLIENT_SERIAL $ssl_client_serial if_not_empty; fastcgi_param SSL_CLIENT_V_END $ssl_client_v_end if_not_empty; fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify if_not_empty; fastcgi_param SSL_CLIENT_V_REMAIN $ssl_client_v_remain if_not_empty; fastcgi_param SSL_CLIENT_V_START $ssl_client_v_start if_not_empty; fastcgi_param SSL_CURVES $ssl_curves if_not_empty; fastcgi_param SSL_EARLY_DATA $ssl_early_data if_not_empty; fastcgi_param SSL_PROTOCOL $ssl_protocol if_not_empty; fastcgi_param SSL_SERVER_NAME $ssl_server_name if_not_empty; fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty; fastcgi_param SSL_SESSION_REUSED $ssl_session_reused if_not_empty; fastcgi_pass localhost:4567; fastcgi_split_path_info ^(.+\.php)(/.+)$; } }
References
- http://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables
- https://gist.github.com/skarllot/9663935
- https://github.com/c0h1b4/autenticacao-ICP-Brasil/blob/master/NGINX/site.nginx
- https://stackoverflow.com/questions/8431528/nginx-ssl-certificate-authentication-signed-by-intermediate-ca-chain