MouseOver Studio

MouseOver Studio header image 2

Comunicação web criptografada e segura com Bouncy Castle e RSA

abril 12th, 2008 por Diego Carrion · 8 comentários

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!

Tags: Api · criptografia · java · JavaScript · livrarias · programação · seguridade

8 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 // dez 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 // dez 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 // mai 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

  • 7 Ricardo // ago 25, 2011 at 3:21 pm

    Se é zica hein…

    vlw…. muito bom o tutorial.

  • 8 Khalil Pereira // set 21, 2011 at 8:32 pm

    Ok, garantimos a segurança enviando as informações do cliente para o Servidor.

    Mas como realizar o sentido inverso?
    segurança do Servidor para o Cliente?

    Criptografar a mensagem no servidor e descriptografar no cliente, por exemplo o servidor vai enviar uma informação importante.

Deixar um comentário