Criptografia rsa em java

dudes…parabens pelo site em primeiro lugar!
To fazendo um tcc na area de criptografia, e preciso implementar um software. O sistema que eu escolhi foi o rsa, mas nao manjo quase nada de programação. Alguem pode me indicar um site que eu ache um algoritmo pronto para o netbeans?? Obrigado!!

Pesquise aqui mesmo no fórum que já tem exemplos…

Não use criptografia RSA para criptografar mais que 100 bytes.
Normalmente RSA é usado para criptografar chaves, não dados.
Eis um exemplo (grave este programa em um arquivo chamado "ExemploCriptografia.java", compile-o e o rode com:
java -cp . ExemploCriptografia
)

import java.io.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.security.cert.*;

class Cifrador {
    public byte[][] cifra (PublicKey pub, byte[] textoClaro) throws NoSuchAlgorithmException, 
    NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
    BadPaddingException, InvalidAlgorithmParameterException {
        byte[] textoCifrado = null;
        byte[] chaveCifrada = null;
        
        //-- A) Gerando uma chave simétrica de 128 bits
        KeyGenerator kg = KeyGenerator.getInstance ("AES");
        kg.init (128);
        SecretKey sk = kg.generateKey ();
        byte[] chave = sk.getEncoded();
        //-- B) Cifrando o texto com a chave simétrica gerada
        Cipher aescf = Cipher.getInstance ("AES/CBC/PKCS5Padding");
        IvParameterSpec ivspec = new IvParameterSpec (new byte[16]);
        aescf.init (Cipher.ENCRYPT_MODE, new SecretKeySpec (chave, "AES"), ivspec);
        textoCifrado = aescf.doFinal (textoClaro);
        //-- C) Cifrando a chave com a chave pública
        Cipher rsacf = Cipher.getInstance ("RSA");
        rsacf.init (Cipher.ENCRYPT_MODE, pub);
        chaveCifrada = rsacf.doFinal (chave);
        
        return new byte[][] { textoCifrado, chaveCifrada };
    }
}

class Decifrador {
    public byte[] decifra (PrivateKey pvk, byte[] textoCifrado, byte[] chaveCifrada) throws NoSuchAlgorithmException, 
    NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
    BadPaddingException, InvalidAlgorithmParameterException  {
        byte[] textoDecifrado = null;
        
        //-- A) Decifrando a chave simétrica com a chave privada
        Cipher rsacf = Cipher.getInstance ("RSA");
        rsacf.init (Cipher.DECRYPT_MODE, pvk);
        byte[] chaveDecifrada = rsacf.doFinal (chaveCifrada);
        //-- B) Decifrando o texto com a chave simétrica decifrada
        Cipher aescf = Cipher.getInstance ("AES/CBC/PKCS5Padding");
        IvParameterSpec ivspec = new IvParameterSpec (new byte[16]);
        aescf.init (Cipher.DECRYPT_MODE, new SecretKeySpec (chaveDecifrada, "AES"), ivspec);
        textoDecifrado = aescf.doFinal (textoCifrado);
        
        return textoDecifrado;
    }
}

class CarregadorChavePublica {
    public PublicKey carregaChavePublica (File fPub) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream (new FileInputStream (fPub));
        PublicKey ret = (PublicKey) ois.readObject();
        ois.close();
        return ret;
    }
}

class CarregadorChavePrivada {
    public PrivateKey carregaChavePrivada (File fPvk) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream (new FileInputStream (fPvk));
        PrivateKey ret = (PrivateKey) ois.readObject();
        ois.close();
        return ret;
    }
}


class GeradorParChaves {
    private static final int RSAKEYSIZE = 1024;
    
    public void geraParChaves(File fPub, File fPvk) 
        throws IOException, NoSuchAlgorithmException, 
        InvalidAlgorithmParameterException, 
        CertificateException, KeyStoreException {
            
        KeyPairGenerator kpg = KeyPairGenerator.getInstance ("RSA");
        kpg.initialize (new RSAKeyGenParameterSpec(RSAKEYSIZE, RSAKeyGenParameterSpec.F4));
        KeyPair kpr = kpg.generateKeyPair ();
        PrivateKey priv = kpr.getPrivate();        
        PublicKey pub = kpr.getPublic();
        
        //-- Gravando a chave pública em formato serializado
        ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream (fPub));
        oos.writeObject (pub);
        oos.close();
        
        //-- Gravando a chave privada em formato serializado
        //-- Não é a melhor forma (deveria ser guardada em um keystore, e protegida por senha), 
        //-- mas isto é só um exemplo
        oos = new ObjectOutputStream (new FileOutputStream (fPvk));
        oos.writeObject (priv);
        oos.close();
        
    }
}

class ExemploCriptografia {
    static void printHex(byte[] b) {
        if (b == null) {
            System.out.println ("(null)");
        } else {
        for (int i = 0; i &lt b.length; ++i) {
            if (i % 16 == 0) {
                System.out.print (Integer.toHexString ((i & 0xFFFF) | 0x10000).substring(1,5) + " - ");
            }
            System.out.print (Integer.toHexString((b[i]&0xFF) | 0x100).substring(1,3) + " ");
            if (i % 16 == 15 || i == b.length - 1)
            {
                int j;
                for (j = 16 - i % 16; j &gt 1; --j)
                    System.out.print ("   ");
                System.out.print (" - ");
                int start = (i / 16) * 16;
                int end = (b.length &lt i + 1) ? b.length : (i + 1);
                for (j = start; j &lt end; ++j)
                    if (b[j] &gt= 32 && b[j] &lt= 126)
                        System.out.print ((char)b[j]);
                    else
                        System.out.print (".");
                System.out.println ();
            }
        }
        System.out.println();
        }
    }    
    
    public static void main(String[] args) throws Exception {
        //-- Gera o par de chaves, em dois arquivos (chave.publica e chave.privada)
        GeradorParChaves gpc = new GeradorParChaves();
        gpc.geraParChaves (new File ("chave.publica"), new File ("chave.privada"));

        //-- Cifrando a mensagem "Hello, world!"
        byte[] textoClaro = "Hello, world!".getBytes("ISO-8859-1");
        CarregadorChavePublica ccp = new CarregadorChavePublica();
        PublicKey pub = ccp.carregaChavePublica (new File ("chave.publica"));
        Cifrador cf = new Cifrador();
        byte[][] cifrado = cf.cifra (pub, textoClaro);
        printHex (cifrado[0]);
        printHex (cifrado[1]);
        
        //-- Decifrando a mensagem 
        CarregadorChavePrivada ccpv = new CarregadorChavePrivada();
        PrivateKey pvk = ccpv.carregaChavePrivada (new File ("chave.privada"));
        Decifrador dcf = new Decifrador();
        byte[] decifrado = dcf.decifra (pvk, cifrado[0], cifrado[1]);
        System.out.println (new String (textoClaro, "ISO-8859-1"));
    }
}
        
1 curtida

putz…valeu ai galera, mas acho que em breve vou precisar de mais ajuda. Abraços

thingol,

Estou estudando um pouco de Criptografia, pois terei de usá-lo para criptografar e descriptografar uma base de dados, e nos exemplos que encontrei na net, o seu foi o mais claro e eficiente q encontrei, parabéns…

Porém gostaria de saber se vc pode me ajudar… o código funciona perfeitamente, porém qdo eu separo as classes e vou utilizar o “Decifrador” separadamente do “Cifrador” para descriptografar um txt previamente criptografado com este código, este dá o seguinte erro:

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
	at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
	at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
	at javax.crypto.Cipher.doFinal(DashoA13*..)
	at Decifrador.decifra(Decifrador.java:31)
	at Decifrador.main(Decifrador.java:85)

Para descriptografar este arquivo, eu mudei a linha 151 do seu código, ficando da seguinte forma:

byte[] decifrado = dcf.decifra(pvk, textoClaro, pub.getEncoded());

Visto que eu não quero chamar a criptografia novamente, antes de descriptografar…

Você pode me orientar para saber oq está ocorrendo?

Desde já agradeço.

1 curtida

Uai, não sei onde é que pode dar esse erro. Rodei meu programa novamente (mas com um conjunto de dados bem maior) e ele funciona corretamente. Qual foi a modificação que você fez no meu código?

Uai, como é que você vai decifrar algo decifrado? Vai dar pau mesmo.

byte[] decifrado = dcf.decifra(pvk, textoClaro, pub.getEncoded());  

Bom, vou separar as variáveis pra ficar mais claro…

Mais na verdade o “textoClaro” é o que está criptografado no arquivo txt.

segue pra ficar amis claro:

File bdOne = new File(System.getProperty("user.dir").concat(
        				"\IF001.fdb"));
        	File bdTwo = new File(System.getProperty("user.dir").concat(
        				"\IF002.fdb"));
        
        	FileInputStream in = new FileInputStream(bdOne);
        
        	InputStreamReader conversor = new InputStreamReader(in);
        
        	BufferedReader bf = new BufferedReader(conversor);
        
        	RandomAccessFile random = new RandomAccessFile(bdTwo, "r");
        
        	int i = 0;
        	
        	while ((linha = bf.readLine()) != null) {
        	    i++;
        	
        		content = linha.split(" ");
        		Long convert = new Long(content[1]);
        
        		random.seek(convert.parseLong(content[1]));
        		random.readLine();
        
        		conteudo = random.readLine();
        		byte[] textoCifrado = conteudo.getBytes();
        
        		CarregadorChavePublica ccp = new CarregadorChavePublica();
        		PublicKey pub = ccp.carregaChavePublica(new File(System.getProperty("user.dir").concat("\chave.publica")));
        		
        		// -- Decifrando a mensagem
        		CarregadorChavePrivada ccpv = new CarregadorChavePrivada();
        		PrivateKey pvk = ccpv.carregaChavePrivada(new File(System.getProperty("user.dir").concat("\chave.privada")));
        		Decifrador dcf = new Decifrador();
        		
			byte[] decifrado = dcf.decifra(pvk, textoCifrado , pub.getEncoded());
        
        		System.out.println(new String(decifrado));
        
        	}

Ah, é que não ficou muito claro no programa que eu mostrei.

A rotina “cifra” retorna 2 arrays de bytes: um deles com os dados cifrados, e outro deles com a chave simétrica (AES) cifrada com a chave assimétrica (RSA).
Quando você vai guardar esses dados, você precisa guardar os 2 arrays, não só um deles. Se você não guardar a chave simétrica cifrada com a chave assimétrica, então você nunca conseguirá decifrar os dados cifrados. Não é perigoso guardar a chave simétrica cifrada, porque ela só poderá ser decifrada por alguém que tiver a chave privada disponível.

Quando você for decifrar os dados, você precisa passar a chave privada (primeiro parâmetro), os dados cifrados (segundo parâmetro), e a chave simétrica cifrada com a chave assimétrica (terceiro parâmetro).

Se você acha isso esquisito, é o jeito correto de fazer, que é usado no padrão PKCS#7 signedData, e no padrão S/MIME (usado para mandar e-mails criptografados pelo MS Outlook, MS Outlook Express, Eudora, Mozilla Thunderbird, etc. )

Obrigada pela paciência e grande ajuda thingol, mais assim, eu estou passando sim os três parâmetros… o estranho é q o programa funciona perfeitamente qdo rodo ele cifrando e decifrando logo em seguida, assim como está seu programa…

Agora se eu separo as classes, cifro um conteúdo e guardo em um arquivo, e depois só rodo o decifrar, ele emite esse erro… porém não entendo, pq eu estou guardando os arquivos com as chaves e lendo as duas, a pública e a privada e tbm leio no arquivo o conteúdo a ser decifrado, porém emite esse erro…

fiz algunst estes e o problema está justamente na linha

[code]
// pvk - chave privada, textoCifrado - conteúdo codificado obtido do arquivo txt, pub.getEncoded() - chave pública

byte[] decifrado = dcf.decifra(pvk, textoCifrado, pub.getEncoded());[/code]

Eu não mantive da forma original, pois este envocava antes a classe para cifrar, e não é oq eu quero, pois este já está no arquivo, eu gostaria apenas de decifrar o conteúdo deste arquivo.

Desde já agradeço novamente… :smiley:

O primeiro parâmetro é a chave privada (ok)
O segundo é o texto cifrado (ok)
O terceiro não é a chave pública e sim a chave simétrica cifrada. (No programa acima era o elemento cifrado[1] do array de arrays retornado por “cifra”.)

Sim, exatamente isso, mais o “Decifra” não tem a função de tbm pegar essa chave pública criptografada, e descriptografar esta chave e com ela, tbm decifrar o texto?

Eu não queria ter de chamar o cifra, pois eu quero apenas decifrar…chamando o cifra para obter a chave, eu criptografo novamente um conteúdo q já está criptografado, e não é isso q eu quero…

O problema é que o Decifra recebe a chave pública como um array de bytes e não como um objeto PublicKey, com isso, eu não consigo transformar este objeto PublicKey em um array de bytes, caso conseguisse fazer isso, creio q resolveria meu problema.

como poderia fazer para utilizar somente o decifra, utilizando uma chave pública previamente criada e armazenada em um arquivo? Já repliquei o método cifra e tirei a parte de cifrar o texto e deixei a parte da chave para tentar utilizá-la separadamente, já tentei transformar a chave em um array de bytes, já tentei um pouco de tudo e obti diversos erros, não sei mais oq posso fazer… conto com a sua ajuda… :frowning:

Agradeço a disposição em ajudar… Obrigada :smiley:

Existem poucos tópicos aquí a respeito de criptografia, e este está off, mais será que ninguém mais pode me ajudar? :cry:

Calma, vou explicar tudo de novo. Pegue a sua cadeirinha e sente-se.

Normalmente, quando se quer cifrar uma grande quantidade de dados (ou seja, mais de 100 bytes), usa-se sempre criptografia normal (ou simétrica, onde a mesma chave que serve para cifrar um dado serve para decifrá-la. Ou seja, é uma chave “secreta” ou “simétrica”).
As vantagens da criptografia simétrica são:

  • Muito mais rápida
  • Segura para pequenas ou grandes quantidades de dados.
    O algoritmo recomendado hoje em dia é o AES ou Rijndael (com força de 128, 192 ou 256 bits); usei no exemplo o AES com chave de 128 bits.
    Entretanto, para você mandar um dado criptografado com criptografia simétrica, é necessário que ambas as partes (remetente e destinatário) conheçam a chave simétrica. Você deve saber muito bem que é difícil mandar essa chave a menos que você pessoalmente apareça com um envelope e o dê nas mãos da pessoa (é modo de dizer); você não pode mandá-la pelo telefone, e muito menos via sockets.
    A criptografia assimétrica veio para ajudar a resolver esse problema. Se você conhecer a chave PÚBLICA do destinatário, você pode mandar um dado (com menos de 100 bytes) para ele, criptografado com a chave PÚBLICA, de modo que o destinatário use a chave PRIVADA para decifrá-la.
    Dados com mais bytes não devem ser criptografados com criptografia assimétrica (por exemplo, RSA), porque isso expõe uma vulnerabilidade nesses algoritmos que só se manifesta quando a quantidade de dados é grande e os dados criptografados podem ser conhecidos. Por exemplo, se você criptografasse uma mensagem - como uma página HTML - integralmente com o algoritmo RSA, ela tem alguns valores conhecidos em vários pontos, como o começo, que normalmente é “”.
    E como o meu programa faz isso? Ele gera uma chave simétrica, que será usada para criptografar os dados a serem enviados. O remetente tem de mandar para o destinatário:
  • Os dados criptografados com a chave simétrica gerada pelo remetente (algoritmo AES)
  • A chave simétrica criptografada com a chave pública do destinatário (algoritmo RSA)
    O destinatário, para decifrar a mensagem, deve obter:
  • Os dados criptografados com a chave simétrica gerada pelo remetente (algoritmo AES)
  • A chave simétrica criptografada (algoritmo RSA)
    O que ele tem de fazer com os dados recebidos:
  • Tem de decifrar a chave simétrica criptografada com a chave privada do destinatário (usando o algoritmo RSA)
  • Uma vez decifrada essa chave simétrica, deve usá-la para decifrar os dados recebidos (usando o algoritmo AES)
    Note que nesse processo o destinatário NÃO PRECISA da própria chave pública. O destinatário só precisa da CHAVE PRIVADA.

Obrigada pela paciência thingol… muitas coisas eu já havia entendido, e algumas outras ficaram mais claras para mim…

Vamos lá:

[quote]O remetente tem de mandar para o destinatário:

  • Os dados criptografados com a chave simétrica gerada pelo remetente (algoritmo AES)
  • A chave simétrica criptografada com a chave pública do destinatário (algoritmo RSA) [/quote]

Ok, isso eu entendi… que em código seria:

// -- B) Cifrando o texto com a chave simétrica gerada
		Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
		IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
		aescf.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(chave, "AES"), ivspec);
		textoCifrado = aescf.doFinal(textoClaro);

// -- C) Cifrando a chave com a chave pública
		Cipher rsacf = Cipher.getInstance("RSA");
		rsacf.init(Cipher.ENCRYPT_MODE, pub);
		chaveCifrada = rsacf.doFinal(chave);[/code]

[quote] O destinatário, para decifrar a mensagem, deve obter:
- Os dados criptografados com a chave simétrica gerada pelo remetente (algoritmo AES)
- A chave simétrica criptografada (algoritmo RSA) [/quote]

que em código:

[code]return new byte[][] { textoCifrado, chaveCifrada };[/code]


[quote] O que ele tem de fazer com os dados recebidos:
- Tem de decifrar a chave simétrica criptografada com a chave privada do destinatário (usando o algoritmo RSA)
- Uma vez decifrada essa chave simétrica, deve usá-la para decifrar os dados recebidos (usando o algoritmo AES) [/quote]


Certo, que isto seria o papel do Decifrar

[code]
// -- A) Decifrando a chave simétrica com a chave privada
		Cipher rsacf = Cipher.getInstance("RSA");
		rsacf.init(Cipher.DECRYPT_MODE, pvk);
		byte[] chaveDecifrada = rsacf.doFinal(pub);
		
// -- B) Decifrando o texto com a chave simétrica decifrada
		Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
		IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
		aescf.init(Cipher.DECRYPT_MODE, new SecretKeySpec(chaveDecifrada, "AES"), ivspec);
		textoDecifrado = aescf.doFinal(textoCifrado);
[/code]

[quote] Note que nesse processo o destinatário NÃO PRECISA da própria chave pública. O destinatário só precisa da CHAVE PRIVADA. [/quote]

Se ele não precisa da chave pública, o que seria o [b]pub[/b] na linha 4 [code]byte[] chaveDecifrada = rsacf.doFinal(pub);[/code] do código acima... ? 

Pois, para decifrar, vc passa os seguintes parâmetros:

[code]byte[] textoCifrado = conteudo.getBytes();
        
        		CarregadorChavePublica ccp = new CarregadorChavePublica();
			PublicKey pub = ccp.carregaChavePublica(new File(System.getProperty("user.dir").concat("\chave.publica")));
			Cifrador cf = new Cifrador();
//////////////////////////////////////////////////////////////////////////
			byte[][] cifrado = cf.cifra(pub, textoCifrado);
//////////////////////////////////////////////////////////////////////////
        		
        		// -- Decifrando a mensagem
        		CarregadorChavePrivada ccpv = new CarregadorChavePrivada();
        		PrivateKey pvk = ccpv.carregaChavePrivada(new File(System.getProperty("user.dir").concat("\chave.privada")));
        		
        		Decifrador dcf = new Decifrador();
        
//////////////////////////////////////////////////////////////////////////////////////
        		byte[] decifrado = dcf.decifra(pvk, cifrado[0], cifrado[1]);
///////////////////////////////////////////////////////////////////////////////////////

Onde nas linhas destacadas pvk = chave privada, cifra[0] = textoCifrado e cifrado[1] = pub, que seria a chave Pública que é obtida seu eu chamo o cifra, sendo q eu quero apenas decifrar a mensagem e não cifrá-la novamente para depois decifrá-la.

Porém, partindo do princício, se eu modificar a linha destacada do código acima, da seguinte forma:

byte[] decifrado = dcf.decifra(pvk, textoCifrado, pub);

Não é possível fazer de tal forma pq o pub é um objeto tipo PublicKey e o decifra espera um parâmetro array de bytes

Este está sendo o meu problema… eu não sei se estou sendo clara…

Peço desculpas caso eu tenha feito muita confusão, mas o conceito de criptografar e descriptografar creio que entendi, porém não sei ao certo então o que seria a variável pub no meio de tudo isso senão a chave pública. :roll:

Obrigada pela ótima e detalhada explicação thingol, porém ainda estou um pouco confusa, como tentei explicar acima… :oops:

Abraços… :slight_smile:

byte[][] cifrado = cf.cifra (pub, textoClaro);  
...
byte[] decifrado = dcf.decifra (pvk, cifrado[0], cifrado[1]);  

Tá bom. Em cf.cifra, que deve ser chamada pelo remetente, eu pego o texto claro, a chave pública do destinatário, e:

  • Gero uma chave simétrica AES e a uso para criptografar o “textoClaro”
  • Criptografo a chave simétrica AES com a chave pública RSA
  • Retorno um array com 2 arrays de bytes. O primeiro array de bytes contém o textoClaro criptografado com a chave simétrica AES. O segundo array de bytes contém a chave simétrica AES criptografada com a chave pública RSA.

Em dcf.decifra, que deve ser chamada pelo destinatário, eu pego a chave privada do destinatário, o textoClaro criptografado com a chave simétrica AES, e a chave simétrica AES criptografada com a chave pública RSA. Aí eu faço o tal do processo inverso.

Perguntinha - pegue o programa original, e separe direito dessa vez em pedaços. Eu vi que você fez isto aqui:

 byte[] chaveDecifrada = rsacf.doFinal(pub);  

Mas eu não fiz isso aí de jeito nenhum. De onde você tirou isso?

thingol realmente esta linha estava diferente do seu código… nas diversas mudanças, devo ter mudado isso sem querer… :oops: sorry… por isso a minha confusão…

Mais de qualquer forma, como eu mesmo havia falado, o conceito e o funcionamento do seu código eu entendi… a minha dúvida é como vou passar a chave simétrica criptografada para o Decifra sem chamar o Cifra, pois eu quero somente descriptografar… sendo que se eu duplico o método cifra e retiro apenas a parte do texto e retorno apenas a chave, ocorre uma mensagem de erro…

:shock:

É que é o seguinte: experimente rodar o tal código diversas vezes com os mesmos dados de entrada.

Você vai ver que a tal chave criptografada MUDA SEMPRE, e isso faz com que os dados criptografados MUDEM SEMPRE.

Portanto é necessário mandar do remetente para o destinatário os dados criptografados e a chave criptografada.

Não dá para mandar só os dados ou só a chave - os dados e a chave são “casados”.

No PKCS#7 envelopedData, que é o formato-padrão para mandar dados criptografados usando-se, por exemplo, RSA + AES, tanto os dados quanto a chave são sempre enviados.

Certo, as chaves são criadas de forma randômica, mais então não teria como eu utilizar a mesma chave criada anteriormente, com o mesmo texto criptografado só para descriptografar?

Seria então “obrigatório” eu chamar o cifra para decifrar? isso acaba criptografando algo q já foi criptografado anteriormente e jogado no banco…

Portanto eu só consigo utilizar o Decifra, caso eu esteja criptografando o conteúdo q eu quero descriptografar simultaneamente, é isso? :shock: