package gov.pr.celepar.tabeliao.client.applet;
/*
Este programa � licenciado de acordo com a
LPG-AP (LICEN�A P�BLICA GERAL PARA PROGRAMAS DE COMPUTADOR DA ADMINISTRA��O P�BLICA),
vers�o 1.1 ou qualquer vers�o posterior.
A LPG-AP deve acompanhar todas PUBLICA��ES, DISTRIBUI��ES e REPRODU��ES deste Programa.
Caso uma c�pia da LPG-AP n�o esteja dispon�vel junto com este Programa, voc� pode contatar o LICENCIANTE ou ent�o acessar diretamente:
http://www.celepar.pr.gov.br/licenca/LPG-AP.pdf
Para poder USAR, PUBLICAR, DISTRIBUIR, REPRODUZIR ou ALTERAR este Programa
� preciso estar de acordo com os termos da LPG-AP
*/
import gov.pr.celepar.tabeliao.client.applet.cms.CMSTabeliaoSignedDataGenerator;
import gov.pr.celepar.tabeliao.util.Base64Utils;
import gov.pr.celepar.tabeliao.util.CertificationChainAndSignatureBase64;
import gov.pr.celepar.tabeliao.util.PrivateKeyAndCertChain;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AuthProvider;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.security.auth.login.LoginException;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
/**
* Classe de metodos utilitarios para a geracao das assinaturas.
*
* @version 1.1
* @author Thiago Meneghello - GIC/CELEPAR
* @author Emerson Sachio Saito - GIC/CELEPAR
*
*/
public class TabeliaoAppletUtil {
private static final String SUN_PKCS11_PROVIDER_CLASS = "sun.security.pkcs11.SunPKCS11";
private static final String PKCS11_KEYSTORE_TYPE = "PKCS11";
private static final String PKCS12_KEYSTORE_TYPE = "PKCS12";
// private static final String DIGITAL_SIGNATURE_ALGORITHM_NAME = "SHA1withRSA";
public static String providerName = null;
public static Provider pkcs11Provider = null;
/**
* Faz a leitura da Chave Privada e a Cadeia de Certificados, usando a senha informada.
* @param arquivo - Se o certificado esta armazendo em arquivo
* @param pin - senha do certificado
* @param isHardware boolean - Se o certificado esta armazendo em arquivo
* @return PrivateKeyAndCertChain Chave privada e a cadeia de certificados
* @throws DocumentSignException
*/
public static PrivateKeyAndCertChain loadPrivateKeyAndCertChain(String arquivo, String pin, boolean isHardware) throws DocumentSignException {
if (isHardware) {
if (arquivo.length() == 0) {
String errorMessage = "Voc� deve selecionar uma biblioteca PKCS#11!";
throw new DocumentSignException(errorMessage);
}
KeyStore keyStore = null;
try {
keyStore = loadKeyStoreFromSmartCard(arquivo, pin);
} catch (KeyStoreException e) {
String errorMessage = "N�o foi poss�vel ler o keyStore do Token/SmartCard.\n" +
"Detalhe do problema: " + e.getMessage();
throw new DocumentSignException(errorMessage, e);
} finally {
pin = null;
}
try {
Enumeration<String> aliasesEnum = keyStore.aliases();
if (aliasesEnum.hasMoreElements()) {
String alias = aliasesEnum.nextElement();
Certificate[] certificationChain = keyStore.getCertificateChain(alias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
PrivateKeyAndCertChain result = new PrivateKeyAndCertChain();
result.mPrivateKey = privateKey;
result.mCertificationChain = certificationChain;
return result;
} else {
throw new KeyStoreException("O keyStore est� vazio!");
}
} catch (Exception e) {
String errorMessage = "N�o foi poss�vel ler a chave privada e o certificado do smart card.\n" +
"Raz�o: " + e.getMessage();
throw new DocumentSignException(errorMessage, e);
} finally {
keyStore = null;
}
} else {
providerName = null;
// Load the keystore from specified file using the specified password
if (arquivo.length() == 0) {
String errorMessage = "Voc� deve selecionar um arquivo de certificado (.PFX ou .P12)!";
throw new DocumentSignException(errorMessage);
}
KeyStore keyStore = null;
try {
keyStore = loadKeyStoreFromPFXFile(arquivo, pin);
} catch (KeyStoreException e) {
pin = null;
String errorMessage = "N�o foi poss�vel ler o keyStore do arquivo.\n" +
"Detalhe do problema: " + e.getMessage();
// String errorMessage = "N�o foi poss�vel ler o arquivo de certificado (" +
// arquivo + ").\n senha senha informada n�o � v�lida, ou o arquivo n�o � um Certificado no formato PKCS#12 (.P12 or .PFX) " +
// "ou pode estar corrompido/adulterado.";
throw new DocumentSignException(errorMessage, e);
}
try {
Enumeration<String> aliasesEnum = keyStore.aliases();
if (aliasesEnum.hasMoreElements()) {
String alias = aliasesEnum.nextElement();
Certificate[] certificationChain = keyStore.getCertificateChain(alias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, pin.toCharArray());
PrivateKeyAndCertChain result = new PrivateKeyAndCertChain();
result.mPrivateKey = privateKey;
result.mCertificationChain = certificationChain;
return result;
} else {
throw new KeyStoreException("O keyStore est� vazio!");
}
} catch (Exception e) {
String errorMessage = "N�o foi poss�vel ler a chave privada e o certificado do smart card.\n" +
"Raz�o: " + e.getMessage();
throw new DocumentSignException(errorMessage, e);
} finally {
keyStore = null;
pin = null;
}
}
}
/**
* Gera assinatura de um documento no formato CMS/PKCS#7
* @param aDocumentToSign - Documento para assinar
* @param privateKeyAndCertChain
* @param anexaArquivo - True se gera anexado.
* @return CertificationChainAndSignatureBase64
* @see CertificationChainAndSignatureBase64
* @throws DocumentSignException
*/
public static CertificationChainAndSignatureBase64 signDocument(
byte[] aDocumentToSign, PrivateKeyAndCertChain privateKeyAndCertChain, boolean anexaArquivo)
throws DocumentSignException {
// Checa se a chave privada est� dispon�vel
PrivateKey privateKey = privateKeyAndCertChain.mPrivateKey;
if (privateKey == null) {
String errorMessage = "N�o achou a chave privada do smart card.";
throw new DocumentSignException(errorMessage);
}
// Checa se a cadeia de certifica��o em X.509 est� dispon�vel
Certificate[] certChain = privateKeyAndCertChain.mCertificationChain;
if (certChain == null) {
String errorMessage = "N�o achou o certificado p�blico.";
throw new DocumentSignException(errorMessage);
}
// Cria o objeto de resultado
CertificationChainAndSignatureBase64 signingResult =
new CertificationChainAndSignatureBase64();
// Salva a cadeia de certifica��o X.509 em um resultado codificado em Base64
try {
signingResult.mCertificationChain = Base64Utils.encodeX509CertChainToBase64(certChain);
} catch (CertificateException cee) {
String errorMessage = "Certificado inv�lido.";
throw new DocumentSignException(errorMessage);
}
// Calcula a assinatura digital para o arquivo (hash),
// Codifica em Base64 e salva no resultado
try {
CMSSignedDataGenerator signer = new CMSSignedDataGenerator();
CMSProcessable proc = new CMSProcessableByteArray(aDocumentToSign);
List<Certificate> list = new ArrayList<Certificate>();
for(int i=0 ; i<certChain.length ; i++) {
list.add(certChain[i]);
}
CertStore certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(list));
signer.addCertificatesAndCRLs(certStore);
signer.addSigner(privateKey, (X509Certificate)certChain[0], CMSSignedDataGenerator.DIGEST_SHA1);
CMSSignedData data = signer.generate(CMSSignedDataGenerator.DATA, proc, anexaArquivo, providerName);
signingResult.mSignature = data.getEncoded();
} catch (Exception gsex) {
String errorMessage = "Erro ao assinar o arquivo.\n" +
"Detalhes: " + gsex.getMessage();
gsex.printStackTrace();
throw new DocumentSignException(errorMessage, gsex);
}
return signingResult;
}
/**
* Assina o arquivos e retorna a assinatura e a cadeia de certificado
*
* @param aFileName -> Nome do arquivo
* @param privateKeyAndCertChain -> usando loadPrivateKeyAndCertChain
* @param anexaArquivo -> Anexa o arquivo com a assinatura
* @return CertificationChainAndSignatureBase64
* @see CertificationChainAndSignatureBase64
* @throws DocumentSignException
*/
public static CertificationChainAndSignatureBase64 signFile(String aFileName, PrivateKeyAndCertChain privateKeyAndCertChain, boolean anexaArquivo)
throws DocumentSignException {
// Carrega o arquivo para ser assinado
byte[] documentToSign = null;
try {
documentToSign = readFileInByteArray(aFileName);
} catch (IOException ioex) {
String errorMessage = "N�o foi poss�vel ler o arquivo para assinar " + aFileName + ".";
throw new DocumentSignException(errorMessage, ioex);
}
try {
// Efetua a assinatura.
CertificationChainAndSignatureBase64 signingResult =
signDocument(documentToSign, privateKeyAndCertChain, anexaArquivo);
return signingResult;
} finally {
}
}
/**
* Assina o Hash do Documento
* @param hash -> Hash do arquivo
* @param privateKeyAndCertChain -> use loadPrivateKeyAndCertChain
* @return CertificationChainAndSignatureBase64
* @see CertificationChainAndSignatureBase64
* @throws DocumentSignException
*/
public static CertificationChainAndSignatureBase64 signHashDocument(
byte[] hash, PrivateKeyAndCertChain privateKeyAndCertChain)
throws DocumentSignException {
// Checa se a chave privada est� dispon�vel
PrivateKey privateKey = privateKeyAndCertChain.mPrivateKey;
if (privateKey == null) {
String errorMessage = "N�o achou a chave privada do smart card.";
throw new DocumentSignException(errorMessage);
}
// Checa se a cadeia de certifica��o X.509 est� dispon�vel
Certificate[] certChain = privateKeyAndCertChain.mCertificationChain;
if (certChain == null) {
String errorMessage = "N�o achou o certificado p�blico.";
throw new DocumentSignException(errorMessage);
}
// Cria o objeto de resultado
CertificationChainAndSignatureBase64 signingResult =
new CertificationChainAndSignatureBase64();
// Salva a cadeia de certifica��o X.509 no resultado codificado em Base64
try {
signingResult.mCertificationChain = Base64Utils.encodeX509CertChainToBase64(certChain);
}
catch (CertificateException cee) {
String errorMessage = "Certificado inv�lido no smart card.";
throw new DocumentSignException(errorMessage);
}
// Calcula a assinatura digital do hash,
// Codifica em Base64 e salva no resultado
try {
//Eh aqui que a assinatura do HASH eh realizada
CMSTabeliaoSignedDataGenerator signer = new CMSTabeliaoSignedDataGenerator();
CMSProcessable proc = new CMSProcessableByteArray(hash);
List<Certificate> list = new ArrayList<Certificate>();
for(int i=0 ; i<certChain.length ; i++) {
list.add(certChain[i]);
}
CertStore certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(list));
signer.addCertificatesAndCRLs(certStore);
signer.addSigner(privateKey, (X509Certificate)certChain[0], CMSTabeliaoSignedDataGenerator.DIGEST_SHA1);
CMSSignedData data = signer.generate(CMSTabeliaoSignedDataGenerator.DIGEST_SHA1, proc, false, providerName, true);
signingResult.mSignature = data.getEncoded();
} catch (Exception gsex) {
String errorMessage = "Erro ao assinar o arquivo.\n" +
"Detalhes: " + gsex.getMessage();
gsex.printStackTrace();
throw new DocumentSignException(errorMessage, gsex);
}
return signingResult;
}
/**
*
* Exibe uma caixa de dialogo com uma mensagem.
*
* @param applet
* @param mensagem -> String com a mensagem a ser exibida
* @param tipo -> Tipo da mensagem, sendo: 0 = Informacao, 1 = Alerta e 2 = Erro
*/
public static void showMensagem(JApplet applet, String mensagem, int tipo) {
String[] botao = new String[]{""};
botao[0]= "Fechar";
String Titulo;
int tipoMsg;
switch (tipo) {
case 0: Titulo= "Informa��o do Sistema."; tipoMsg=JOptionPane.INFORMATION_MESSAGE; break;
case 1: Titulo= "Alerta do Sistema!"; tipoMsg=JOptionPane.WARNING_MESSAGE; break;
case 2: Titulo= "Erro na Execu��o!"; tipoMsg=JOptionPane.ERROR_MESSAGE; break;
default: Titulo= "Informa��o do Sistema!"; tipoMsg=JOptionPane.INFORMATION_MESSAGE; break;
}
JOptionPane.showOptionDialog(applet,
mensagem,
Titulo,
JOptionPane.YES_OPTION,
tipoMsg,
null,
botao,
botao[0]);
//JOptionPane.showMessageDialog(applet, message, "Mensagem do Sistema",JOptionPane.NO_OPTION);
}
/**
* Carrega o chaveiro do smartcard/token usando a implementacao da biblioteca PKCS#11
* e o provider de seguranca PKCS#11 da Sun.
* Sera requisitado o PIN para acessar o smartcard/token
*
* @param aPKCS11LibraryFileName -> local da .so ou .dll para Leitora ou Token
* @param aSmartCardPIN -> PIN para acesso ao smartcard
* @return Keystore
* @see java.security.KeyStore
* @throws KeyStoreException
*/
private static KeyStore loadKeyStoreFromSmartCard(String aPKCS11LibraryFileName, String aSmartCardPIN) throws KeyStoreException {
// First configure the Sun PKCS#11 provider. It requires a stream (or file)
// containing the configuration parameters - "name" and "library".
String pkcs11ConfigSettings = "name = SmartCard\n" + "library = " + aPKCS11LibraryFileName;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
// Instantiate the provider dynamically with Java reflection
Class<?> sunPkcs11Class;
Constructor<?> pkcs11Constr;
try {
sunPkcs11Class = Class.forName(SUN_PKCS11_PROVIDER_CLASS);
pkcs11Constr = sunPkcs11Class.getConstructor(java.io.InputStream.class);
pkcs11Provider = (Provider) pkcs11Constr.newInstance(confStream);
providerName = pkcs11Provider.getName();
Security.addProvider(pkcs11Provider);
} catch (SecurityException e) {
throw new KeyStoreException("Sem permiss�o para instanciar a Classe '" + SUN_PKCS11_PROVIDER_CLASS + "'.", e);
} catch (ClassNotFoundException e) {
throw new KeyStoreException("A Classe '" + SUN_PKCS11_PROVIDER_CLASS + "' n�o foi encontrada.", e);
} catch (NoSuchMethodException e) {
//Nao deve entrar aqui!!!
throw new KeyStoreException("O construtor da Classe '" + SUN_PKCS11_PROVIDER_CLASS + "' n�o foi encontrado.", e);
} catch (IllegalArgumentException e) {
//Nao deve entrar aqui!!!
throw new KeyStoreException("Argumentos inv�lidoa na chamada da Classe '" + SUN_PKCS11_PROVIDER_CLASS + "'.", e);
} catch (InstantiationException e) {
//Nao deve entrar aqui!!!
throw new KeyStoreException("Ocorreu um erro ao criar uma nova inst�ncia da Classe '" + SUN_PKCS11_PROVIDER_CLASS + "'.", e);
} catch (IllegalAccessException e) {
//Nao deve entrar aqui!!!
throw new KeyStoreException("Sem permiss�o para instanciar a Classe '" + SUN_PKCS11_PROVIDER_CLASS + "'.", e);
} catch (InvocationTargetException e) {
//Nao deve entrar aqui!!!
throw new KeyStoreException("N�o pode chamar o m�todo da Classe '" + SUN_PKCS11_PROVIDER_CLASS + "'.", e);
}
// try {
// } catch (Exception e) {
// throw new KeyStoreException("N�o foi poss�vel inicializar o provedor de seguranca PKCS#11. " +
// "Raz�o: " + e.getCause().getMessage());
// }
// Read the keystore from the smart card
char[] pin = aSmartCardPIN.toCharArray();
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE);
keyStore.load(null, pin);
} catch (KeyStoreException e) {
throw new KeyStoreException("Erro ao tentar carregar as chaves do certificado do Token/SmartCard.\n" +
"O Cart�o SmartCard n�o est� inserido na leitora,\n" +
"ou o Token n�o conectado ao computador, \n" +
"ou h� problemas t�cnicos com a Leitora/Cart�o ou Token! \n" +
"Verifique a posi��o do Cart�o para tentar novamente.\n" +
"Feche e abra novamente o Navegador!\n"+
"Estando aparentemente tudo correto: Anote este Erro!,\n" +
" Feche o Navegador e acione o suporte t�cnico!", e);
} catch (NoSuchAlgorithmException e) {
throw new KeyStoreException("Erro ao tentar carregar o algoritmo para validar o certificado.\n" +
"Anote este Erro!, Feche a aplica��o e acione o suporte t�cnico!", e);
} catch (CertificateException e) {
throw new KeyStoreException("Erro ao tentar ler o certificado do Token/SmartCard.\n" +
"Anote este Erro!, Feche a aplica��o e acione o suporte t�cnico!", e);
} catch (IOException e) {
throw new KeyStoreException("A informada Senha N�O � v�lida, \n" +
"ou Cart�o SmartCard n�o est� inserido na leitora,\n" +
"ou Token n�o conectado ao computador, \n" +
"ou h� problemas t�cnicos com a Leitora/Cart�o ou Token!\n" +
"Voc� pode informar a senha novamente! \n" +
"CUIDADO: mais de 3 (tr�s) tentativas erradas ir�o bloquear seu CART�O/TOKEN!\n" +
"Persistindo o problema acione o suporte t�cnico!", e);
}
return keyStore;
}
/**
* Efetua logout do SmartCard
* @throws LoginException
*/
public static void logoutSmart () throws LoginException{
if (pkcs11Provider != null){
AuthProvider ap = (AuthProvider) pkcs11Provider;
ap.logout();
pkcs11Provider = null;
}
if (providerName != null){
Security.removeProvider(providerName);
providerName = null;
}
}
/**
* Carrega o chaveiro de um arquivo de certificado .PFX ou .P12 (devera estar no formato PKCS#12)
* usando a senha para o certificado
* @param aFileName -> local fisico onde esta armazenado o certificado
* @param aKeyStorePasswd -> senha para acesso ao certificado
* @return Keystore
* @see java.security.KeyStore
* @throws KeyStoreException
*/
private static KeyStore loadKeyStoreFromPFXFile(String aFileName, String aKeyStorePasswd) throws KeyStoreException {
KeyStore keyStore = null;
FileInputStream keyStoreStream;
char[] password = aKeyStorePasswd.toCharArray();
try {
keyStore = KeyStore.getInstance(PKCS12_KEYSTORE_TYPE);
keyStoreStream = new FileInputStream(aFileName);
keyStore.load(keyStoreStream, password);
} catch (KeyStoreException e) {
throw new KeyStoreException("N�o foi poss�vel carregar as chaves do certificado do tipo '" + PKCS12_KEYSTORE_TYPE + "'.\n" +
"Anote este Erro!, Feche a aplica��o e acione o suporte t�cnico!", e);
} catch (FileNotFoundException e) {
throw new KeyStoreException("N�o foi poss�vel localizar o arquivo '" + aFileName + "'.\n" +
"Informe novamente, e certifique-se de que o arquivo � v�lido.", e);
} catch (NoSuchAlgorithmException e) {
throw new KeyStoreException("N�o foi poss�vel encontrar o algoritmo para validar o certificado. \n" +
"Anote este Erro!, Feche a aplica��o e acione o suporte t�cnico! ", e);
} catch (CertificateException e) {
throw new KeyStoreException("N�o foi poss�vel carregar o certificado do KeyStore.\n" +
"Anote este Erro!, Feche a aplica��o e acione o suporte t�cnico!", e);
} catch (IOException e) {
throw new KeyStoreException("Senha informada n�o � valida!, \n" +
"ou o arquivo pode estar corrompido/adulterado!\n" +
"Informe a senha novamente, ou acione o suporte t�cnico!", e);
}
return keyStore;
}
/**
* faz a leitura de um arquivo especifico em um array de byte.
*
* @param aFileName -> Nome do arquivo
* @return byte[]
* @throws IOException
*/
private static byte[] readFileInByteArray(String aFileName)
throws IOException {
File file = new File(aFileName);
FileInputStream fileStream = new FileInputStream(file);
try {
int fileSize = (int) file.length();
byte[] data = new byte[fileSize];
int bytesRead = 0;
while (bytesRead < fileSize) {
bytesRead += fileStream.read(data, bytesRead, fileSize-bytesRead);
}
return data;
}
finally {
fileStream.close();
}
}
}