Na empresa onde atualmente trabalho vou começar a trabalhar num projeto onde a seguridade é muito importante, pelo que fui instruído para aprender sobre Bouncy Castle. Nunca tinha ouvido falar aquelas palavras juntas, então vou começar do zero.
O que é Bouncy Castle?
Depois de acessar a página do Bouncy Castle, a definição no Wikipedia e a definição no Java Glossary me atrevo a dizer que Bounce Castle é uma API de criptografia para Java e também um provedor das especificações JCE e JCA da Sun.
Algo curioso sobre Bouncy Castle é que por ser um projeto australiano ele não precisa cumprir as leis de Estados Unidos que proíbem a exportação de algoritmos fortes de criptografia. Mais informação aqui.
O que é RSA?
De acordo com a Wikipedia, RSA é
um algoritmo de encriptação de dados, (…) até à data (2008), a mais bem sucedida implementação de sistemas de chaves assimétricas, e fundamenta-se em Teorias Clássicas dos Números. É considerado dos mais seguros, já que mandou por terra todas as tentativas de quebrá-lo. Foi também o primeiro algoritmo a possibilitar encriptação e assinatura digital, e uma das grandes inovações em criptografia de chave pública.
RSA é um algoritmo que cria duas chaves, uma delas sendo pública e a outra privada. Toda mensagem que for cifrada utilizando a chave pública somente vai poder ser decifrada utilizando a chave privada.
Trabalhando com RSA
Vamos por as mãos na massa e brincar um pouco. Construiremos uma aplicação web simples que utilizando RSA crie uma chave privada e mande uma pública ao cliente. A aplicação servidor depois ira decifrar mensagens que foram cifradas com a chave publica. Para cifrar a chave pública na aplicação cliente utilizaremos a biblioetaca jsbn, dos estudandes de Stanford, implementada em JavaScript.
Começarei criando uma aplicação web dinâmica no Eclipse que rodara sobre o Tomcat e adicionarei ao meu pacote uma classe serviço que fará as operações relacionadas com Bouncy Castle e RSA e uma servlet que delegara as chamadas HTTP para o serviço correspondente.
Vou começar a implementar o serviço de RSA que utilizara Bouncy Castle como implementação, pelo que terei que incluir a livraria correspondente no meu classpath. As livrarias podem ser descarregadas de aqui. Nesse caso somente precisaremos da livraria Provider e OpenPGP/BCPG.
O primeiro passo ao trabalhar com Bouncy Castle é adicionar o provedor Bouncy Castle à lista de provedores. Colocarei o seguinte código no construtor da minha classe serviço:
Security.addProvider(new BouncyCastleProvider());
Agora vou criar um gerador de chaves pares para o algoritmo RSA utilizando como provedor o Bouncy Castle (BC) e depois inicializar ele especificando que o tamanho das chaves vai ser 1024 e que o provedor de números aleatórios vai ser SecureRandom, do pacote java.security. Essas operações irão estar num método chamado generateKeys que terminara devolvendo um par de chaves. O método fica assim:
public KeyPair generateKeys() throws Exception {
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA",
"BC");
keyGenerator.initialize(1024, new SecureRandom());
return keyGenerator.generateKeyPair();
}
O segundo método da classe serviço recebera uma mensagem cifrada junto com a chave privada correspondente à chave pública utilizada para cifrar a mensagem e retornara a mensagem decifrada.
O primeiro passo é pegar uma instância da classe Cipher. A classe Cipher é encarregada de cifrar e de decifrar arrays de bytes. Para pegar uma instância da classe Cipher chamamos o método estático getInstance da mesma. Ela recebe dois parâmetros: a transformação a ser utilizada e o provedor. Já sabemos que o provedor é Bouncy Castle, mas o que é a transformação?
A transformação é o método que foi utilizado para cifrar nossa mensagem. No nosso caso a transformação é RSA/NONE/PKCS1Padding, que define que utilizamos RSA como algoritmo de criptografia, que não utilizamos nenhuma técnica especial para cifragem em bloco e que utilizamos PKCS #1 como técnica de enchimento. Escolhi essa transformação porque é compatível com a livraria JavaScript utilizada.
Talvez agora surgiram mais dúvidas. Técnica para cifragem em bloco? Técnica de enchimento?
Quando ciframos um texto utilizando RSA, partimos ele em vários blocos e cada bloco vai sendo cifrado de alguma maneira. Como os blocos se relacionam e o tamanho deles, entre outras coisas, são características que definem as diferentes técnicas de cifragem em bloco. Para aprender mais podem acessar a definição no site da Wikipedia.
Agora imaginemos que nossos blocos devem ser de 10 bytes mas ao particionar nossa mensagem o último bloco ficou de 4 bytes. Nesse caso temos que fazer alguma operação para completar esses 6 bytes restantes. Essa operação e chamada de padding e existem varias técnicas. Mas detalhes podem ser obtidos novamente na definição no site da Wikipedia.
Uma vez com nossa instância da classe Cipher nosso seguinte passo é inicializar ele indicando que vamos a realizar um processo de descriptografia utilizando tal chave privada. Finalmente realizamos o processo chamando o método doFinal do cipher passando como parámetro os bytes da mensagem criptograda. O método doFinal ira retornar os bytes da mensagem decifrada, os quais utilizaremos para criar uma string e devolver ela como retorno do método, o qual fica assim:
public String decrypt(PrivateKey key, byte[] message) throws Exception {
Cipher decrypt = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
decrypt.init(Cipher.DECRYPT_MODE, key);
return new String(decrypt.doFinal(message));
}
Começarei agora a desenvolver a classe que atuara como servlet. A classe servlet ira ter duas funções. A primeira função e mandar o serviço criar um par de chaves, guardar a chave privada na sessão e mandar os dados necessários da chave pública para o cliente (modulus e exponente). A segunda função é receber um texto em formato hexadecimal, passar ele para uma seqüencia de bytes e chamar o método do serviço encarregado de decifrar a cadeia de bytes. Finalmente devolvera a mensagem decifrada para o usuário.
Como o código da servlet é Java de todos os dias, não tem muito para explicar. Segue o código dos dois métodos encarregados de realizar as operações:
private void decrypt(HttpServletRequest request,
HttpServletResponse response) throws Exception {
KeyPair keys = (KeyPair) request.getSession().getAttribute("keyPair");
PrivateKey privateKey = keys.getPrivate();
RSAService service = new RSAService();
byte[] bytes = Hexadecimal.parseSeq(request.getParameter("Decrypt"));
String decrypted = service.decrypt(privateKey, bytes);
response.getWriter().write(decrypted);
}
private void getPublicKey(HttpServletRequest request,
HttpServletResponse response) throws Exception {
RSAService service = new RSAService();
KeyPair keys = service.generateKeys();
request.getSession().setAttribute("keyPair", keys);
RSAPublicKey publicKey = (RSAPublicKey) keys.getPublic();
String modulus = publicKey.getModulus().toString(16);
String exponent = publicKey.getPublicExponent().toString(16);
PrintWriter writer = response.getWriter();
writer.append("{'modulus':'");
writer.append(modulus);
writer.append("','exponent':'");
writer.append(exponent);
writer.append("'}");
writer.flush();
}
O código cliente é bem simples. Quando a página carregar, a página ira solicitar ao servidor o modulus e o exponente da chave pública. Quando o usuário acionar um botão escolhido, ira ser chamada a função sendMessage. A função sendMessage ira instanciar um objeto do tipo RSAKey que sera o encarregado de cifrar o texto escrito no devido textbox. Apos instanciar um objecto do tipo RSAKey, a função sendMessage especificara o modulus e o exponente da chave pública e logo cifrara a mensagem. Finalmente a função enviara ela pro servidor e mostrara uma alerta com a resposta. Nosso código em JavaScript fica assim:
$(document).ready(function() {
$.getJSON("do?GetPublicKey", function(key) {
$("#exponent").val(key.exponent);
$("#modulus").val(key.modulus);
})
});
function sendMessage() {
var rsa = new RSAKey();
rsa.setPublic($("#modulus").val(), $("#exponent").val());
var message = rsa.encrypt($("#text").val());
$("#encrypted").html(message);
$.get("do?Decrypt=" + message, function(data) {
window.alert(data);
})
}
O código da aplicação, incluíndo o jQuery, a classe Hexadecimal pegada da Internet, a biblioteca jsbn e o web.xml pode ser descarregado de aqui. Não esquecer que devem ser descarregadas ainda as bibliotecas do Bouncy Castle referentes a versão da JVM.
Se tiver dito alguma bobagem, por favor me perdoem e me corrijam nos comentários ou por email.
Ate a próxima!
6 respostas ate agora ↓
1 raphael // Jun 1, 2008 at 11:26 am
caro companheiro, achei muito interessante a questao do sistema rsa… fiz algumas leituras sobre ele onde todas dizem que é a multiplicação de dois numeros primos gigantes… porém, ficaria satisfeito se vc pudesse me passar um exemplo desse num primo gigante que é formado pela multiplicação para ter idéia do que se trata… pois até agora só li que eles são grandes mas nunca vi o exemplo de nenhum…
2 Diego Carrion // Jun 3, 2008 at 12:29 pm
Ola Raphael, a multiplicação dos dois números primos é somente o primeiro passo para criar um par de chaves RSA. O processo completo pode ser visto aqui: http://pt.wikipedia.org/wiki/RSA#Gera.C3.A7.C3.A3o_das_chaves . No link você vai poder apreciar que a chave publica e composta de “n” e “e” e a privada de “n” e “d”. Nesse link http://ohdave.com/rsa/ podem ser geradas um par de chaves em JavaScript, o resultado é impresso na tela.
Outro jeito de mandar chaves publicas RSA e em formato PGP. Mais sobre isso pode ser visto aqui:
http://pt.wikipedia.org/wiki/PGP
3 Vagner Schoaba // Dec 13, 2008 at 2:45 pm
Olá.
O seu material ficou bom.
Eu estou defendendo o meu mestrado segunda-feira, e também trabalhei com o bouncy castle, so que para J2ME.
Parabens pela iniciativa.
abracos
4 Diego Carrion // Dec 13, 2008 at 8:55 pm
Obrigado pelo comentário Vagner, e muita boa sorte com o mestrado.
5 Tiago // Jun 25, 2009 at 10:35 am
Olá!Parabéns pelo artigo! Muito bom mesmo!!!
Você saberia me dizer aonde eu devo alterar o código para que ele aceite cacacteres especiais na mensagem, como ‘ç’, ‘ã’, ‘á’, ‘à’, …
Desde já agradeço!
6 Cácio // May 17, 2010 at 10:45 am
Diego, estou com um probleminha, veja se pode me ajudar.
Recebi uns arquivos para descriptografar, mas está muito lento. Eu abri a chave e dei um toString, e lá informa que ela é de 1024 bits (Sun RSA private CRT key, 1024 bits).
Mas na hora de descriptografar, o cipher não aceitar blocos maiores que 128. O block size dele é 128.
Tem como aumentar o tamanho do bloco para 1024 ou 512? Já que o bloco tem que ser menor que a chave. Se pude me ajudar eu agradeceria muito
Deixar um comentário