Como validar assinatura digital?

Amigos, como posso validar a assinatura digital de um arquivo texto?

O arquivo já está assinado (a última linha do arquivo contém a assinatura digital) e tenho disponível a chave pública.

Como então posso verificar se a assinatura é válida?

Amigos, alguma sugestão?

Trata-se de um arquivo texto cuja última linha contém o registro de assinatura digital (EAD).

A chave pública está disponível. Eu precisaria de mais algum outro dado para validar a assinatura digital?

Há alguma função nativa de java que verifica a assinatura?

Alguém já utilizou as classes java.security.PublicKey e java.security.Signature?

Amigos,

Vi no site da sun que, após instanciar a classe Sinature, é preciso adotar o seguinte procedimento:

  1. Chamar o método initiVerify(), passando como parâmetro um objeto PublicKey (chave pública).
  2. Chamar o método update(), passando como parâmetro um array de bytes com o dado a ser verificado.
  3. Chamar o método verify(), passando como parâmetro um array de bytes com a assinatura. Este método retornará true ou false, conforme a assinatura seja válida ou não.

O que eu preciso inicialmente superar é como obter um objeto publicKey tendo a chave pública em um String. Como?

Parece que Publickey é uma interface. É isso mesmo?

Olá André, tudo bem?

Você conseguiu evoluir nesse caso da assinatura digital? Estou para participar de um projeto e terei que fazer uma verificação parecida com a tua…

Abraço.

[quote=ari_cesar]Olá André, tudo bem?

Você conseguiu evoluir nesse caso da assinatura digital? Estou para participar de um projeto e terei que fazer uma verificação parecida com a tua…

Abraço.[/quote]

Consegui sim (anteontem).
Depois de muito pesquisar na internet (e quase desistir), obtive ajuda de um excelente programador de c++ que me deu o passo-a-passo de como validar a assinatura digital de um arquivo.
No meu caso trata-se de verificar se um determinado arquivo texto encontra-se íntegro, ou seja, saber se ele não foi alterado depois de sua geração.
Primeiramente tive que entender como o arquivo é assinado. Resumidamente é assim (passos executados por quem gera o arquivo, o remetente):

  1. O arquivo é submetido à função unidirecional MD5 e o resultado disso é um código (hash) de 16 bytes.
  2. O hash é criptografado com o algoritmo RSA, utilizando-se a chave privada do remetente.
  3. Então o arquivo é fornecido juntamente o hash criptografado.

Passos executados para verificar a integridade do arquivo (os passos que o meu programa em java precisam executar):

  1. Submeter o arquivo recebido à função unidirecional MD5 e armazenar o resultado (um hash de 16 bytes).
  2. Descriptografar o hash recebido do remetente utilizando-se o algoritmo RSA e a chave pública do remetente.
  3. Comparar o hash do item 1 com o hash descriptogradado no item 2.
  4. Se forem diferentes significará que o arquivo foi alterado depois de sua geração.

Depois de conseguir entender todo o processo e saber os passos que meu programa precisava executar, fiquei emperrado em como transformar uma string com a chave pública do remetente em um objeto PublicKey do java. Nesse ponto fiquei emperrado por vários dias até encontrar o seguinte código na internet:

public void saveToFile(String fileName, BigInteger mod, BigInteger exp) throws IOException { ObjectOutputStream oout = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream(fileName))); try { oout.writeObject(mod); oout.writeObject(exp); } catch (Exception e) { throw new IOException("Unexpected error", e); } finally { oout.close(); } }
Obs.: Para transformar uma string cujo conteúdo representa uma chave pública em hexadecimal, utilizei o new BigInteger(chave, 16), onde chave é a string com o conteúdo da chave.

Tendo a chave gravada em um arquivo publickey.key, por exemplo, os passos seguintes foram:

  1. Obter o hash do arquivo:

MessageDigest md = MessageDigest.getInstance("MD5"); md.update(conteudoArquivo); byte[] novoHash = md.digest();

Obs.: conteudoArquivo é um array de bytes com todo o conteúdo do arquivo.

  1. Obter a chave pública gravada em um arquivo.

InputStream in = new FileInputStream("publickey.key"); ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in)); BigInteger m = (BigInteger) oin.readObject(); BigInteger e = (BigInteger) oin.readObject(); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e); KeyFactory fact = KeyFactory.getInstance("RSA"); PublicKey pubKey = fact.generatePublic(keySpec);

  1. Descritografar o hash recebido junto com o arquivo.

Cipher rsaCipher = null; rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding"); rsaCipher.init(Cipher.DECRYPT_MODE, pubKey); byte[] hashOriginal = rsaCipher.doFinal(assinatura);
Obs.: assinatura é um array de bytes cujo conteúdo representa o hash criptografado.

  1. Comparar o hashOriginal com o novoHash.
    Se coincidir, significará “ASSINATURA VÁLIDA”.

Coloquei de forma resumida. Tem outros detalhes de, por exemplo, ter que transformar de string para byte, de hexa para binário, que não mencionei, pois ficaria muito extenso. Mas a base, no meu caso, é esse passo-a-passo acima.
Eu gastei muitas horas (alguns dias) de pesquisa na questão da chave pública e também em outros pontos, mas ao final deu certo.

Saudações.

Bom dia amigos!

Eu estou com um problema no meu programa de validar assinatura digital e queria saber se vocês podem me ajudar, já estou a dias tentando resolver e não consigo.
O problema é o seguinte, eu tenho um arquivo e nele tem o conteúdo e a assinatura no rodapé e eu tenho a chave pública para validação.

Já preparei tudo certinho no fonte e não ocorre erro nenhum, o problema é que na hora de validar a assinatura, o método da classe Signature sempre me retorna falso. Já tentei de todos os jeitos. E eu tenho o mesmo exemplo feito em para .NET e este funciona.

Será que poderiam me ajudar?
Segue abaixo o código fonte do java e do .NET:

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import sun.misc.BASE64Decoder;

@SuppressWarnings("restriction")
public class ValidFile {

	private PublicKey publicKey;
	
	private final String PUBLIC_KEY   = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdqxkltzW8MIMRt3xreMcYR+qA" +
	                                    "XKXnTTZX6zoHH4gGUq0t1CueI5Ook5iZEn5pOYKLsMpnIStDso/51nzI9BULjiRJ" + 
                                        "0f/E7FMhaVekYS2m5B4+RaG4jrf+wZLJ1Koni3XgdOvpCa+bEMzGx5BYKtmFr7Oa" +
                                        "bMnoj4qgw3IH7bDcUQIDAQAB";
	
	private final String FILE_CHKSUM  = "TT0fhJ0ihUJ8L0fEBkeke0Wh4CQpLQjj4vBroX4wh6tKfsdqcxYaEaj5d8+9rivIe7YA6wlfKtk3Kf2b31RYluYBMqvME/EQNUTWXvh4i1U8tpu7sP/akB2oWVlVMCNM2rFWIf/t/UtdG6uKHwZcZvleyTP35AU/HNdpEh3NhJ0=";
	private final String FILE_CONTENT = "20120530\r\n053.113.791/0017-90 \r\n043.816.719/0001-08 ";
		
	public static void main(String args[]) throws NoSuchAlgorithmException, IOException {
		ValidFile vf = new ValidFile();
		
		String chksum  = vf.FILE_CHKSUM;
		String content = vf.FILE_CONTENT;
				
		byte[] data = null;
		byte[] signature = null;
		
		MessageDigest md = MessageDigest.getInstance("MD5");
		data = md.digest(content.getBytes());
		
		BASE64Decoder decode64 = new BASE64Decoder();
		signature = decode64.decodeBuffer(chksum);
	  
		System.out.println("It's a valid data? (" + vf.validSignature(data, signature) + ").");
	}
	
	public ValidFile(){
		/*
		 * The public key below "apparently" results the modulus and exponent
		 * to use in C#
		 * 
		 * Modulus:
		 * 3asZJbc1vDCDEbd8a3jHGEfqgFyl5002V+s6Bx+IBlKtLdQrniOTqJOYmRJ+aTmC
		 * i7DKZyErQ7KP+dZ8yPQVC44kSdH/xOxTIWlXpGEtpuQePkWhuI63/sGSydSqJ4t1
		 * 4HTr6QmvmxDMxseQWCrZha+zmmzJ6I+KoMNyB+2w3FE=
		 *
		 * Exponent:
		 * AQAB
		 * 
		 * -----BEGIN PUBLIC KEY-----
         * MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdqxkltzW8MIMRt3xreMcYR+qA
         * XKXnTTZX6zoHH4gGUq0t1CueI5Ook5iZEn5pOYKLsMpnIStDso/51nzI9BULjiRJ
         * 0f/E7FMhaVekYS2m5B4+RaG4jrf+wZLJ1Koni3XgdOvpCa+bEMzGx5BYKtmFr7Oa
         * bMnoj4qgw3IH7bDcUQIDAQAB
         * -----END PUBLIC KEY-----
         * 
		 */
	}
	
	private boolean loadPublicKey() {		
		BASE64Decoder decode64 = new BASE64Decoder();
		
		try {
			//Load the public key
			byte[] pkBytes = decode64.decodeBuffer(this.PUBLIC_KEY);
					  
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		  //RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
			
			EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkBytes);
		  //EncodedKeySpec spec = new X509EncodedKeySpec(pkBytes);
			
			publicKey = keyFactory.generatePublic(spec);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
		return true;
	}
	
	public boolean validSignature(byte[] data, byte[] signature) {
		boolean status = true;
		
		this.loadPublicKey();
		
		try {
			Signature sign = Signature.getInstance("MD5withRSA");
			sign.initVerify(publicKey);
			sign.update(data);
			
			status = sign.verify(signature);
		} catch (SignatureException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
				
		return status;
	}

}
using System;
using System.Security.Cryptography;
using System.Text;
using System.Numerics;

namespace Teste
{
	class Program
	{
		public static void Main()
        {
            try
            {
                string cChkSum   = "TT0fhJ0ihUJ8L0fEBkeke0Wh4CQpLQjj4vBroX4wh6tKfsdqcxYaEaj5d8+9rivIe7YA6wlfKtk3Kf2b31RYluYBMqvME/EQNUTWXvh4i1U8tpu7sP/akB2oWVlVMCNM2rFWIf/t/UtdG6uKHwZcZvleyTP35AU/HNdpEh3NhJ0=";
                string cconteudo = "20120530\r\n053.113.791/0017-90 \r\n043.816.719/0001-08 ";

                MD5 md5Hasher = MD5.Create();

                byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(cconteudo));
                byte[] ChkSum = Convert.FromBase64String(cChkSum);

                bool lValido = VerifyHash(data, ChkSum);
            } catch (Exception e) {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        
		public static bool VerifyHash(byte[] signedData, byte[] signature)
        {

            bool lret = false;
            
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSAPKCS1SignatureDeformatter RSADeformatter;

            RSA.FromXmlString("<RSAKeyValue><Modulus>3asZJbc1vDCDEbd8a3jHGEfqgFyl5002V+s6Bx+IBlKtLdQrniOTqJOYmRJ+aTmCi7DKZyErQ7KP+dZ8yPQVC44kSdH/xOxTIWlXpGEtpuQePkWhuI63/sGSydSqJ4t14HTr6QmvmxDMxseQWCrZha+zmmzJ6I+KoMNyB+2w3FE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>");
            
            RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
            RSADeformatter.SetHashAlgorithm("MD5");

            lret = RSADeformatter.VerifySignature(signedData, signature);

            return lret;
        }
	}
}

Agradeço desde já!

Obrigado,
Rubens Dos Santos Filho