Mestrado em Informática - ALFA
Transcrição
Mestrado em Informática - ALFA
UNIVERSIDADE DO MINHO Mestrado em Informática Desenvolvimento de PKIs em Java MESTRADO EM INFORMÁTICA Desenvolvimento de PKIs em Java Pedro Miguel Félix Alípio [email protected] Índice 1. Introdução 1 2. Conceitos Básicos 2 2.1 Criptografia de Chave Secreta 2 2.2 Criptografia de Chave Pública 2 2.3 Assinaturas Digitais 3 2.4 Funções de Hash 3. Requisitos de uma PKI 3 4 3.1 Introdução 4 3.2 Requisitos Obrigatórios 4 3.2.1 Principais Funcionalidades 4 Criação de Autoridades de Certificação 4 Iniciação de Subscritores 4 Registo e Certificação 5 Publicação de Certificados 5 Anulação de Certificados 6 Publicação de CRLs 6 Mudança do Equipamento dos Subscritores 6 Comunicações 6 Software pré-existente 6 Normalizações pré-existentes 6 Autorização de Acessos 7 Escalabilidade 7 Robustez e tolerância a falhas 7 Auditorias 7 3.3 Requisitos Opcionais 8 Adaptabilidade 8 Facilidade de implementação 8 Gestão distribuída dos componentes da PKI 8 Compatibilidade de Normas 8 Interoperabilidade com outros sistemas 9 Recuperação de Chaves 9 Desempenho 9 4. O Java Criptográfico 10 4.1 Avalanche 10 Algoritmos criptográficos 10 Funções de hash 10 Classes utilitárias 10 Plataforma 10 Números Aleatórios 11 4.2 BeeJCE e BeeCrypt 11 4.3 ABA JCE 12 Algoritmos de cifragem 12 Algoritmos de Message Digest 12 Licença 12 4.4 BC Crypto 12 Algoritmos 12 4.5 IAIK 13 Algoritmos 13 Implementação do JCE 14 ASN.1 14 PKCS 14 X509 14 Geração de Números Aleatórios 15 4.5 JCSI 15 JCE 15 PKI 15 TLS 15 Mensagens Criptográficas 16 S/MIME 16 Kerberos 16 Licença 5. Funcionalidades de uma PKI e respectiva implementação em Java 16 17 5.1 Introdução 17 5.1 Subscritor 17 5.1.1 Iniciar Pedido de Certificado 17 5.1.2 Gerar par de chaves 18 Criar um gerador de pares de chaves 18 Iniciar o gerador de pares de chaves 18 Gerar o par de chaves 19 5.1.3 Criar pedido de certificado 19 Construtores 19 Métodos 20 Envio do pedido de certificado 20 5.1.4 Iniciar pedido de actualização de chaves 21 1 Determinar o final do ciclo de vida de uma chave 21 Gerar pedido de actualização de chaves 21 Obter um objecto para assinatura 21 Iniciar o objecto de assinatura 22 Envio do pedido de actualização de chaves 22 5.1.5 Instalar chave privada 22 Guardar a chave privada 22 Ler a chave privada 23 5.1.6 Prova de posse de chave privada 23 5.1.7 Instalar e guardar certificados 23 5.1.8 Assinar e decifrar com a chave privada 24 5.1.9 Apresentar o certificado aos outros 24 5.1.10 Iniciar pedido de revogação 25 5.1.11 Iniciar pedido extensão do prazo de validade 25 5.2 Autoridade de Registo 25 5.2.1 Verificar a identidade do subscritor 26 5.2.2 Aprovar ou modificar o conteúdo do certificado 26 O campo attributes do PKCS#10 26 5.2.3 Gerar pares de chaves 26 5.2.4 Guardar pares de chaves 27 5.2.5 Distribuição de tokens 27 5.2.6 Testes de posse de chave privada 27 Obter chave pública 28 Iniciar objecto de assinatura 28 Assinar a mensagem 28 Verificar a assinatura 28 5.2.7 Atribuição de DN e modificação do conteúdo do certificado 5.3 Autoridade de Certificação 28 29 5.3.1 Responder a pedidos de renovação de certificados 29 5.3.2 Modificar ou aprovar modificações do conteúdo do certificado 29 5.3.3 Iniciar e aprovar revogação 30 5.3.4 Criar certificado raiz auto assinado 30 Gerar um certificado a partir de um par de chaves 30 Gerar um certificado CA auto assinado com o JCSI 30 5.3.5 Emissão de certificados 30 5.3.6 Disponibilizar os certificados aos subscritores 31 5.3.7 Publicação dos certificados 32 5.3.8 Revogar certificados (Adicionar à CRL) 32 5.3.9 Publicar CRL 33 5.3.10 Pedido de Certificação Cruzada 33 5.3.11 Aceitar Certificação Cruzada 33 5.4 Repositório de Certificados 34 5.4.1 Armazenar certificados e CRLs 34 5.4.2 Disponibilizar Certificados e CRLs 34 2 5.4.3 Verificar o estado do Certificado 34 5.5 Aplicações ou Utilizadores 35 5.5.1 Receber certificados 35 5.5.2 Pedir Certificados 35 5.5.3 Validar Certificado 35 5.5.4 Verificar identidades e Assinaturas 36 5.5.5 Cifrar com o Certificado 6. Conclusão 36 37 7. Referências 38 Implementação de um CA de domínio público 38 APIs Criptográficos 38 LDAP 38 SSL 38 PKI em geral 39 3 1. Introdução Introdução ás infra-estruturas de chave publica Com o rápido crescimento da Internet e o seu alargamento a todos os sectores da sociedade, nomeadamente às áreas de negócios, foi surgindo a necessidade de implementar sistemas que fossem capazes de garantir privacidade e autenticidade nas comunicações. Como se sabe a Internet é uma rede insegura onde as comunicações estão sujeitas a intercepções, monitorizações e adulterações. O conceito de privacidade consiste na transmissão de mensagens que não possam ser alteradas nem lidas durante a sua transmissão. O conceito de autenticidade consiste em garantir que cada um dos intervenientes envolvidos na comunicação tenham a certeza da identidade dos outros, o que implica que as mensagens não podem ser adulteradas. A ciência capaz de dar resposta a estes problemas e criar soluções para garantir estas duas propriedades nas comunicações chama-se Criptografia. Esta ciência disponibiliza meios para que duas pessoas, normalmente apelidadas de Alice e Bob, possam comunicar abertamente sem que uma terceira pessoa, normalmente apelidada de Oscar, seja capaz de saber o conteúdo da comunicação. Assegurando a privacidade, a Criptografia garante indirectamente a autenticidade porque apenas a Alice e o Bob sabem encriptar e decifrar as mensagens dum e do outro. A criptografia de chave pública é a técnica criptográfica que melhor encaixa na Internet. Cada utilizador de um sistema destes tem um par de chaves. Uma mensagem é encriptada com uma das chaves e pode ser decifrada com a outra. Uma das chaves permanece secreta enquanto a outra é publicada. Esta técnica permite que se possam enviar mensagens encriptadas com a chave publica e apenas a pessoa que detém a chave privada as possa ler. Desta forma garante-se a privacidade na comunicação. Para que seja garantida a autenticidade basta que a mensagem seja assinada com a chave secreta. Se os destinatários aplicaram a chave publica do emissor podem certificar-se que a mensagem é efectivamente autentica. Para que todo o processo de atribuição e publicação das chaves públicas fosse credível, foram criadas as infra-estruturas de chaves públicas (PKI). Com este trabalho, em primeiro lugar, pretendo identificar as principais funcionalidades e características que uma infra-estrutura de chave publica deve possuir. De seguida pretendo identificar os packages de Java de que implementem funcionalidades criptográficas que possam ser usadas na implementação destas infra-estruturas. Finalmente pretendo explicar de que forma se podem aplicar alguns desses packages na implementação em cada componente de uma PKI. 1 2. Conceitos Básicos Conceitos e técnicas criptográficas básicas 2.1 Criptografia de Chave Secreta A criptografia de chave secreta é forma mais clássica de criptografia. Esta técnica já é utilizada desde tempos muito remotos. Num sistema de chave secreta, ambas as partes partilham um segredo, que consiste na chave de cifragem e decifragem da mensagem. Este sistema obriga a trocas de mensagens entre o emissor e o receptor de forma a negociarem uma chave. O mais famoso algoritmo de chave secreta é o DES (Data Encryption Standard). Hoje em dia é frequente usar uma evolução deste algoritmo chamado Triple-DES. Existem sistemas para comunicação segura sobre redes públicas que usam este tipo de criptografia. Um dos mais conhecidos é o Kerberos. A grande desvantagem deste tipo de sistemas é que não são facilmente escaláveis a populações inter-organizacionais de grande dimensão, e exigem procedimentos de segurança suplementares que os sistemas de chaves públicas não necessitam, como por exemplo, guardar as chaves secretas num servidor central de segurança. 2.2 Criptografia de Chave Pública A criptografia de chave pública é uma técnica criptográfica bastante recente. Foi desenvolvida em 1976 por Diffie e Hellman. Em 1977, Rivest, Shamir e Adleman criaram a primeira implementação de um sistema de chave publica, ao qual chamaram de RSA Cryptosystem. Ao longo do tempo foram surgindo novos sistemas como o ElGamal ou o Sistema da Curva Elíptica. Cada sistema de chave pública tem particularidades técnicas distintas, no entanto todos partilham o conceito de que, dada uma chave de cifragem não se pode a partir desta obter a chave de decifragem e vice-versa. Este conceito permite que um utilizador possa publicar uma chave (chave pública), toda a gente que desejar enviar mensagens a esse utilizador podem cifrar as mensagens com essa chave. Apenas esse utilizador poderá ler as mensagens pois é o único a deter a chave capaz de as decifrar (chave privada). O facto de o desempenho de um sistema deste tipo ser mais lento que um sistema de chave secreta, levou a que se utilizassem sistemas de chave pública para o transporte da chave privada, por exemplo, enviar uma mensagem cifrada usando um sistema de chave secreta DES, e a chave secreta ser enviada usando um sistema de chave pública RSA, isto é, o sistema de chave pública transporta a chave privada. Tendo em conta que a chave privada é bastante mais pequena que a mensagem, esta técnica resulta em bastante mais rapidez de processamento que usando apenas um sistema de chave pública. 2 2.3 Assinaturas Digitais A criptografia de chave pública permite a “assinatura digital” de mensagens. Se um indivíduo publicar a chave de decifragem e manter secreta a chave de cifragem, e se enviar mensagens cifradas, todos os receptores usando a chave publicada podem certificar-se que apenas esse individuo poderia ser o emissor. Pode dizer-se que o emissor “assinou” as mensagens. Em alguns sistemas de chave pública, como por exemplo o RSA, tanto a chave pública como a chave privada, podem ser usadas para cifrar e decifrar. Isto permite que um par de chaves possa ser usado simultaneamente para cifragem de mensagens e assinatura digital. Esta prática pode no entanto levantar vários problemas no que diz respeito à gestão do par de chaves. Se o par de chaves for usado para assinatura digital, a chave privada não pode ser guardada, e deve ser destruída no fim do seu ciclo de vida. Se a chave for descoberta pode vir a ser usada para forjar documentos. Se isso acontecer depois do seu ciclo de vida, continua ser possível forjar assinaturas em documentos antigos. Se o par de chaves for usado para garantir privacidade a chave privada deve ser guardada durante o maior tempo possível, porque caso a chave privada seja perdida, será impossível ler as mensagens cifradas com a chave pública. Tendo em conta que para as duas funções têm requisitos contraditórios, o melhor será então usar dois pares de chaves. 2.4 Funções de Hash Normalmente para assinar mensagens usam-se funções de hash em vez da criptografia de chave pública. Uma função de hash criptográfica mapeia uma mensagem de tamanho variável a um número fixo de bits. As funções de hash têm as seguintes propriedades: • Não produzem colisões. Isto é, duas mensagens distintas nunca produzem o mesmo valor. • Só têm um sentido. Isto é, a partir de um determinado valor não é possível produzir uma qualquer mensagem que produza esse valor. Estas funções são também designadas de Message Digest ou algoritmos de fingerprint. Os mais conhecidos são o MD5 e o SH-1. Para assinar uma mensagem são necessários dois passos. Em primeiro lugar aplica-se a função de hash à mensagem, depois o resultado e cifrado usando um sistema de chave pública. A mensagem é depois transmitida juntamente com os seu hash cifrado. Para verificar a assinatura, o receptor aplica a função de hash à mensagem, se o valor obtido for igual ao valor de hash cifrado temos a garantia que a mensagem não foi adulterada. 3 3. Requisitos de uma PKI Descrição dos principais requisitos uma PKI. 3.1 Introdução Os requisitos podem dividir-se em dois tipos: Obrigatórios e opcionais. Os requisitos obrigatórios são aqueles que são comuns a todas as soluções e são necessários para que essas soluções sejam consideradas viáveis. Os requisitos opcionais são mais valias, isto é, são desejados mas não obrigatórios. São um factor fundamental na escolha da melhor solução. Os principais objectivos de uma PKI é a emissão de certificados de chave pública para que seja assegurada a autenticação, privacidade, integridade e autorização para determinadas aplicações críticas. Uma implementação de uma infra-estrutura deste tipo deverá ter vários tipos de componentes, nomeadamente tecnológicos, humanos, políticos e procedimentais. Existem várias alternativas na implementação de PKIs. Muitas dessas alternativas passam pela integração de vários produtos preenchendo assim todos requisitos. 3.2 Requisitos Obrigatórios 3.2.1 Principais Funcionalidades A solução de PKI deve implementar todos as funções e processos que são necessários para que de forma segura e credível sejam criados, distribuídos, e geridos os certificados de chave pública para as entidades-fim (subscritores). Criação de Autoridades de Certificação A solução de PKI deve disponibilizar a criação de Autoridades de Certificação (CA) que possam emitir, certificar, e gerir certificados. Iniciação de Subscritores A solução de PKI deve disponibilizar a possibilidade de iniciar os subscritores na PKI. Devem ser disponibilizados certificados CA-raiz e outros detalhes para que o subscritor tome consciência da PKI e use os serviços de segurança que esta disponibiliza. 4 Registo e Certificação A solução de PKI deve assegurar a emissão e assinatura de certificados. Passa isso deve possuir as seguintes funções: Pedido de registo /certificação inicial. A solução de PKI tem de possibilitar que os subscritores possam iniciar um pedido de um certificado e enviar esse pedido de forma segura à Autoridade de Registo (RA) e/ou à Autoridade de Certificação. A solução de PKI tem de permitir que os subscritores criem um par de chaves pública-privada. Depois, deve ser criado um Pedido de Certificado de Assinatura contendo a chave pública para que o pedido seja enviado de forma segura à RA e/ou à CA. Registo. A solução de PKI tem de permitir a validação da identidade de um subscritor pela RA e/ou CA e associar essa identidade a um determinado pedido de certificado. A RA e/ou a CA têm de ter a capacidade de requerer ao subscritor uma prova de posse, para que seja assegurado que esse subscritor realmente possui e controla a chave privada correspondente à chave pública do certificado a ser enviado. Envio de certificados. A solução de PKI tem de permitir que a RA e/ou a CA possam emitir certificados contendo informação determinada por elas próprias, isto é a informação que os certificados contêm pode ser alterada podendo ser diferente da que foi pedida pelo subscritor no Pedido de Certificado de Assinatura. Os certificados devem poder ser assinados pela(s) CA(s) definidas. Actualização de pares de chaves das CAs. A solução de PKI tem de permitir que as chaves das CAs possam ser actualizadas e que os certificados das CAs sejam reenviados. Certificação cruzada. A solução de PKI tem de permitir o estabelecimento de acordos de certificação cruzada com outras CAs Actualização de certificação cruzada. Os certificados cruzados devem poder ser refrescados. Publicação de Certificados A solução de PKI tem de permitir que os certificados sejam publicados em repositórios de forma a que as entidades que contam com os certificados possam verificar as suas validades e as entidades que requererem a chave publica de um subscritor para cifrar uma sessão ou uma mensagem, possam obte-la através desse repositório. Para além disto, a solução de PKI devem também disponibilizar a possibilidade de se enviarem certificados ao subscritor para instalação no seu software (Servidores web, Browsers, Clientes de e-mail, etc...) ou para os subscritores adquirirem os certificados (por exemplo via HTTP) para depois os instalarem. 5 Anulação de Certificados A solução de PKI tem de permitir processar pedidos de anulação de determinados certificados por parte dos administradores e por parte dos subscritores. Publicação de CRLs A solução de PKI tem de permitir a publicação e distribuição das Listas de Certificados Anulados (CRL) para que as entidades que contam com eles possam ser informadas que esses certificados foram anulados. Mudança do Equipamento dos Subscritores A solução de PKI tem responder às mudanças do software e harware dos subscritores. Para isso deve implementar as seguintes funções: Mover um par de chaves para a nova máquina, enquanto se retém o mesmo certificado que liga este par de chaves a um subscritor particular. É aceitável mas não desejável o reenvio do certificado quando o hardware é alterado. Mudar o mecanismo de protecção que é usado para proteger a imagem guardada da chave privada. É aceitável a regeneração do par de chaves quando o mecanismo de protecção for alterado. Comunicações As comunicações entre os subscritores, gestores, e os componentes da PKI devem ser efectuadas utilizando os canais de comunicação existentes (Rede local, Wan, Internet, etc...) de forma segura. Software pré-existente A solução de PKI tem de ser capaz de gerir certificados de acordo com os formatos e normalizações que existem nas versões actuais do software já existente (Navigator, Explorer, etc...). Normalizações pré-existentes A solução de PKI tem de poder publicar certificados numa estrutura de directório acessível. Por exemplo o LDAP (Lightweight Directory Access Protocol). 6 Autorização de Acessos Deve ser implementada uma Autoridade de Autorização de Acessos (AAA) através da qual administradores distribuídos poderão criar e gerir a autorização de acesso nos directórios LDAP. As entradas de um directório LDAP que são usadas para conter os certificados poderão ser usadas para conterem informação relativa à autorização do acesso. A solução de PKI não deve impor restrições ao formato no directório LDAP, que possam de alguma forma criar conflitos ou obstruir o armazenamento da informação da autorização. A autorização de acessos pode ser armazenada fora da estrutura de certificados para facilitar a separação entre emissão de certificados (CA/RA) e a autorização de acessos (AAA), não sendo necessária a implementação de funções de autorização dentro da solução PKI. No entanto existe uma relação de dependência entre a solução de PKI e a AAA. A AAA usará certificados para assinar a entradas de autorizações para que seja mantida a sua integridade. Escalabilidade A solução de PKI deve ser escalável relativamente ao número de subscritores, deve permitir administração humana distribuída, deve suportar tipos de certificados distintos enviados segundo distintas políticas (as aplicações e os recursos podem ter políticas de certificados distintas e/ou diferentes propósitos para o uso dos certificados). Robustez e tolerância a falhas A solução de PKI deve possuir mecanismos ou características que garantam a continuidade do funcionamento da solução, isto é, deve ser estável e deve “correr” continuamente sem que seja necessária intervenção manual regular (por exemplo reboots). A solução de PKI deve recuperar de falhas nos seus componentes. No caso de uma falha em larga escala nunca poderá resultar numa situação de insegurança (por exemplo acessos a pessoas não autorizadas). Auditorias A solução de PKI deve permitir a auditoria das suas principais funções. Deve ser escrito um log com o registo da actividade da PKI. Deve também ser implementada a capacidade de reconstruir o estado de um certificado a partir de um qualquer tempo no passado. O acesso aos logs deve ser restrito. 7 3.3 Requisitos Opcionais Adaptabilidade A solução de PKI poderá possuir a capacidade de se adaptar a novas políticas de certificados, a novos usos para os certificados e a novos algoritmos criptográficos. Facilidade de implementação A solução de PKI deve ser fácil de implementar. Gestão distribuída dos componentes da PKI A solução de PKI deve suportar a separação opcional dos papeis de CA e RA. A solução de PKI deve suportar vários utilizadores como administradores CA ou RA. A solução de PKI deve suportar a administração distribuída do repositório LDAP de certificados (CR) para que a responsabilidade de autorização possa ser distribuída a vários utilizadores actuando como AAA. Compatibilidade de Normas A solução de PKI deverá emitir e gerir certificados de acordo com as normas existentes: O repositório de certificados deverá ser publicado numa estrutura de directório acessível pelo LDAP. Os certificados devem respeitar a norma X509v3 e suportar extensões locais. As soluções que utilizem Browsers sem serem modificados deverão suportar as extensões do Netscape e Explorer. A gestão de certificados para a interoperabilidade com outras PKIs deve respeitar as normas de IETF PKIX Working Group. A solução PKI deverá suportar o JCE. A solução PKI deverá disponibilizar a capacidade de emitir e gerir certificados para serem usados clientes de e-mail com S/MIME. A solução de PKI não deve disponibilizar serviços através de métodos proprietários ou extensões proprietárias. 8 Interoperabilidade com outros sistemas A solução de PKI deverá poder interoperar com outros componentes de segurança existentes, por exemplo com o Kerberos/DCE, com Objectos assinados, IPsec, etc... Recuperação de Chaves A solução de PKI deverá permitir opcionalmente a recuperação de pares de chaves que tenham sido perdidos ou ficado corrompidos, ou cujo mecanismo de protecção já não seja detido ou conhecido pelo subscritor. Esta funcionalidade pode ser bastante importante, pois informação cifrada que se tenha tornado inacessível pela perda ou corrupção de chaves, poderá desta forma ser novamente acedida pelo subscritor. Desempenho A solução de PKI deverá ser capaz de desempenhar todas as funções em tempo útil gastando o mínimo possível de recursos de computação ou largura de banda de comunicações. 9 4. O Java Criptográfico Estudo dos principais packages criptográficos de Java. 4.1 Avalanche Algoritmos criptográficos DES, Triple-DES, Blowfish, TEA, e cifras de blocos de 3 vias CBC, CFB, OFB, e ECB Todos os modos suportados para todas as cifras de blocos Pike stream cipher, disponibiliza rápida cifragem de dados e uma segura geração de números aleatórios. “Fast DES” opcional que não efectua permutações iniciais nem finais Funções de hash MD2, MD5, e SHA-1 HMAC para autenticação de dados Classes utilitárias Cifragem, autenticação e hashing de streams de input e output O protocolo de stream cifrado usa o PKCS#5 As streams podem ser seguras contra ataques activos e monitorização passiva Conversão de frase chave para chave. Plataforma Java puro, totalmente portável JDK 1.0 e JDK 1.1 10 Números Aleatórios Geração segura de números pseudo-aleatórios para suporte dos algoritmos criptográficos Sistema multi-fonte de medida de entropia A entropia pode ser introduzida no gerador quando este fica disponível 4.2 BeeJCE e BeeCrypt É uma implementação do JCE (Java Cyptographic Extension). Foi desenvolvido porque o JCE não pode ser exportado dos E.U.A (as últimas versões beta podem mas só segundo determinadas condições). O BeeJCE está disponível sob uma licença GNU (Lesser General Public License) que pode ser usado livremente e grátis para aplicações comerciais. O código fonte encontra-se também disponível. O BeeJCE pode ser usado juntamente com o BeeCrypt for Java que consiste numa biblioteca criptográfica e num JCE 1.2 cryptographic service provider. O BeeCrypt contém : Fontes de entropia para inicializar a geração de números pseudo-aleatórios Geradores de números pseudo-aleatórios : FIPS-186, Mersenne Twister Cifras de blocos: Blowfish Funções de hash: SHA-1 Funções de hash com chaves: SHA-1/HMAC Biblioteca de números inteiros de precisão variável, com rotinas implementadas em Assembler para optimização. Teste probabilistico de primos Geração de parâmetros logarítmico discretos sobre um campo de um primo Acordo de chaves Diffie-Hellman Duas variantes do ElGamal para assinaturas. 11 4.3 ABA JCE É uma implementação do JCE (Java Cyptographic Extension). Foi desenvolvido porque o JCE não pode ser exportado dos E.U.A tal como BeeJCE. O objectivo desta implementação é ser o mais fiel possível à implementação da Sun Microsystems. Algoritmos de cifragem O ABA cryptographic provider disponibiliza os seguintes algoritmos de cifragem: DES, DESede (ou "triple DES"), IDEATM, RC4®, Blowfish, Twofish e RSA. Existem geradores de chaves para todos os algoritmos. Algoritmos de Message Digest Algoritmos de Message Digest: SHA-0, SHA-1, e MD5. Licença Licença de domínio público e código fonte disponível. 4.4 BC Crypto O package Bouncy Castle Crypto é mais uma implementação de um pacote criptográfico em Java. Esta implementação está em conformidade com o JCE. Possui uma licença do tipo MIT X Consortium. O BC Crypto as seguintes características: É uma implementação do JCE. Tem suporte para : Cifras de blocos, Cifras de blocos com buffers, Cifras de blocos assimétricas, Cifras de blocos assimétricas com buffers, Cifras de 12 Digest: MD2, MD5, RipeMD160, SHA-1. MAC: CBCBlockCipherMac, CFBBlockCipherMac, Hmac.. PBE: PKCS5S1ParametersGenerator, PKCS5S2ParametersGenerator, PKCS12ParameterGenerator. Acordo de chaves: Duas versões do Diffie-Hellman, em que uma é a básica e outra para chaves de longo prazo. ASN.1 : Uma API com interface directo sobre um package capaz de ler e escrever na codificação DER para geração de certificados X509v3. Também está implementado BER InputStream. BC Provider : Compatível com o JCE. 4.5 IAIK Também o IAIK é uma alternativa ao JCE dadas as restrições à suaeaçoçsriud 13 Implementação do JCE Cifras: DES, DESede, IDEA, Blowfish, GOST, CAST128, RC2, RC4, RC5, RSA (PKCS#1), PbeWithMD5AndDES_CBC (PKCS#5), PbeWithSHAAnd3_KeyTripleDES (PKCS#12), PbeWithSHAAnd40BitRC2_CBC (PKCS#12) Fábricas de chave secreta: DES, DESede, PBE (PKCS#5), PKCS#12 Algoritmos de acordos de chaves: DH (PKCS#3) MACs: HMAC/MD5, HMAC/SHA Geradores de chaves: DES, DESede, IDEA, Blowfish, GOST, CAST128, RC2, RC4, RC5, RSA, PKCS#12, PKCS#12-IV, PKCS#12-MAC, PBKDF2 (PKCS#5 ver 2) Modos : ECB, CBC, PCBC, CFB, OFB, 0 1 e 2 para RSA (PKCS#1) Padding: PKCS5Padding, SSL3Padding, PKCS1Padding ASN.1 ASN.1 (Abstract Syntax Notation One) Definido na norma ISO 8824/ITU X.208 que especifica uma linguagem para descrever estruturas de dados de uma forma abstracta e independente da plataforma. O IAIK suporta os tipos de dados: BOOLEAN, INTEGER, BITSTRING, OCTETSTRING, NULL, OBJECTIDENTIFIER, ENUMERATED, SEQUENCE, SET, SEQUENCE OF, SET OF, UTCTime, etc… Utilitários para codificação e descodificação para DER, Base64, e PEM para que estruturas ASN.1 possam ser implementadas como classes de Java. PKCS PKCS#1 – Norma criptográfica RSA PKCS#5 – Norma de cifragem baseada em palavra-chave. PKCS#7 – Norma do sintaxe de mensagens criptográficas. PKCS#8 – Norma do sintaxe de informação sobre chaves privadas. PKCS#10 – Norma do sintaxe do pedido de certificado. PKCS#12 – Norma do sintaxe de troca de informação pessoal. X509 14 O IAIK suporta todas as extensões da norma x509v3, extensões CRL da norma x509v2 e também as extensões de certificados da Netscape. X509v3: AuthorityKeyIdentifier, BasicConstraints, CertificatePolicies, CRLDistributionPoints, IssuerAltName, KeyUsage, NameContraints, PolicyConstraints, PolicyMappings, PrivateKeyUsagePeriod, SubjectAltName, SubjectKeyIdentifier. CRL X509v2: CRLNumber, ReasonCode. Extenções de certificados Netscape: NetscapeBaseUrl, NetscapeCaPolicyUrl, NetscapeCaRevogationUrl, NetscapeCertRenewalUrl, NetscapeCertType, NetscapeComment, NetscapeRevocationUrl, NetscapeSSLServerName Geração de Números Aleatórios O IAIK possui vários geradores de números aleatórios. 4.5 JCSI O JCSI inclui: JCE Uma implementação do JCE compatível com o JCE 1.2 Um security provider para o JCA/JCE para: Chave Pública: RSA, DSA, Diffie-Hellman. Cifras: IDEA, DESede, Blowfish, RC5, RC4, RC2 e DES PKI Uma biblioteca para desenvolvimento de PKI Fornecedor de fábrica de certificados x509v3 e v2 CRLs PKCS#12 – Armazenagem de chaves Uma API para PKCS#10 – Pedidos de certificados Uma API para PKCS#8 e protecção de chaves privadas tipo SSLeavy Uma API para geração de certificados x509 e CRLs que podem ser usada para construir ferramentas para uma CA. TLS Uma biblioteca que implementa uma camada de transporte segura (TLS) Suporte para SSLv3 e TLS 15 Suporta todos os conjuntos de cifras não anónimas baseadas em RSA ou Diffie-Hellman Implementa a API JESSE Mensagens Criptográficas Uma biblioteca para sintaxe de mensagens criptográficas Suporta CMS SignedData e EnvelopedData Suporta RSA e DSA para assinaturas Suporta RSA e Diffie-Hellman para cifrar S/MIME Uma biblioteca para suporte de S/MIME v3 Assinatura e cifragem de mensagens MIME RSA e DSA para assinaturas e RSA para cifragem Assinatura limpa e opaca Incorporação no framework JavaMail Kerberos Uma biblioteca para o Kerberos 5 Uma API para efectuar pedidos e processar respostas de um centro de distribuição de chaves Kerberos (KDC). Uma API para GSS (IETF RFC2853) para mensagens ao nivel da aplicação. Licença Este software é gratuito para aplicações não comerciais. É possível discutir alternativas à licença contactando DSTC Pty. Ltd, mail: [email protected]. 16 5. Funcionalidades de uma PKI e respectiva implementação em Java Estudo das principais funcionalidades de uma infra-estrutura de chaves públicas e respectiva implementação em Java criptográfico 5.1 Introdução Neste capitulo irão ser descritos em detalhe os componentes fundamentais de uma Infra-estrutura de Chave Pública, assim como que recursos de programação em Java que o JCA/JCE, assim como ouros packages disponibilizam para a implementação de cada um desses componentes. Tipicamente uma PKI é composta por cinco componentes básicos. Três desses componentes podem ser considerados como o núcleo da PKI: a Autoridade de Registo (RA), a Autoridade de Certificação (CA) e o Repositório de Certificados (CR). Os dois componentes restantes representam as entidades que usam os certificados para impor segurança: os Subscritores (são também vulgarmente designados por entidades-fim) e Aplicações/Utilizadores (também designados por “relying entities”). 5.1 Subscritor O Subscritor é que possui o certificado. O objectivo dos certificados é atribuir um par de chaves a uma identidade. A identidade pode ser um utilizador humano ou pode ser um recurso de informação (aplicações). Na prática, o objectivo dos certificados pode ser: verificação de identidade (autenticação), criação e verificação de assinaturas digitais, ou a troca de mensagens cifradas. Normalmente é necessária bastante interacção entre vários utilizadores humanos (exemplo: trocas de e-mails), entre utilizadores humanos e recursos de informação (exemplo: aplicações cliente-servidor), ou entre vários processos computacionais (exemplo: pontos fim de VPNs). Nestes casos cada entidade desempenha simultaneamente os papeis de subscritor e aplicação/utilizador (entidade que confia). 5.1.1 Iniciar Pedido de Certificado Esta função representa o primeiro passo no ciclo de vida do certificado. Pode ser efectuado por um subscritor humano, um administrador actuando em representação de um subscritor não humano, ou por um administrador de segurança. 17 5.1.2 Gerar par de chaves Os pares de chaves podem ser gerados por software cliente, um servidor central de segurança, o motor criptográfico de smart cards. São geradas as chaves publicas e as chaves privadas. É uma preocupação primária a protecção da chave privada depois de ter sido gerada. Grande parte das implementações tentam minimizar o número de humanos que podem aceder à chave privada. Na realidade, a chave privada geralmente nunca é vista pelo utilizador humano. Existe um token ou palavra chave que serve para desbloquear a chave privada. Algumas aplicações dividem a chave privada em várias partes e/ou mantêm várias cópias da chave para recuperação. Em Java, para se gerarem par de chaves, têm de se seguir os seguintes passos: Criar um gerador de pares de chaves O primeiro passo é obter um gerador de pares de chaves. Da mesma forma que para todas as classes tipo motor, para se obter um objecto KeyPairGenerator para um determinado tipo de algoritmo, é necessário invocar o método “fábrica” getInstance static na classe KeyPairGenerator. Existem duas formas de invocar este método: uma apenas tem um argumento que é uma string correspondente ao algoritmo, a outra tem um argumento adicional que consiste numa string que especifica qual o provider a ser utilizado. Pode-se desta forma escolher um algoritmo de um qualquer provider que pode ser o da Sun ou um dos referidos no capítulo anterior. Vejamos um exemplo: KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN"); Iniciar o gerador de pares de chaves O passo seguinte é iniciar o gerador de pares de chaves. Todos os geradores de pares de chaves têm em comum os conceitos de tamanho da chave e fonte de aleatori edade. A classe KeyPairGenerator tem um método que se chama initialize que recebe estes dois conceitos como argumentos. Por exemplo, para o algoritmo DSA o tamanho da chave em bits deve ser 1024. A fonte de aleatoriedade deverá ser uma instância da classe SecureRandom. Vejamos um exemplo em que usa o gerador de pseudo-números aleatórios SHA1PRNG disponibilizado pela Sun. A instância da classe SecureRandom é passada ao método de iniciação do gerador de pares de chaves. SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); keyGen.initialize(1024, random); 18 A implementação de SecureRandom irá tentar ela mesma gerar um estado interno aleatório, a não ser que seja invocado o método setSeed a seguir ao getInstance. Caso isto seja feito, o gerador de números aleatórios irá usar a semente indicada. Gerar o par de chaves O passo final é i gerar o par de chaves é guarda-las nos objectos PrivateKey e PublicKey. Vejamos: KeyPair pair = keyGen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); 5.1.3 Criar pedido de certificado O software cliente gera um pedido de certificado que contem a identidade do subscritor e a sua chave pública. O formato normalmente usado é o PKCS#10. O JCA/JCE da Sun não implementa este protocolo. No entanto pode ser construída uma classe com base na notação ASN.1 que implemente o sintaxe para pedidos de certificados segundo o PKCS#10: CertificationRequest ::= SEQUENCE { certificationRequestInfo CertificationRequestInfo, signatureAlgorithm SignatureAlgorithmIdentifier, signature Signature } CertificationRequestInfo ::= SEQUENCE { version Version, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, attributes [0] IMPLICIT Attributes } Version ::= INTEGER Attributes ::= SET OF Attribute SignatureAlgorithmIdentifier ::= AlgorithmIdentifier Signature ::= BIT STRING O JCSI por exemplo, já tem suporte para este protocolo e implementa: Construtores Que geram pedidos de certificados baseando-se na codificação DER, com o seguinte sintaxe: PKCS10CertificationRequest(byte[] encoded) ou PKCS10CertificationRequest(InputStream is) caso a origem seja um stream. 19 Que geram pedidos de certificados a partir do DN (Distinguished Name) do sujeito, chave pública, um conjunto de atributos, o nome do algoritmo de assinatura e a chave privada do sujeito. PKCS10CertificationRequest(String dn, PublicKey pub, Set atts, String sigAlgName, PrivateKey priv) Métodos Um método que devolve a codificação DER do pedido de certificado. Métodos que devolvem a chave pública, o nome do algoritmo da chave, o nome do algoritmo de assinatura e o nome do sujeito. Um método para verificar a assinatura. Envio do pedido de certificado O Envio do pedido de certificado pode ser enviado à CA de forma segura usando por exemplo SSL. Existe uma implementação de referência em Java: o JESSE de Sun. Por exemplo o JCSI tem um implementação deste modelo. Vejamos um exemplo: import java.io.*; import javax.net.ssl.*; ... int port = availablePortNumber; String host = "hostname"; try { SLSocketFactory sslFact = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket s = (SSLSocket)sslFact.createSocket(host, port); OutputStream out = s.getOutputStream(); InputStream in = s.getInputStream(); // Para enviar o pedido de certificado pode por exemplo ser // codificado na codificação DER e enviado para o // Stream out // } catch(IOException e) { } 20 5.1.4 Iniciar pedido de actualização de chaves Para que uma chave seja substituída no final do seu ciclo de vida (é politica que determina a duração da chave), o software cliente ou o subscritor terá de pedir um novo certificado. Para isso terá de gerar uma nova chave pública e uma nova chave privada. Os pedidos de actualização de chaves confiam na integridade dos certificados anteriores, por isso não podem servir para substituição de chaves perdidas ou roubadas. Caso uma chave seja perdida o subscritor não pode provar a sua possessão. No caso de ser roubada não é possível destinguir o subscritor real do impostor. Em qualquer um destes casos o subscritor necessita de fazer um novo pedido de certificado. Determinar o final do ciclo de vida de uma chave A classe X509Certificate do javax.security.cert (JDK) tem um método que permite verificar a validade do certificado (checkValidity). Desta forma pode-se determinar se uma chave atingiu ou está próxima em atingir o final do seu ciclo de vida. Gerar pedido de actualização de chaves Como os pedidos de actualização de chaves confiam no certificado anteriormente emitido, o pedido de actualização de chaves terá de ser enviado de forma que seja provado que o subscritor possui o certificado antigo. Deverá ser enviada a nova chave pública e a identificação do subscritor. Para isso é necessário gerar um novo par de chaves (ver 5.1.2). Uma solução simples será gerar um novo pedido de certificado (PKCS#10) e assina-lo com a chave privada antiga. Desta forma o subscritor poderá provar a posse do certificado anterior. Obter um objecto para assinatura Para se obter um objecto para assinaturas, tem de ser invocado o método getInstance da classe Signature. Este método recebe dois argumentos: o nome do algoritmo e o security provider ou fornecedor de segurança. Para obter o algoritmo do certificado que está a expirar pode usar-se o método getSigAlgName da classe X509Certificate. O certificado pode ser lido da seguinte forma : CertificateFactory cf = CertificateFactory.getInstance("X.509"); FileInputStream fis = new FileInputStream("NomeDoFicheiro"); X509Certificate certx509 = (X509Certificate) cf.generateCertificate(fis); O algoritmo de assinatura pode ser obtido com: Signature sig = Signature.getInstance(certx509.getSigAlgName(), "SUN"); 21 Iniciar o objecto de assinatura Para que o objecto de assinatura seja iniciado é necessário passar-lhe a chave privada. A chave privada, caso esteja implementado o protocolo PKCS#8, pode ser obtida através de um método que a decifre e outro que a devolva. Por exemplo o JCSI implementa uma classe PKCS8EncryptedPrivateKey que disponibiliza construtores para, por exemplo obter a chave a partir de um Stream (ficheiro, socket, etc...), e métodos para decifrar a chave privada, por exemplo através de uma palavra chave ( decrypt(char[] password) ), e também um método para obter a chave ( PrivateKey getPrivateKey() ). Assim, após obtida a chave sob a forma de um objecto PrivateKey, podemos finalmente iniciar o objecto de assinatura. sig.initSign(priv); Podemos por exemplo obter o novo pedido de certificado sob a codificação DER através do método getEncoded() da classe PKCS10CertificationRequest e assina-lo com a chave privada. byte[] buffer = novoPedidoDeCert.getEncoded(); sig.update(buffer); byte[] pedidoCertAssinado = sig.sign(); Envio do pedido de actualização de chaves O pedido de actualização pode ser enviado via canal seguro usando SSL (ver 5.1.3) 5.1.5 Instalar chave privada O software cliente tem de possibilitar o armazenamento da chave privada depois de gerada. Pode ser guardada em diferentes suportes, cifrada e protegida por password. Existem vários protocolos para as chaves privadas, por exemplo o PKCS#5 que especifica as normas de cifragem com base em palavra chave, o PKCS#8 que especifica uma norma para o sintaxe da informação, ou o PKCS#11 que um interface para tokens criptográficos para serem usados por smartcards. Como já vimos anteriormente, o JCSI implementa a norma PKCS#8. Podemos através desta implementação facilmente guardar a chave privada cifrada e protegida por palavra chave da seguinte maneira: Guardar a chave privada Vejamos um exemplo para guardar a chave privada através da palavra chave “myPassword” num ficheiro: PKCS8EncryptedPrivateKey encKey = new PKCS8EncryptedPrivateKey(privateKey); FileOutputStream fos = new FileOutputStream(fileName); encKey.encrypt("myPassword".toCharArray()); fos.write(encKey.getEncoded()); 22 Ler a chave privada Vejamos agora um exemplo para ler e decifrar a chave privada guardada num ficheiro: FileInputStream fis = new FileInputStream("NomeDoFicheiro"); PKCS8EncryptedPrivateKey encKey = new PKCS8EncryptedPrivateKey(fis); encKey.decrypt("myPassword".toCharArray()); PrivateKey privateKey = encKey.getPrivateKey(); 5.1.6 Prova de posse de chave privada O software cliente tem de responder a pedidos de outros componentes da PKI para provar a posse de chave privada. Normalmente, o que se faz é cifrar alguns dados com a chave privada e enviar como resposta para que seja decifrado com a chave pública correspondente. Isto pode ser feito simplesmente assinando o pedido de prova de posse de chave privada com a chave privada do subscritor e envia-lo como resposta. Para as comunicações pode usar-se por exemplo SSL (JESSE com em 5.1.3). Para obter a chave privada pode usar-se uma implementação do PKCS#8 ou a classe PKCS8EncryptedPrivateKey do JCSI (ver 5.1.5), para assinar a resposta pode utilizar-se a classe Signature do JDK. (como em 5.1.4). 5.1.7 Instalar e guardar certificados O software cliente tem de poder guardar o certificado do subscritor. Em alguns casos, quando o formato de armazenamento é proprietário do software cliente, deve existir a possibilidade de exportar sob o formato PKCS#12. Não é necessário guardar de forma cifrada nem segura, porque os certificados são 23 5.1.8 Assinar e decifrar com a chave privada O software cliente tem de ter a possibilidade de cifrar e decifrar informação com a chave privada. Os métodos de cifragem podem variar com o tipo de aplicações. Normalmente, no envio de mensagens, a chave privada é usada para cifrar a message digest que funciona como assinatura digital, para que o conteúdo e a fonte da informação possa ser verificada pelos receptores. Na recepção de mensagens, a chave privada é usada para decifrar a informação que foi cifrada pelo emissor através da chave publica do subscritor. Para se poder usar a chave privada é necessário que o subscritor decifre a chave através da sua palavra chave. Caso a chave privada esteja guardada num token físico (por exemplo: smartcard) o subscritor tem de possuir esse token. Já vimos anteriormente como se pode obter e assinar com a chave privada (ver 5.1.4). 5.1.9 Apresentar o certificado aos outros O software cliente tem de poder enviar o certificado do subscritor às aplicações, servidores, e outros subscritores que necessitam saber a identidade do subscritor e a sua chave pública. A apresentação do certificado é semelhante a uma afirmação de identidade. É necessária uma prova de posse (ver 5.1.6) para garantir que o certificado apresentado é autentico. A apresentação de certificados pode não ser necessária quando existe um repositório de certificados, isto é, o certificado do subscritor pode ser pesquisado em vez de ser apresentado directamente. A forma de implementar esta funcionalidade é bastante semelhante à prova de posse de chave privada. Basta que o certificado seja enviado à aplicação, servidor, utilizador, etc assinado com a chave privada do subscritor. Desta forma pode ser apresentado o certificado e provada a posse da chave privada. As mensagens podem ser trocadas por exemplo através de SSL ( JESSE como anteriormente foi discutido em 5.1.3). A obtenção da chave privada pode ser obtida através de uma implementação do protocolo PKCS#8 (por exemplo a classe PKCS8EncryptedPrivateKey do JCSI como em 5.1.5). O certificado pode ser lido como em 5.1.4 e assinado da seguinte forma: CertificateFactory cf = CertificateFactory.getInstance("X.509"); FileInputStream fis = new FileInputStream("NomeDoFicheiro"); X509Certificate certx509 = (X509Certificate) cf.generateCertificate(fis); Byte[] buffer = certx509.getEncoded(); Signature sig = Signature.getInstance(certx509.getSigAlgName(), "SUN"); sig.initSign(chavePrivada); sig.update(buffer); byte[] certx509Assinado = sig.sign(); Finalmente pode ser enviado à aplicação, servidor ou utilizador. (como em 5.1.3) 24 5.1.10 Iniciar pedido de revogação O subscritor tem de ter a possibilidade de efectuar um pedido para que o seu certificado seja anulado. Obviamente esse pedido tem de ser assinado pela chave privada do subscritor, a não ser que a o motivo do pedido de revogação do certificado seja a perda total da chave. Neste caso a RA ou a CA têm de iniciar o processo de revogação. Uma forma simples de codificar esta funcionalidade em Java é codificar uma mensagem relativa ao pedido de revogação assinada com a chave privada do subscritor (ver 5.1.4 ou 5.1.9). Caso o subscritor tenha perdido a sua chave, deverá contactar a CA ou RA para que o seu certificado seja revogado. 5.1.11 Iniciar pedido extensão do prazo de validade O subscritor tem de ter a possibilidade de efectuar um pedido, para que um certificado que esteja em vias de expirar, tenha o prazo prolongado. Se este processo é feito de forma automática ou não depende da política de implementação. Isto pode ser implementado em Java enviando uma mensagem assinada com a chave privada do subscritor a pedir o prolongamento do prazo de validade do certificado (na alínea anterior). O certificado terá de ser reemitido pela CA, caso o pedido seja atendido. Normalmente o envio de certificados é feito segundo a norma PKCS#7. O código que se segue serve para obter os certificados a partir de um stream (que pode ser um ficheiro ou um socket SSL). CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate( inputStream); Depois de receber o certificado com o no prazo de validade estabelecido basta substituir o certificado antigo pelo novo certificado. 5.2 Autoridade de Registo A Autoridade de Registo (RA) é o componente responsável por assegurar que a ligação entre os pares de chaves e as identidades é realizada segundo determinadas políticas. Normalmente este componente consiste numa aplicação que um operador humano manipula para verificar as identidades dos subscritores, no entanto algumas funções podem ser automatizadas. Nem sempre este componente se encontra distinguido, por vezes é incluído na Autoridade de Certificação. 25 5.2.1 Verificar a identidade do subscritor Esta função pode ou não ser automática dependendo da política de implementação. Tendo em conta que o certificado serve para associar uma identidade a um par de chaves, a RA tem de efectuar os testes de identidade necessários para que esta associação seja credível tendo em conta os objectivos do uso do certificado. Em termo práticos podem ser usadas muitas estratégias de verificação de identidade. Por exemplo a validação automática de endereços de e-mail, ou requerer informação exaustiva acerca do subscritor, como por exemplo (certidão de nascimento, passaporte, BI, etc....). Para aplicações em intranets, os testes de verificação de identidade podem ser semelhantes à emissão do cartão do funcionário da organização. 5.2.2 Aprovar ou modificar o conteúdo do certificado O pedido de certificado é analisado, por um humano ou pelo software da RA. É verificado o conteúdo de cada campo. Alguns campos poderão ser modificados para respeitar determinadas políticas pré-definidas. A RA poderá aprovar ou reprovar os pedidos de certificados em função de critérios pré-estabelecidos. Em algumas implementações, o subscritor pode pedir certos campos como por exemplo o nome comum. No entanto, a RA deve poder sobrepor esses pedidos para que seja cumprida uma determinada política local de funcionamento. Esta política estabelece quais os campos serão preenchidos pelo subscritor e quais os que serão preenchidos pela RA e ainda que campos serão definidos pela política de funcionamento. Em Java já vimos anteriormente como podem ser manipulados os pedidos de certificados utilizando uma implementação do protocolo PKCS#10 (por exemplo a classe PKCS10CertificationRequest do JCSI em 5.1.3) O campo attributes do PKCS#10 Este campo pode ser usado para adicionar ao pedido, informação acerca do certificado ou do subscritor. Os campos estão descritos no PKCS#6. O campo attributes está na notação ASN.1 logo é necessário implementar classes da Java que implementem esta notação para criar estes campos adicionais. 5.2.3 Gerar pares de chaves Esta função é opcional, isto é, a RA pode opcionalmente gerar o par de chaves para o subscritor, o que implica ter implementado o mesmo tipo de aplicações descritas em 5.1.2. Este tipo de função é usada em RAs quando existe a necessidade que as chaves tenham de ser geradas por software ou hardware dedicado existente na RA. 26 Aparentemente a geração do par de chaves pela RA, é mais insegura do que a geração local, no entanto o software usado para gerar as chaves que existe no subscritor pode ser mais vulnerável a diversos tipos de ataques durante o processo de geração das chaves, tendo em conta que os PC são geralmente inseguros. As RAs podem empregar mecanismos muito seguros de hardware para gerar os pares de chaves. As chaves são depois enviadas de forma segura ao subscritor. Esta técnica tem no entanto a desvantagem de reduzir a credibilidade da assinatura digital. O subscritor pode afirmar que a RA autorizou a outra pessoa o acesso à sua chave privada. A adesão ou não adesão a este sistema é uma questão de política de implementação da RA. 5.2.4 Guardar pares de chaves Esta função não é obrigatória e implica que a RA tenha gerado o par de chaves do subscritor ou conheça a sua chave secreta. Se estiver implementado um serviço de recuperação de chaves, a RA tem de manter armazenados os pares de chaves dos subscritores. A forma como as chaves são armazenadas tem de ser o mais segura possível para não comprometer todo o sistema. O mais comum é manter um arquivo das chaves de cifragem em vez de chaves de assinatura, o que implica a existência de dois pares de chaves. No caso das chaves de cifragem, a perda da chave privada implica que a informação se torne ilegível para o subscritor. Neste caso a chave pode ser recuperada e a informação pode tornar-se novamente legível. No caso das chaves de assinatura, é vital que apenas o subscritor tenha controlo sobre a chave, porque o que está em causa é a sua credibilidade. No desenvolvimento deste componente, caso seja usado o Java, têm de ser consideradas todas a precauções de segurança para que a base de dados onde estão guardados os pares de chaves dos subscritores, seja inviolável. O acesso à base de dados pode ser feito por JDBC. É conveniente que o SGBD seja o mais seguro possível. 5.2.5 Distribuição de tokens No caso das chaves serem geradas pela RA, a chave privada poderá ser entregue ao subscritor num suporte físico (smartcard, disquete, etc...). Para que isto seja possível a chave privada terá de ser instalada num token. A norma que é utilizada para interface com funções de PKI existentes num smartcard é o PKCS#11. 5.2.6 Testes de posse de chave privada Antes da RA aprovar um certificado que associe uma chave pública a uma identidade, é necessário saber se o subscritor possui a chave privada correspondente. Para isso a RA pode por exemplo enviar uma determinada informação aleatória ao subscritor e pedir que a assine com a sua chave privada e a envie de novo à RA. Se a RA confirmar a assinatura da mensagem, quer dizer que o subscritor é realmente detentor da chave privada. Para implementar em Java esta função basta obter a chave publica do subscritor. Para isso pode usar-se ou programar uma implementação da norma PKCS#10 (por exemplo a classe PKCS10CertificationRequest do JCSI em 5.1.3). Depois do pedido de certificado ter ser recebido pela 27 RA pode-se obter a chave pública do subscritor invocando o método getPublicKey(). Para verificar se a assinatura é autentica pode fazer-se: Obter chave pública PublicKey chavePublica= pedidoDeCertificado.getPublicKey(); Iniciar objecto de assinatura Inicia-se uma instância do objecto de assinatura para verificação com o algoritmo prédefinido para o certificado do subscritor. Signature assinatura = Signature.getInstance(algoritmo); assinatura.initVerify(chavePublica); Assinar a mensagem Obtém-se a mensagem sob a forma de array de bytes e passa-se a mensagem ao objecto de assinatura. byte[] mensagem = "Mensagem".getBytes(); assinatura.update(mensagem); Verificar a assinatura Compara-se a assinatura obtida com a assinatura que a mensagem transporta. if (assinatura.verify(assinatura_da_mensagem)) { // assinatura válida } else { // assinatura inválida } 5.2.7 Atribuição de DN e modificação do conteúdo do certificado A RA deve poder editar o Distinghished Name (DN) dos certificados e outro tipo de conteúdos para corresponder a determinadas políticas. Se existir uma verificação automática de verificações de pedidos, é comum por exemplo que haja um formato normalizado para o DN, ou numeração sequencial para os certificados. 28 5.3 Autoridade de Certificação A Autoridade de Certificação (CA) é o componente da PKI responsável pela gestão dos certificados. Inicialmente a CA cria os certificados e adiciona-lhes a “assinatura CA” que garante a integridade do certificado. Não pode ser modificado sem invalidar a assinatura, por isso, qualquer certificado com uma “assinatura CA” válida é garantido ter os mesmos conteúdos desde a sua geração. A CA é também responsável por revogar (anular) os certificados que por qualquer motivo deixam de ser válidos. As políticas de funcionamento da CA pode necessitar de administração humana para aprovar a criação, a revogação de certificados. O funcionamento da CA pode também ser automatizado. A função que normalmente é executada por um operador humano é a de verificação da identidade do subscritor. Como já foi visto anteriormente, esta função pode ser, caso exista, incorporada numa RA, e a CA só deve emitir os certificados se forem anteriormente aprovados pela RA sem a necessidade intervenção humana. 5.3.1 Responder a pedidos de renovação de certificados A CA pode, de acordo com uma determinada política, aprovar pedidos dos subscritores para renovação de certificados (5.1.4 e 5.1.11), quer relativamente ao prazo de validade das chaves, quer a renovação das chaves. Partindo do principio que a identidade do subscritor foi provada quando o certificado original foi emitido, estas funções não exigem o envolvimento da RA. De qualquer forma, se houver políticas que o exijam, pode ser revalidada a identidade do subscritor.. A implementação desta função em Java deve receber o pedido de renovação, desempacotar o pedido e estabelecer o novo período de validade e reenviar o certificado ao subscritor, isto se o pedido for de prolongamento da validade do certificado. Caso o pedido seja de renovação das chaves terá de ser exigida uma prova de posse da chave privada para garantir que a chave pública corresponde à chave privada do subscritor. 5.3.2 Modificar ou aprovar modificações do conteúdo do certificado De forma similar RA, também a CA pode implementar a funcionalidade de responder a pedidos de modificação do conteúdo ou modificar ela mesma o conteúdo de um certificado. Esta função pode ser desempenhada por um operador humano. As modificações podem incluir extensões de certificados, por exemplo, extensões Netscape ou Microsoft. A implementação em Java desta funcionalidade deve seguir os seguintes passos: Caso seja um pedido de um subscritor, esse pedido deve ser desempacotado, a CA deve depois aceitar ou reprovar o pedido, caso o pedido seja aprovado o certificado deve ser alterado e reenviado ao subscritor. Caso seja uma alteração do conteúdo pela CA, o certificado deve ser alterado e reenviado ao subscritor. 29 5.3.3 Iniciar e aprovar revogação A CA terá de aprovar pedidos de revogação de certificados provenientes da RA ou do subscritor. A CA também poderá iniciar o processo de revogação de certificados de acordo com uma determinada política (por exemplo: prazo de validade das chaves). No caso de serem envidados pedidos de revogação pela RA e pelo subscritor é porque as chaves foram perdidas ou de alguma forma encontram-se comprometidas no que diz respeito a segurança. 5.3.4 Criar certificado raiz auto assinado A CA deve poder criar e assinar certificados CA para ela própria e para as CAs subordinadas. Opcionalmente, o certificado CA poderá ser gerado por uma outra entidade que mantenha uma relação de confiança. O certificado de nível de topo ou certificado raiz é sempre auto assinado. O simples facto do certificado conter uma chave pública não se lhe deve atribuir confiança, porque qualquer um pode criar um certificado auto assinado com o distinguished name de uma CA bem conhecida. A confiança deve advir do facto de a chave pública desse certificado ser uma chave pública bem conhecida para essa CA que pode ser verificada a partir de outras fontes como por exemplo anúncios. A única razão pela chave pública ser guardada no certificado raiz é porque é exigido o seu preenchimento no formato de certificados na maior parte das ferramentas. Neste caso, o certificado é usado para que transporte a chave pública da CA para que possa ser verificada através de outras fontes. Gerar um certificado a partir de um par de chaves O comando genkey do utilitário keytool pode ser usado para criar um par de chaves e para empacotar a chave pública num certificado auto assinado. Neste caso, é necessário atribuir uma palavra chave para o armazém de chaves que irá conter a chave secreta e essa palavra chave. Também será necessário providenciar informação relativa à entidade que irá possuir o certificado. Também é possível criar um novo certificado usando um par de chaves associado a um certificado gerado anteriormente. Gerar um certificado CA auto assinado com o JCSI Para gerar um certificado CA auto assinado pode usar-se o seguinte construtor da classe X509CertGen : X509CertGen(String issName, Signature sig) 5.3.5 Emissão de certificados A CA tem de poder emitir e assinar certificados. A assinatura garante aos receptores que o certificado foi emitido de acordo com a política de publicação da CA. A chave privada para efectuar esta assinatura deve estar bem segura. O JDK não disponibiliza ferramentas de programação para a criação de certificados, apenas disponibiliza a capacidade de importar certificados e usa-los de várias formas. Por exemplo o JCSI 30 disponibiliza classes para manipulação e geração de certificados. Outra alternativa é programar as classes para geração e manipulação de certificados a partir das normas X500 e X509. Os certificados X509 são constituidos pela seguinte informação: Versão Número de Série Identificador de algoritmo para assinatura X500 (nome da entidade geradora, conhecido como distinguished name) Período de validade Nome da entidade identificada com a chave pública (subject name) Informação relativa à chave publica dessa entidade (chave pública, algoritmo, parâmetros) Os X500 Distinguished Names são constituídos por : Nome comum Nome do departamento Nome da organização Nome da localidade Nome do estado ou província Código do país Vejamos como poderia ser criado um certificado para um determinado subscritor: X509CertGen cg = new X509CertGen(caSignature, caCert); cg.setPublicKey(userPublicKey); cg.setSerialNumber(BigInteger.valueOf((long)12345678 )); cg.setSubjectDN("CN = John Smith, OU=Security, O=DSTC, C=AU"); cg.setValidity(365); cg.setSubjectEmail("[email protected]"); X509Certificate userCert = cg.getCertificate(); 5.3.6 Disponibilizar os certificados aos subscritores A CA deve ter a capacidade de entregar os certificados aos subscritores. Isto pode não ser obrigatório, isto é, caso exista um Repositório de Certificados, a solução de PKI pode requerer ao subscritor que obtenha o seu certificado a directamente a partir desse repositório após ter sido publicado. No entanto, a maior parte das soluções implementam a capacidade de enviar o certificado ao subscritor. Normalmente a CA indica ao subscritor onde pode descarregar o certificado. 31 O formato usado para a entrega de certificados é o PKCS#7. O JDK não disponibiliza ferramentas para criar ou manipular este formato. Existem duas alternativas, ou se desenvolvem classes para implementar este protocolo, ou utiliza-se um framework que o implemente. Por exemplo o IAIK, implementa uma classe chamada PKCS7CertList que manipula correntes de certificados compatíveis com o Netscape Navigator e Microsoft Explorer. Esta classe disponibiliza vários métodos e construtores para criar um objecto assinado que contém uma lista de certificados. Essa lista pode ser escrita ou lida de um ficheiro (com a extenção .p7c). Vejamos um exemplo para escrever a lista: X509Certificate[] certs = ...; PKCS7CertList pkcs7 = new PKCS7CertList(); pkcs7.setCertificateList(certs); pkcs7.writeTo(new FileOutputStream(pkcs7File)); Para ler a lista: PKCS7CertList pkcs7 = new PKCS7CertList(new FileInputStream("certs.p7c")); X509Certificate[] certs = pkcs7.getCertificateList(); 5.3.7 Publicação dos certificados A CA deve possuir a capacidade de enviar os certificados ao Repositório de Certificados, onde serão disponibilizados a quem necessitar um cópia. Relativamente à implementação desta funcionalidade em Java, o que pode ser feito é, por exemplo, uma aplicação cliente servidor, em que o cliente (implementado na CA) envie o certificado, depois da sua emissão, ao servidor (Repositório de certificados) para que seja publicado. 5.3.8 Revogar certificados (Adicionar à CRL) A CA deve poder revogar certificados. Para isso será necessário adicionar o certificado à Lista de Certificados Revogados (CRL) e tornar o estado do certificado para certificado revogado no repositório. No JDK não existem classes para criar, ou manipular CRLs. As classe que existem apenas permitem obter informação (ler) o conteúdo das CRLs. Mais uma vez, existem duas alternativas, ou criamos classes que permitam manipular CRLs a partir da notação ASN.1, ou então usamos um framework que já as implemente. Por exemplo no JCSI, existe uma classe chamada X509CRLGen que permite criar uma CRL. Vejamos como é que isso pode ser feito: X509CRLGen crlGen = new X509CRLGen(caSignature, caCert); cg.setThisUpdate(new Date()); Calendar c = Calendar.getInstance(); c.set(2002,5,21); cg.setNextUpdate(c.getTime()); cg.setCRLNumber(BigInteger.valueOf((long)635)); Agora vejamos como podem ser adicionados certificados à lista. Para isso basta invocar o método addRevokedCert com o número de série do certificado revogado: cg.addRevokedCert(BigInteger.valueOf((long)35467)); cg.addRevokedCert(BigInteger.valueOf((long)4587467)); 32 Para obtermos um objecto com a lista basta fazer: X509CRL crl = crlGen.getCRL(); 5.3.9 Publicar CRL A CA deve enviar a CRL para o Repositório de certificados para que seja disponibilizada a quem necessitar uma cópia. A CA assina a CRL para que os receptores saibam que se trata de uma CRL válida. Dependendo de uma determinada política, as CRLs podem ser enviadas periodicamente ou quando acontece uma revogação. Se optar-mos pelo envio das CRLs cada vez que um certificado seja revogado o procedimento poderá ser semelhante à publicação de certificados, isto é, basta construir uma aplicação do tipo cliente servidor, em que o servidor corre no Repositório de certificados e o cliente na CA. Depois de um certificado ser revogado, a CRL é enviada ao servidor para que seja publicada. 5.3.10 Pedido de Certificação Cruzada A CA deve poder enviar pedidos a outras CAs pedindo os seus certificados, para que por exemplo, pedidos assinados pela CA possam ser aceites por outra CA remota. Ainda não existem normas para este tipo de funcionalidades. As implementações limitam-se a fazer certificação cruzada dentro de uma solução PKI.. Como ainda não existem normas também não existem implementações desta funcionalidade em frameworks de Java. 5.3.11 Aceitar Certificação Cruzada A CA deve poder responder a pedidos de certificação cruzada, isto é, deve responder a pedidos de certificação de outras CAs indicando que irão aceitar os seus certificados. Da mesma forma que os pedidos de certificação cruzada não existem ainda normas para esta funcionalidade, consequentemente a forma de implementação é distinta para cada solução. 33 5.4 Repositório de Certificados O Repositório de Certificados consiste num serviço de directório que permite que os certificados e as CRLs sejam devolvidas em função de pedidos. Este componente é por vezes implementado como parte de um serviço de directório de propósito geral como por exemplo o LDAP. No entanto pode também ser um serviço especifico da solução PKI e estar até inserido no componente de Autoridade de Certificação. Para aceder a um serviço de directório LDAP, existe uma API incluída no JDK 1.3 que se chama JNDI. O JNDI não só permite o acesso a serviços de directório LDAP como também a outros tipos de serviços e protocolos como por exemplo o DNS, NDS, NIS e X500. Para implementar um serviço de directório LDAP pode usar-se o OpenLDAP ou se se optar por uma implementação em Java existe o JavaLDAP. Ambos são de domínio público. O servidor JavaLDAP funciona como motor do protocolo LDAP e disponibiliza acesso a informação de várias fontes a um qualquer cliente LDAP. 5.4.1 Armazenar certificados e CRLs O repositório de Certificados tem de possibilitar que os certificados e as CRLs possam ser armazenados. Se usar-mos um serviço de directório terá de se usar uma API para publicar os certificados, caso seja usada uma base de dados poderemos criar uma aplicação que através de JDBC permita o acesso a essa base de dados para guardar os certificados as CRLs e outra informação que possa ser útil. 5.4.2 Disponibilizar Certificados e CRLs O repositório de Certificados tem de receber pedidos para pesquisar um determinado certificado ou CRL e disponibilizar uma cópia. Como já foi dito, podem adoptar-se duas abordagens. Ou se utiliza, e é recomendado que assim seja, um serviço de directório, ou o JDBC para aceder a uma Base de Dados onde se encontram guardados os Certificados e as CRLs. Depois de encontrado o registo relativo ao pedido, deve ser disponibilizada um cópia. 5.4.3 Verificar o estado do Certificado O repositório de Certificados tem de disponibilizar a capacidade de verificar o estado de um certificado. Isto pode ser feito como parte da funcionalidade de disponibilizar o certificado, isto é, o estado está contido no certificado quando este é disponibilizado, ou então pode ser uma função separada em que apenas retorna uma flag indicadora do estado em vez de todo o certificado. 34 Algumas norma estão a ser desenvolvidas para esta funcionalidade. O Online Certificate Status Protocol (OCSP) é uma dessas norma. A existência de uma norma que permita verificar o estado de um certificado sem que seja necessário obter o certificado completo, tem grandes vantagens, nomeadamente a redução da largura de banda requerida. 5.5 Aplicações ou Utilizadores As aplicações e os utilizadores que usam certificados para interagir com os subscritores que os possuem, utilizam esses certificados para obter serviços de segurança de acordo com o propósito desses certificados. Fala-se em aplicações ou utilizadores porque ambos podem obter esses serviços de segurança. Um exemplo de uma aplicação é um servidor Web e um exemplo de um utilizador humano pode ser utilizador de correio electrónico de forma segura. Estas entidades são normalmente designadas por “entidades que confiam” (relying entities) pois confiam na representação da identidade do subscritor por parte dos certificados. 5.5.1 Receber certificados A aplicação deve receber certificados de subscritores. O subscritor pode apresentar o certificado para autenticar a sua identidade ou para que a informação lhe seja enviada de forma segura (cifrada). Esta funcionalidade não é tecnicamente necessária. A aplicação poderá alternativamente obter o certificado do subscritor a partir do Repositório de Certificados. No entanto existem protocolos (SSL, S/MIME) que permitem uma troca directa de certificados entre o subscritor e a aplicação (5.1.3). Caso o certificado seja usado para autenticação, deve ser acompanhado com um teste de posse do certificado que confirma que o subscritor realmente possui o certificado. A forma de implementação é semelhante a 5.2.6. 5.5.2 Pedir Certificados A aplicação deve poder elaborar pedidos de certificados ao Repositório de Certificados. Esta função é necessária para que a aplicação possa cifrar as mensagens com a chave pública do subscritor para que depois as mensagens lhe sejam envidadas de forma segura. Esta função pode ser considerada um cliente da função descrita em 5.4.2. 5.5.3 Validar Certificado A aplicação deve poder validar certificados. Para isso existem duas alternativas: Os se consulta a lista de certificados revogados contactando o Repositório de Certificados, podendo concluir se esse certificado continua válido; ou alternativamente usando um protocolo como por exemplo o 35 OCSP, que interroga o Repositório de Certificados ou a CA sobre a validade de um determinado certificado. Esta função está interligada com a função descrita em 5.4.3. 5.5.4 Verificar identidades e Assinaturas Dependendo nos propósitos dos certificados e das necessidades da aplicação, esta pode ter a capacidade de usar o certificado para identificar a identidade do subscritor. A identidade do subscritor pode ser verificada através da sua assinatura. Para se implementar esta funcionalidade usando Java podem seguir-se os seguintes passos: Obter a chave pública do subscritor e criar um objecto de assinatura; CertificateFactory cf = CertificateFactory.getInstance("X.509"); FileInputStream fis = new FileInputStream("NomeDoFicheiro"); X509Certificate certx509 = (X509Certificate) cf.generateCertificate(fis); PublicKey certx509.getPublicKey(); Assinar a mensagem e finalmente verificar a assinatura (como em 5.2.6). 5.5.5 Cifrar com o Certificado Dependendo nos propósitos dos certificados e das necessidades da aplicação, esta pode ter a capacidade de usar o certificado para cifrar informação que apenas o subscritor possa ler. Para isso a informação de ser cifrada com a chave publica do subscritor. Em algumas implementações, utiliza-se criptografia de chave secreta em conjunto com chave pública. É feito da seguinte forma: A chave pública do subscritor é usada para cifrar uma chave de sessão que consiste numa chave secreta que é gerada pela aplicação. O corpo da mensagem é então cifrado com essa chave. O subscritor deve usar a sua chave secreta para decifrar a chave de sessão de finalmente usar essa chave para decifrar o resto da mensagem. Este método é usado fundamentalmente por causa do desempenho, isto é, os algoritmos de chave secreta são bastante mais rápidos que os de chave publica. Este método é designado de envelope digital e é usado pelo PKCS#7. O JCSI contém um package (com.dstc.security.cms )que manipula este protocolo. Se for desejado implementar este protocolo em java é necessário construir classes baseadas na notação ASN.1 tal como está na especificação PKCS#7. Para implementar esta função usando o JCSI basta usar a classe CMSCipher. Esta classe disponibiliza um método de iniciação initEncrypt, que recebe um vector com os certificados dos receptores da mensagem e o algoritmo de cifragem de chave secreta (DESede, RC2, RC2/40). Depois de criado e iniciado o objecto de cifragem as mensagens podem ser passadas ao objecto invocando o método setDataToBeEncrypted que recebe a mensagem sob a forma de um array de bytes. Finalmente para cifrar a mensagem e envia-la a um stream usa-se o método encrypt que envia a mensagem cifrada sob a forma ASN.1 BER.. 36 6. Conclusão Para implementar uma PKI em Java, existe um número considerável de ferramentas ou APIs disponíveis. Algumas são de domínio público, outras possuem licenças gratuitas para fins educativos, e outras possuem licenças puramente comerciais, embora disponibilizem licenças para avaliação. Normalmente as APIs de domínio público apenas implementam um security provider compatível com o JCE da Sun. Se for desejado implementar uma PKI usando apenas ferramentas de domínio público, terão de ser implementadas “de raiz” algumas funcionalidades, nomeadamente as especificações PKCS e a notação ASN.1. Algumas ferramentas que disponibilizam licenças para fins educacionais, por exemplo o JCSI, já possuem funcionalidades que ultrapassam a mera implementação de algoritmos criptográficos, disponibilizando packages com classes que permitem a implementação das principais funcionalidades de uma PKI.. No caso do JCSI, existe a possibilidade de ser usado para fins comerciais, contactando a DSTC. As ferramentas puramente comerciais são obviamente as mais completas, no entanto, por serem puramente comerciais, decidi não inclui-las neste trabalho. Normalmente estas ferramentas são desenvolvidas pelas principais Autoridades de Certificação existentes. Para além do Java existem outras opções de implementação para os componentes da PKI. É comum existirem implementações de algumas funções da RA ou da CA em servidores Web através de módulos de SSL, CGIs, Servlets, Perl, etc... Para a comunicação segura através de sockets, existe o OpenSSL que é de domínio público. Nas aplicações, as linguagens de programação usadas na implementação são sempre bastante diversas. O que tem realmente de ser respeitado, são as especificações existentes para as trocas de certificados com o Repositório de Certificados ou com a CA. Considero que utilizar a linguagem Java para o desenvolvimento de uma PKI é uma boa opção, porque: em primeiro lugar se beneficia das vantagens do Java, nomeadamente a independência relativamente à plataforma (*NIX, Microsoft, Mac, etc...) ; em segundo lugar, o facto de já existirem com vimos anteriormente soluções para cada um dos componentes da PKI, inclusivamente para as comunicações seguras ou mesmo para serviços de directório (LDAP). Para terminar, creio que este trabalho pode ser uma ferramenta interessante para apoiar o desenvolvimento de PKIs, fundamentalmente como orientação na identificação dos componentes e das funcionalidades obrigatórias e opcionais da PKI, assim como na orientação no desenvolvimento dessas funcionalidades através dos exemplos ou das referências às classes e métodos de APIs que as implemente. Janeiro de 2001, Pedro Miguel Félix Alípio 37 7. Referências Implementação de um CA de domínio público http://www.OpenCA.org/ APIs Criptográficos http://java.sun.com/security/JCE1.2/earlyaccess/apidoc/ http://java.sun.com/products/jdk/1.2/docs/api/ (JCA) http://www.dstc.edu.au (JCSI) http://www.bouncycastle.org/ http://www.virtualunlimited.com/products/beecrypt/ http://xsi.unil.ch/java/IAIK/Readme.html http://www.eracom.com.au/ http://www.protekt.com/ http://www.freestylesoft.com/products/crypto/avalanche.html 38 PKI em geral http://www.rsa.com/rsalabs/pubs/PKCS/ http://www.netscape.com/eng/security/certs.html (Certificados Netscape) http://www.infoseceng.com/corppki.htm http://www1.umn.edu/oit/arch/sec-strat/essrprt.htm http://www.entrust.org/resourcecenter/docs/protocols_pki.htm http://conferences.oreilly.com/java/news/java_pki_0200.html http://www.tristone.com/pki/pki2.html http://www.baltimore.com http://www.rsasecurity.com http://www.certicom.com http://www.verisign.com http://www.phaos.com 39