======================================================================================================== Configuração do apache+ssl, com verficação de certificado do cliente para permitir entrada. ======================================================================================================== Escrito : Eduardo Jorge Feres Serrano Neves - eth0 E-mail : serrano.neves@gmail.com ICQ : 132271374 URL : http://www.securityopensource.org.br | www.serrano-neves.locaweb.com.br/eduardo/ Versão : 0.3 Data da criação : 22/09/2004 Ultima modificação: 24/09/2004 Agradecimentos: A todos os meus amigos, a galera da Security OpenSource Waldemar,Bruno,Antonio. Para mandar comentarios, sugestões ou erros: ======================================================================================================== Bom, neste tutorial vamos configurar o Apache para trabalhar o SSL, autorizando somente o acesso, para quem tem um certificado válido e também fazer uma verificação por determinados parâmetros do certificado par autorizar entrada somente em alguns diretórios de nosso servidor web. Para que possamos dar inicio a configuração antes você precisara ter o apache funcionando com mod_ssl, e tambem o openssl configurado para geração dos certificados. Neste texto serei bem expecifico não explicarei como colocar o apache funcionando com mod_ssl, apenas irei mostrar como configurar o openssl, gerar as chaves e mostar como fazer a verificação do certificado do cliente, e explicar um pouco mais sobre os campos do certificado. Bom agora vamos ao que nos interessa,que e parte de configuração, primeiramente vou fazer um desenho de como nossa solução vai funcionar, ou seja um esquema para facilitar nosso entendimento +----------------------+ +--------------------------------------------+ | Client Hello --> | | Estabelece a versão do protocolo,o id da | +----------------------+ ==========> | sessão,estabelece os metodos de compressão,| | <-- Server Hello | | Troca dos valores randomicos | +----------------------+ +--------------------------------------------+ +----------------------+ +--------------------------------------------+ |<-- Certificate | | Manda o certificado do servidor | +----------------------+ | e requisita o do cliente | |<-- Request cert | ==========> | | +----------------------+ +--------------------------------------------+ |<-- ServerHelloDone | +----------------------+ +----------------------+ +--------------------------------------------+ | Certificate --> | | Envia o certificado do cliente | +----------------------+ ==========> | se requisitado | | ClientVerify --> | | | +----------------------+ +--------------------------------------------+ +----------------------+ |ChangeCipherSuite --> | +----------------------+ +--------------------------------------------+ | Finished --> | | | +----------------------+ ==========> | Troca da CipherSuite e fim do handshake | |<-- ChangeCipherSuite | | | +----------------------+ +--------------------------------------------+ |<-- Finished | +----------------------+ | | Cliente Servidor 1 - Negociar a Cipher Suite,que será utilizada durante a transação. 2 - Estabilizar e compartilhar a chave de sessão, entre o cliente e o servidor. 3 - Opicionalmente autenticar o servidor ao cliente 4 - Opicionalmente autenticar o clietne ao servidor. Bom este é o nosso cenário, agora vamos ao implementações que devem ser feitas no arquivos para que possamos estar validando as entradas atravéz de um certificado. ==================================================== Configuração básica do OpenSSL ==================================================== Primeramente temos que ter o openSSL, instalado ele pode ser baixado em: http://www.openssl.org/, vamos agora ver um pouco mais sobre a configuração do openSSL.Estarei aqui abordando a configuração basica do openssl.conf, para obter maior detalhes de sua configurção pode ser obitido no seu site oficial (www.openssl.org),http://www.gtlib.cc.gatech.edu/pub/linux/docs/HOWTO/other-formats/html_single/SSL-Certificates-HOWTO.html. Bom vamos a uma parte do arquivo. #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = ./demoCA # Diretorio raiz da ca onde tudo é mantido certs = $dir/certs # Diretorio aonde os certificados são mantidos crl_dir = $dir/crl # Diretorio aonde a lista de revogados e mantido database = $dir/index.txt # Base de dados que indexa os arquivos new_certs_dir = $dir/newcerts # Local dos certificados novos. certificate = $dir/cacert.pem # Certificado da CA serial = $dir/serial # Arquivo contendo o numero de serie atual crl = $dir/crl.pem # Lista de revogados atual. private_key = $dir/private/cakey.pem# Chave privada da CA RANDFILE = $dir/private/.rand # Arquivo privado com numero randomico x509_extensions = serv_cert # Extensões para adicionar ao certificado # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # (descomentei apesar do que esta dito acima): crl_extensions = crl_ext default_days = 365 # Tempo de vida do certificado em dias default_crl_days= 30 # Tempo de vida de cada lista de revogado default_md = md5 # Tipo de md utilizado. preserve = no # Padrão deste campo e no, para maiores detalhes verifique os links mensionados acima. # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy # # O tipo pode ser, supplied se ele poder ser omitido, optional, se ele for opcional, ou match se ele for obrigatorio, aqui vamos # colocar o organizationName, emailAddress, e o organizationalUnitName como match, para utilizarmos para fazer nossas autenticações # [ policy_match ] countryName = supplied stateOrProvinceName = optional organizationName = match organizationalUnitName = match commonName = supplied emailAddress = match # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional Bom este e apenas uma breve introdução ao arquivo openssl.cnf, nele podem ser feitas varias modificações, como colocar os valores default para cada campo e muitas outras implementações que poderei estar abordando em uma outra versão deste tutorial. ==================================================== Roteiro para criação de chaves e certificados ==================================================== ===> Criação da chave e certificado do CA Os comandos necessarios para a geração dos certificados da CA são: openssl genrsa -des3 -out chave_privada_da_ca.pem 1024 cp chave_privada_ca.pem ./demoCA/private/cakey.pem openssl req -new -x509 -config openssl.cnf -key chave_privada_da_ca.pem -out certificado_da_ca.pem -days 365 cp certificado_da_ca.pem ./demoCA/cacert.pem ===> Criação da chave e certificado do Servidor Os comandos necessarios para a geração dos certificados do servidor são: openssl genrsa -des3 -out chave_privada_do_serv.pem 1024 openssl req -new -config openssl.cnf -key chave_privada_do_serv.pem -out pedido_serv.pem openssl ca -config openssl.cnf -extensions usr_cert -out certificado_serv.pem -in pedido_serv.pem ===> Criação da chave e certificado do Cliente Os comandos necessarios para a geração dos certificados do servidor são: openssl genrsa -des3 -out chave_privada_cliente.pem 1024 openssl req -new -config ca.config -key chave_privada_cliente.pem -out pedido_client.pem openssl ca -config openssl.cnf -extensions cliente_cert -out certificado_cliente.pem -in pedido_client.pem openssl pkcs12 -export -clcerts -in certificado_cliente.pem -inkey chave_privada_cliente.pem -out certificado_cliente.pfx ==================================================== Configuração do SSL ==================================================== Primeiramente vamos editar o ssl.conf colocando as seguintes diretivas SSLEngine on SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL # Certificado do Apache SSLCertificateFile /var/www/html/phpki/ca/CA/certs/100001.crt # Chave do apache SSLCertificateKeyFile /var/www/html/phpki/ca/CA/private/100001-key.pem # Path do certificado da CA SSLCACertificatePath /var/www/html/phpki/ca/CA/certs # Certificado da CA SSLCACertificateFile /var/www/html/phpki/ca/CA/certs/cacert.pem # Diretorio de revogação SSLCARevocationPath /var/www/html/phpki/ca/CA/crl #Arquivos de certificados revogados SSLCARevocationFile /var/www/html/phpki/ca/CA/crl/cacrl.pem # Diretiva para que seja necessario a verificação do certificado do cliente SSLVerifyClient require # Hierarquia do certificado, 2 seguinifica que foi assinado pela ca, e e do usuario ou seja so existem 2 niveis. SSLVerifyDepth 2 Após esta configuração feita, agora ja temos nosso servidor aceitando as conexoes HTTPS apenas que quem tem um certificado assinado pela nossa CA indicado no SSLCACertificateFile, vamos agora a parte de configuração dos diretorios em que queremos barrar apenas o acesso a alguns certificados. ==================================================== Configuração do httpd.conf ==================================================== Primeiramente vou mostrar algumas das variaveis que podem ser utilizadas para estar fazendo esta restrição diferenciada de cada certificado. +----------------------+----------------------+ |Cliente | Servidor | +----------------------+----------------------+ |SSL_CLIENT_M_VERSION | SSL_SERVER_M_VERSION | +----------------------+----------------------+ |SSL_CLIENT_M_SERIAL | SSL_SERVER_M_SERIAL | +----------------------+----------------------+ |SSL_CLIENT_V_START | SSL_SERVER_V_START | +----------------------+----------------------+ |SSL_CLIENT_V_END | SSL_SERVER_V_END | +----------------------+----------------------+ |SSL_CLIENT_S_DN | SSL_SERVER_S_DN | +----------------------+----------------------+ |SSL_CLIENT_S_DN_C | SSL_SERVER_S_DN_C | +----------------------+----------------------+ |SSL_CLIENT_S_DN_ST | SSL_SERVER_S_DN_ST | +----------------------+----------------------+ |SSL_CLIENT_S_DN_L | SSL_SERVER_S_DN_L | +----------------------+----------------------+ |SSL_CLIENT_S_DN_O | SSL_SERVER_S_DN_O | +----------------------+----------------------+ |SSL_CLIENT_S_DN_OU | SSL_SERVER_S_DN_OU | +----------------------+----------------------+ |SSL_CLIENT_S_DN_CN | SSL_SERVER_S_DN_CN | +----------------------+----------------------+ |SSL_CLIENT_S_DN_T | SSL_SERVER_S_DN_T | +----------------------+----------------------+ |SSL_CLIENT_S_DN_I | SSL_SERVER_S_DN_I | +----------------------+----------------------+ |SSL_CLIENT_S_DN_G | SSL_SERVER_S_DN_G | +----------------------+----------------------+ |SSL_CLIENT_S_DN_S | SSL_SERVER_S_DN_S | +----------------------+----------------------+ |SSL_CLIENT_S_DN_D | SSL_SERVER_S_DN_D | +----------------------+----------------------+ |SSL_CLIENT_S_DN_UID | SSL_SERVER_S_DN_UID | +----------------------+----------------------+ |SSL_CLIENT_S_DN_Email | SSL_SERVER_S_DN_Email| +----------------------+----------------------+ |SSL_CLIENT_I_DN | SSL_SERVER_I_DN | +----------------------+----------------------+ |SSL_CLIENT_I_DN_C | SSL_SERVER_I_DN_C | +----------------------+----------------------+ |SSL_CLIENT_I_DN_ST | SSL_SERVER_I_DN_ST | +----------------------+----------------------+ |SSL_CLIENT_I_DN_L | SSL_SERVER_I_DN_L | +----------------------+----------------------+ |SSL_CLIENT_I_DN_O | SSL_SERVER_I_DN_O | +----------------------+----------------------+ |SSL_CLIENT_I_DN_OU | SSL_SERVER_I_DN_OU | +----------------------+----------------------+ |SSL_CLIENT_I_DN_CN | SSL_SERVER_I_DN_CN | +----------------------+----------------------+ |SSL_CLIENT_I_DN_T | SSL_SERVER_I_DN_T | +----------------------+----------------------+ |SSL_CLIENT_I_DN_I | SSL_SERVER_I_DN_I | +----------------------+----------------------+ |SSL_CLIENT_I_DN_G | SSL_SERVER_I_DN_G | +----------------------+----------------------+ |SSL_CLIENT_I_DN_S | SSL_SERVER_I_DN_S | +----------------------+----------------------+ |SSL_CLIENT_I_DN_D | SSL_SERVER_I_DN_D | +----------------------+----------------------+ |SSL_CLIENT_I_DN_UID | SSL_SERVER_I_DN_UID | +----------------------+----------------------+ |SSL_CLIENT_I_DN_Email | SSL_SERVER_I_DN_Email| +----------------------+----------------------+ |SSL_CLIENT_A_SIG | SSL_SERVER_A_SIG | +----------------------+----------------------+ |SSL_CLIENT_A_KEY | SSL_SERVER_A_KEY | +----------------------+----------------------+ |SSL_CLIENT_CERT | SSL_SERVER_CERT | +----------------------+----------------------+ |SSL_CLIENT_CERT_CHAINn| | +----------------------+----------------------+ |SSL_CLIENT_VERIFY | | +----------------------+----------------------+ Estas são algumas das variaveis que podem ser utilizada, tem sem nome praticamente auto-explicativo, por exemplo SSL_CLIENT_S_DN_O, seguinifica o campo Organization do certificado, SSL_CLIENT_S_DN_OU, seguinifica o campo Unit do certificado, ou seja o departemento. As expressões que podem ser utilizadas devem seguir o seguinte padrão. +----------------------------------------------------------------------------+ | Padrão das Expressões | +----------------------------------------------------------------------------+ | | | expr ::= "true" | "false" | | | "!" expr | | | expr "&&" expr | | | expr "||" expr | | | "(" expr ")" | | | comp | | | | comp ::= word "==" word | word "eq" word | | | word "!=" word | word "ne" word | | | word "<" word | word "lt" word | | | word "<=" word | word "le" word | | | word ">" word | word "gt" word | | | word ">=" word | word "ge" word | | | word "in" "{" wordlist "}" | | | word "=~" regex | | | word "!~" regex | | | | wordlist ::= word | | | wordlist "," word | | | | word ::= digit | | | cstring | | | variable | | | function | | | | digit ::= [0-9]+ | | cstring ::= "..." | | variable ::= "%{" varname "}" | | function ::= funcname "(" funcargs ")" | +----------------------------------------------------------------------------+ Agora de posse destas informações podemos começar a dar alguns exemplos de como, trabalhar com essas restrições, essas configurações devem ser feitas dentro da diretiva Directory do apache, dentro do arquivo httpd.conf, vamos a nosso primeiro exemplo: Exemplo 1: Autorizar o acesso somente de pessoas onde o departamento do certificado seja igual a TESTE SSLVerifyClient require SSLVerifyDepth 2 SSLCACertificateFile conf/ssl.crt/ca.crt SSLCACertificatePath conf/ssl.crt SSLOptions +FakeBasicAuth SSLRequireSSL SSLRequire %{SSL_CLIENT_S_DN_OU} eq "TESTE" Exemplo 2: Autorizar o acesso de um certificado da organização TABAJARA, mais somente dos departamentos, FUTEBOL, COZINHA,FINANCEIRO SSLVerifyClient require SSLVerifyDepth 2 SSLCACertificateFile conf/ssl.crt/ca.crt SSLCACertificatePath conf/ssl.crt SSLOptions +FakeBasicAuth SSLRequireSSL SSLRequire %{SSL_CLIENT_S_DN_O} eq "TABAJARA" \ and %{SSL_CLIENT_S_DN_OU} in {"FUTEBOL", "COZINHA", "FINANCEIRO"} Sei que este texto devem conter muitos, erros e muitos assuntos que não foram abordados, gostaria que para melhora do mesmo, fossem enviados criticas, sugestões, e o erros aqui encontrados para serrano.neves@gmail.com ==================================================== Bibliografia consultada ==================================================== http://www.openssl.org http://www.apache.org http://www.cic.unb.br/docentes/pedro/segdadtop.htm