Demoiselle Certificate QuickStart

Transcrição

Demoiselle Certificate QuickStart
Demoiselle Certificate
QuickStart
Ednara Oliveira
Emerson Oliveira
Sobre o QuickStart - versão 2.0.0 ............................................................................................... v
1. Ambiente ......................................................................................................................... 1
1.1. Ambiente recomendado .............................................................................................. 1
2. Criação da aplicação .......................................................................................................... 3
2.1. Nossa primeira aplicação ............................................................................................ 3
2.2. Criando sua Applet para exibir informações em uma página ................................................. 3
2.3. Assinatura dos jars ................................................................................................... 4
2.4. Projeto Web Exemplo ................................................................................................ 5
2.5. Assinando um documento ........................................................................................... 7
2.6. Executando o Applet ................................................................................................ 11
3. Usando um carimbo de tempo ........................................................................................... 13
3.1. Como funciona ....................................................................................................... 13
3.2. Criando uma Servlet para gerar o TimeStamp ................................................................. 14
3.3. Criando sua própria implementação de TimeStampGenerator .............................................. 17
3.4. Adicionando segurança ............................................................................................. 21
3.4.1. Configurando o Jboss EAP 6 ........................................................................... 21
3.4.2. Configurando a aplicação Web ......................................................................... 21
3.4.3. Criando as páginas HTML ............................................................................... 22
3.4.4. Configurando o Servlet ................................................................................... 23
A. Repositório dos JARs ......................................................................................................... 25
B. Assinando JARs com certificado auto-assinado ......................................................................... 27
B.1. Criando um certificado ............................................................................................. 27
B.2. Assinando um jar com certificado auto-assinado .............................................................. 27
iii
iv
Sobre o QuickStart - versão 2.0.0
Este documento é um tutorial do tipo "passo-a-passo" que visa ilustrar de forma rápida e prática a criação de uma
aplicação para assinatura digital utilizando o Demoiselle Certificate 2.0.0.
Nota
É desejável o conhecimento de algumas tecnologias envolvidas no desenvolvimento de
aplicações Java Web, incluindo:
• Linguagem Java
• Java Applet
• Servlets, JSP e Tag Libraries
• HTML e XML
• Contêineres e Servidores Web
Nota
Esta documentação refere-se à versão 2.0.0 do Demoiselle Certificate e pode diferir
significativamente de outras versões.
v
vi
Ambiente
1.1. Ambiente recomendado
Para desenvolver e executar as aplicações deste guia, utilizamos e recomendamos o seguinte ambiente:
Software
Versão
Site (Download)
Java Development Kit (JDK)
7.0
www.oracle.com
[http://www.oracle.com/
technetwork/java/javase/downloads/
index.html]
Apache Maven
2.2
maven.apache.org
[http://
maven.apache.org/docs/2.2.1/releasenotes.html]
Eclipse IDE
4.3
www.eclipse.org
[https://www.eclipse.org/
kepler/]
m2eclipse plugin
1.4
m2eclipse.sonatype.org
[http://
m2eclipse.sonatype.org/installingm2eclipse.html]
JBoss Application Server
7.1.1
www.jboss.org
[http://download.jboss.org/
jbossas/7.1/jboss-as-7.1.1.Final/jbossas-7.1.1.Final.zip]
1
2
Criação da aplicação
2.1. Nossa primeira aplicação
Nesta seção apresentaremos o passo-a-passo para construção de um projeto de exemplo do demoiselle-applet.
Nele será construída uma página html que executará a applet para assinatura de documentos, utilizando
certificados A1 ou A3, e apresentação das informações do certificado na própria página html.
2.2. Criando sua Applet para exibir informações em
uma página
Crie um novo projeto Maven em branco, marcando a opção "Create a Simple Project" e atribua os seguintes valores:
Group-id: br.gov.frameworkdemoiselle.certificate.sample
Artifact-id: AppletCustomizada
Packaging: JAR
Após criada a aplicação adicione como dependência o componente demoiselle-certificate-applet:
<dependencies>
<dependency>
<groupId>br.gov.frameworkdemoiselle.component</groupId>
<artifactId>demoiselle-certificate-applet</artifactId>
<version>2.0.0</version>
</dependency>
...
</dependencies>
Como esse componente não está no repositório Maven, devemos adicionar também no pom.xml o repositório do
qual o componente deve ser baixado:
<repositories>
<repository>
<id>demoiselle</id>
<name>Demoiselle SourceForge Repository</name>
<url>http://demoiselle.sourceforge.net/repository/release</url>
</repository>
...
</repositories>
Em
seguida,
crie
a
classe
App.java
no
br.gov.frameworkdemoiselle.certificate.sample.applet,
estendendo
AbstractAppletExecute.
pacote
a
classe
3
Capítulo 2. Criação da aplicação
public class App extends AbstractAppletExecute {
@Override
public void execute(KeyStore keystore, String alias, Applet applet) {
try {
/* Exibe alguns dados do certificado */
ICPBrasilCertificate certificado = super.getICPBrasilCertificate(keystore, alias, false);
AbstractAppletExecute.setFormField(applet, "mainForm", "cpf", certificado.getCpf());
AbstractAppletExecute.setFormField(applet, "mainForm", "nome", certificado.getNome());
AbstractAppletExecute.setFormField(applet, "mainForm", "nascimento", certificado.getDataNascimento())
AbstractAppletExecute.setFormField(applet, "mainForm", "email", certificado.getEmail());
} catch (KeyStoreException e) {
JOptionPane.showMessageDialog(applet, e.getMessage(), "Error",
}
JOptionPane.ERROR_MESSAGE);
}
@Override
public void cancel(KeyStore keystore, String alias, Applet applet) {
/* Seu codigo customizado aqui... */
}
}
No código acima o método execute será acionado logo após o carregamento do keystore do usuário. O método
getICPBrasilCertificate retorna um objeto do tipo ICPBrasilCertificate que possui todas as informações
de um certificado ICPBrasil.
Os métodos setFormField escrevem no formulário html chamado de mainForm no qual a applet está sendo
executado. O terceiro parâmetro do método informa em qual campo do formulário a informação será registrada.
O método cancel pode ser utilizado para implementar uma ação no caso do usuário desistir da ação. No código
de exemplo é feito apenas a ocultação da applet.
2.3. Assinatura dos jars
O modelo de segurança da plataforma Java é centrado sobre o conceito de sandbox (caixa de areia), no qual um
código remoto como um applet por padrão não é confiável e, portanto, não pode ter acesso ilimitado ao Sistema
Operacional. Para que possamos executar nossa applet, precisamos assinar todos os jars necessários à sua
execução, conforme mostrado na tabela abaixo:
Tabela 2.1. Lista dos jars assinados
Jar Original
Jar Assinado
demoiselle-certificate-applet-customizada-1.0.0.jar
demoiselle-certificate-applet-customizada-1.0.0assinado.jar
demoiselle-certificate-applet-2.0.0.jar
demoiselle-certificate-applet-2.0.0-assinado.jar
demoiselle-certificate-core-2.0.0.jar
demoiselle-certificate-core-2.0.0-assinado.jar
demoiselle-certificate-signer-2.0.0.jar
demoiselle-certificate-signer-2.0.0-assinado.jar
demoiselle-certificate-policy-engine-2.0.0.jar
demoiselle-certificate-policy-engine-2.0.0-assinado.jar
demoiselle-certificate-timestamp-2.0.0.jar
demoiselle-certificate-timestamp-2.0.0-assinado.jar
demoiselle-certificate-criptography-2.0.0.jar
demoiselle-certificate-criptography-2.0.0-assinado.jar
4
Projeto Web Exemplo
Jar Original
Jar Assinado
demoiselle-certificate-ca-icpbrasil-2.0.0.jar
demoiselle-certificate-ca-icpbrasil-2.0.0-assinado.jar
demoiselle-certificate-ca-icpbrasil-
demoiselle-certificate-ca-icpbrasil-homologacao-2.0.0-
homologacao-2.0.0.jar
assinado.jar
bcprov-jdk15on-1.51.jar
bcprov-jdk15on-1.51-assinado.jar
bcpkix-jdk15on-1.51.jar
bcpkix-jdk15on-1.51-assinado.jar
bcmail-jdk15on-1.51.jar
bcmail-jdk15on-1.51-assinado.jar
commons-io-1.3.2.jar
commons-io-1.3.2-assinado.jar
log4j-1.2.17.jar
log4j-1.2.17-assinado.jar
slf4j-api-1.6.1.jar
slf4j-api-1.6.1-assinado.jar
slf4j-log4j12-1.6.1.jar
slf4j-log4j12-1.6.1-assinado.jar
plugin.jar
plugin-assinado.jar
Para mais detalhes sobre os procedimentos para assinatura dos artefatos, consulte a documentação de referência
[http://demoiselle.sourceforge.net/docs/components/certificate/reference/2.0.0/html_single/], e o Apendice B.
2.4. Projeto Web Exemplo
Crie um novo projeto Maven em branco, marcando a opção "Create a Simple Project" e atribua os seguintes valores:
Group-id: br.gov.frameworkdemoiselle.certificate.sample
Artifact-id: AppletCustomizadaWeb
Packaging: WAR
Adicione a página html que irá executar a applet:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Exemplo de Assinatura de Documento</title>
<link href="css/default.css" rel="stylesheet">
</head>
<body>
<form id="mainForm" name="mainForm" method="post" action="">
<h3>Applet Exemplo de Assinatura Digital</h3>
<object codetype="application/java"
classid="java:br.gov.frameworkdemoiselle.certificate.applet.view.JPanelApplet"
width=480 height=350
archive="AppletCustomizada-1.0.0-assinado.jar,
demoiselle-certificate-applet-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-core-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-signer-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-policy-engine-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-timestamp-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-criptography-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-ca-icpbrasil-2.0.0-SNAPSHOT-assinado.jar,
demoiselle-certificate-ca-icpbrasil-homologacao-2.0.0-SNAPSHOT-assinado.jar,
bcprov-jdk15on-1.51-assinado.jar,
5
Capítulo 2. Criação da aplicação
bcpkix-jdk15on-1.51-assinado.jar,
bcmail-jdk15on-1.51-assinado.jar,
commons-io-1.3.2-assinado.jar,
log4j-1.2.17-assinado.jar,
slf4j-api-1.6.1-assinado.jar,
slf4j-log4j12-1.6.1-assinado.jar,
plugin-assinado.jar">
<param name="factory.applet.action"
value="br.gov.frameworkdemoiselle.certificate.sample.applet.App" />
<param name="applet.javascript.postaction.failure" value="foo" />
No Applet
</object>
<label for="documento">Arquivo: </label>
<input id="documento" type="file" name="documento">
<label
<input
<label
<input
<label
<input
<label
<input
</form>
</body>
for="cpf">CPF</label>
id="cpf" type="text" name="cpf" disabled="disabled">
for="nome">Nome</label>
id="nome" type="text" name="nome" disabled="disabled">
for="nascimento">Nascimento</label>
id="nascimento" type="text" name="nascimento" disabled="disabled">
for="email">Email</label>
id="email" type="text" name="email" disabled="disabled">
</html>
Nota
É necessário que os arquivos .jar assinados na sessão anterior sejam adicionados ao mesmo
diretório da página html que irá utilizá-los
Para o funcionamento da appletapplet são necessárias as propiedades: factory.applet.action e
applet.javascript.postaction.failure. A primeira define qual classe será instanciada no momento
do clique do botão Ok e carregamento do Keystore do usuário, enquanto a segunda define qual método JavaScript
deverá ser chamado em caso de alguma falha.
Acrescente um arquivo default.css na pasta webpapp/css, com o seguinte conteúdo:
label {
display: block;
margin-bottom: -1em;
}
input {
display: block;
position: relative;
left: 7em;
top: -0.5em;
}
input[type="file"]{
border: 1px solid #cccccc;
padding: 5px;
6
Assinando um documento
}
O seu projeto ficará com a seguinte estrutura de diretórios:
AppletCustomizadaWeb
### pom.xml
### src
### main
#
#
#
#
### java
### resources
### webapp
### css
#
#
#
#
#
###
###
###
### default.css
AppletCustomizada-1.0.0-assinado.jar
applet.html
bcmail-jdk15on-1.51-assinado.jar
#
#
#
#
#
#
#
###
###
###
###
###
###
###
bcpkix-jdk15on-1.51-assinado.jar
bcprov-jdk15on-1.51-assinado.jar
commons-io-1.3.2-assinado.jar
demoiselle-certificate-applet-2.0.0-assinado.jar
demoiselle-certificate-ca-icpbrasil-2.0.0-assinado.jar
demoiselle-certificate-core-2.0.0-assinado.jar
demoiselle-certificate-criptography-2.0.0-assinado.jar
#
###
#
###
#
###
#
###
#
###
#
###
#
###
### test
demoiselle-certificate-policy-engine-2.0.0-assinado.jar
demoiselle-certificate-signer-2.0.0-assinado.jar
demoiselle-certificate-timestamp-2.0.0-assinado.jar
log4j-1.2.17-assinado.jar
plugin-assinado.jar
slf4j-api-1.6.1-assinado.jar
slf4j-log4j12-1.6.1-assinado.jar
### java
### resources
2.5. Assinando um documento
Para realizar assinatura digital do contéudo utilizamos o componente demoiselle-certificate-signer.
Esse componente assina um conteúdo carregado no applet, utilizando um certificado digital também carregado
pela applet.
Para isso, devemos adicionar dependência à esse componente no pom.xml do projeto AppletCustomizada:
<dependencies>
<dependency>
<groupId>br.gov.frameworkdemoiselle.component</groupId>
<artifactId>demoiselle-certificate-signer</artifactId>
<version>2.0.0</version>
</dependency>
...
7
Capítulo 2. Criação da aplicação
</dependencies>
Para assinatura de documentos é necessário criar um objeto PKCS7Singer, configurar os atributos e chamar o
método doSign():
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificates(keystore.getCertificateChain(alias));
signer.setPrivateKey((PrivateKey) keystore.getKey(alias, null));
byte[] signed = signer.doSign(content);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_1);
O componente implementa assinatura padrão ICPBrasil de referência básica (RB) e de referência temporal (RT). No
método setSignaturePolicy() você pode definir a política a ser usada. Atualmente o componente suporta
as política de assinatura listadas abaixo:
• PolicyFactory.Policies.AD_RB_CADES_1_0, Refere-se à Assinatura Digital de Referência Básica
versão 1.0;
• PolicyFactory.Policies.AD_RB_CADES_1_1, Refere-se à Assinatura Digital de Referência Básica
versão 1.1;
• PolicyFactory.Policies.AD_RB_CADES_2_0, Refere-se à Assinatura Digital de Referência Básica
versão 2.0;
• PolicyFactory.Policies.AD_RB_CADES_2_1, Refere-se à Assinatura Digital de Referência Básica
versão 2.1;
• PolicyFactory.Policies.AD_RT_CADES_1_0, Refere-se à Assinatura Digital de Referência Temporal
versão 1.0;
• PolicyFactory.Policies.AD_RT_CADES_1_1, Refere-se à Assinatura Digital de Referência Temporal
versão 1.1;
• PolicyFactory.Policies.AD_RT_CADES_2_0, Refere-se à Assinatura Digital de Referência Temporal
versão 2.0;
• PolicyFactory.Policies.AD_RT_CADES_2_1, Refere-se à Assinatura Digital de Referência Temporal
versão 2.1;
Por isso, devemos adicionar ainda outra dependência ao projeto AppletCustomizada, justamente ao componente
responsável pelo gerenciamento das políticas, o demoiselle-certificate-policy-engine:
<dependencies>
<dependency>
<groupId>br.gov.frameworkdemoiselle.component</groupId>
<artifactId>demoiselle-certificate-policy-engine</artifactId>
<version>2.0.0</version>
</dependency>
...
</dependencies>
8
Assinando um documento
Para utilizar esses recursos, devemos modificar nossa classe App.java, também no projeto AppletCustomizada,
que ficará assim:
public class App extends AbstractAppletExecute{
private static final Logger logger = LoggerFactory.getLogger(App.class);
@Override
public void execute(KeyStore keystore, String alias, Applet applet) {
try {
/* Exibe alguns dados do certificado */
ICPBrasilCertificate certificado = super.getICPBrasilCertificate(keystore, alias, false);
AbstractAppletExecute.setFormField(applet, "mainForm", "cpf", certificado.getCpf());
AbstractAppletExecute.setFormField(applet, "mainForm", "nome", certificado.getNome());
AbstractAppletExecute.setFormField(applet, "mainForm", "nascimento", certificado.getDataNascimento())
AbstractAppletExecute.setFormField(applet, "mainForm", "email", certificado.getEmail());
/* Carregando o conteudo a ser assinado */
String documento = AbstractAppletExecute.getFormField(applet, "mainForm", "documento");
if (documento.length() == 0) {
JOptionPane.showMessageDialog(applet, "Por favor, escolha um documento para
assinar", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
String path = new File(documento).getAbsolutePath();
byte[] content = readContent(path);
logger.info("Path.........: {}", path );
/* Parametrizando o objeto doSign */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificates(keystore.getCertificateChain(alias));
signer.setPrivateKey((PrivateKey) keystore.getKey(alias, null));
signer.setAttached(true);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_1);
/* Realiza a assinatura do conteudo */
logger.info("Efetuando a assinatura do conteudo");
byte[] signed = signer.doSign(content);
/* Grava o conteudo assinado no disco */
writeContent(signed, documento.concat(".p7s"));
/* Valida o conteudo */
logger.info("Efetuando a validacao da assinatura.");
boolean checked = signer.check(content, signed);
if (checked) {
JOptionPane.showMessageDialog(applet, "O arquivo foi assinado e validado com
sucesso.", "Mensagem", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(applet,
inv#lida.", "Error", JOptionPane.ERROR_MESSAGE);
"Assinatura
}
9
Capítulo 2. Criação da aplicação
} catch (KeyStoreException e) {
JOptionPane.showMessageDialog(applet, e.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
} catch (UnrecoverableKeyException e) {
JOptionPane.showMessageDialog(applet, e.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
} catch (NoSuchAlgorithmException e) {
JOptionPane.showMessageDialog(applet, e.getMessage(), "Error",
}
}
@Override
public void cancel(KeyStore keystore, String alias, Applet applet) {
// TODO Auto-generated method stub
}
private byte[] readContent(String arquivo) {
byte[] result = null;
try {
File file = new File(arquivo);
FileInputStream is = new FileInputStream(file);
result = new byte[(int) file.length()];
is.read(result);
is.close();
} catch (IOException ex) {
logger.info(ex.getMessage());
}
return result;
}
private void writeContent(byte[] conteudo, String arquivo) {
try {
File file = new File(arquivo);
FileOutputStream os = new FileOutputStream(file);
os.write(conteudo);
os.flush();
os.close();
} catch (IOException ex) {
logger.info(ex.getMessage());
}
}
}
O seu projeto ficará com a seguinte estrutura de diretórios:
AppletCustomizada
### pom.xml
### src
### main
10
#
#
### java
#
### br
#
#
#
#
#
#
#
#
### gov
### frameworkdemoiselle
### certificate
### sample
JOptionPane.ERROR_MESSAGE);
Executando o Applet
#
#
#
#
### applet
### App.java
#
### resources
### test
### java
### resources
2.6. Executando o Applet
Agora publique sua aplicação web em um servidor Jboss EAP6, e acesse a url http://localhost:8080/
AppletCustomizadaWeb/applet.html. Se o certificado digital necessitar do pin para que seja feito o acesso, a
aplicação solicitará imediatamente o pin de seu certificado, conforme a tela abaixo:
Figura 2.1. Solicitação de Pin do Certificado
O componente exibirá uma tela com os certificados disponíveis, seu número de série, sua data inicial de validade,
sua data final de validade, e o emissor deste certificado, fornecendo ao usuário a possibilidade de escolher qual
certificado deseja-se utilizar.
11
Capítulo 2. Criação da aplicação
Figura 2.2. Lista com certificado de usuário
12
Usando um carimbo de tempo
3.1. Como funciona
Para usar uma política AD_RT (Assinatura Digital de Referência Temporal) do ICP-Brasil, precisamos
requisitar uma referência temporal digital a uma ACT (Autoridade de Carimbo do Tempo), que possue um
servidor devidamente homologado. O componente demoiselle-certificate-timestamp possui toda a
implementação necessária para acessar o servidor de carimbo de tempo do SERPRO (que é uma ACT). Para
obter uma referência temporal é necessário assinar uma requisição usando um certificado que esteja autorizado a
acessar esse servidor, e assim gerar uma assinatura de conteúdo com referência temporal.
Nota
Para mais informações sobre o serviço de Assinatura Digital de Referência Temporal
ofericido pela ACT SERPRO, acesse o site carimbodotempo.serpro.gov.br/act [http://
carimbodotempo.serpro.gov.br/act/].
Figura 3.1. Arquitetura padrão
Para aplicações que disponibilizam assinatura com referência temporal para diversos usuários, essa implementação
padrão pode não ser a ideal, pois será necessário autorizar cada usuário para obter um carimbo de tempo. Pensando
nisso, o componente oferece possibilidade para que a aplicação não peça o carimbo de tempo diretamente ao
servidor de carimbo de tempo, mas que solicite a um outro servidor que faça isso pela aplicação, como ilustra a figura
2.2. Dessa forma, apenas o servidor intermediário necessita assinar as requisições com um certificado autorizado
pelo servirdor de carimbo de tempo, não havendo então a necessidade de autorizar cada usuário da aplicação.
13
Capítulo 3. Usando um carimbo...
Figura 3.2. Arquitetura com Servlet
Essa alternativa pode ser aplicada através da implementação, na própria aplicação, da classe que obtém o carimbo
de tempo, em substituição à implementação padrão, como mostraremos a seguir.
Em nossa aplicação vamos criar uma implementação de gerador de timestamp que ao invés de pedir ao servidor
um carimbo usando o certificado que assina o documento, peça a um servidor que tenha acesso a um certificado
de máquina devidamente autorizado. A assinatura do documento será feita pelo certificado do usuário que terá um
carimbo de tempo para aquele conteúdo gerado com um certificado de aplicação. Nossa implementação irá enviar o
conteúdo lido pela applet para um Servlet que irá carregar o certificado da aplicação e solicitar o carimbo de tempo.
Nesse exemplo usamos Servlet, mas você também pode usar um serviço REST ou um WebService, lembrando
apenas que é importante esse serviço exigir autenticação para que possa ser acessado.
3.2. Criando uma Servlet para gerar o TimeStamp
Em nosso projeto web, vamos adicionar ao pom.xml a dependência do componente demoiselle-
certificate-timestamp. Vamos utilizar sua API para obter um carimbo de tempo.
<dependencies>
<dependency>
<groupId>br.gov.frameworkdemoiselle.component</groupId>
<artifactId>demoiselle-certificate-timestamp</artifactId>
<version>2.0.0</version>
</dependency>
...
</dependencies>
Crie
a
classe
TimeStampGeneratorServlet
no
pacote
br.gov.frameworkdemoiselle.certificate.sample. Nessa classe vamos implementar um método
para ler o certificado de máquina. Leremos o conteúdo enviado para a Servlet e faremos a requisição ao servidor
de carimbo de tempo.
14
Criando uma Servlet para gerar o TimeStamp
Para usar a API de Servlet do Java adicione a dependência ao javaee-web-api, no pom.xml da aplicação
AppletCustomizadaWeb:
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
...
</dependencies>
Além disso, nossa Servlet irá receber e enviar dados via stream. Por isso, adicionamos também dependência ao
commons-io, que oferece algumas facilidades para a execução dessas tarefas:
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
...
</dependencies>
Crie o método loadCertificate, que carrega o certificado no servidor da sua aplicação que tem acesso
ao servidor de carimbo de tempo. Para mais detalhes de como implementar a leitura de certificado consulte
a
documentação de referência [http://demoiselle.sourceforge.net/docs/components/certificate/reference/2.0.0/
html_single/].
private void loadCertificate() throws Exception{
CertificateLoader certificateLoader = new CertificateLoaderImpl();
X509Certificate certificate = certificateLoader.load(new File("certificado.cer");
}
O segundo passo é ler o arquivo enviado à nossa Servlet.
content = IOUtils.toByteArray(request.getInputStream());
O terceito e último passo é enviar a solicitação para o servidor de carimbo de tempo usando a API do componente
demoiselle-certificate-timestamp. Para criar a solicitação, basta criarmos um TimeStampOperator
e chamarmos o método createRequest passando o conteúdo, o certificado e a chave privada para assinar a
requisição.
15
Capítulo 3. Usando um carimbo...
TimeStampOperator timeStampOperator = new TimeStampOperator();
byte[] reqTimestamp = timeStampOperator.createRequest(privateKey,certificates, content);
timestamp = timeStampOperator.invoke(reqTimestamp);
Abaixo segue um exemplo completo da classe.
@WebServlet("/carimbo")
public class TimeStampGeneratorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(TimeStampGeneratorServlet.class);
private PrivateKey privateKey = null;
private Certificate[] certificates = null;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOEx
byte[] content;
byte[] timestamp;
try {
loadCertificate();
//Lendo o Conte#do enviado
content = IOUtils.toByteArray(request.getInputStream());
if (content.length > 0) {
//requisitando um carimbo de tempo
TimeStampOperator timeStampOperator = new TimeStampOperator();
byte[] reqTimestamp = timeStampOperator.createRequest(privateKey,certificates, content);
timestamp = timeStampOperator.invoke(reqTimestamp);
response.setContentType("application/octet-stream");
response.getOutputStream().write(timestamp);
}else{
response.setContentType("text/plain");
response.setStatus(500);
response.getOutputStream().write("Conte#do n#o enviado".getBytes());
}
} catch (CertificateCoreException e) {
response.setContentType("text/plain");
response.setStatus(500);
response.getOutputStream().write(e.getMessage().getBytes());
} catch (Exception e) {
response.setContentType("text/plain");
response.setStatus(500);
response.getOutputStream().write("Erro ao fazer load do certificado habilitado
para requisitar carimbo de tempo".getBytes());
} finally {
response.getOutputStream().flush();
response.getOutputStream().close();
}
16
Criando sua própria implementação de TimeStampGenerator
private void loadCertificate() throws Exception{
CertificateLoader certificateLoader = new CertificateLoaderImpl();
X509Certificate certificate = certificateLoader.load(new File("certificado.cer");
}
}
3.3. Criando sua própria implementação de
TimeStampGenerator
Agora vamos criar uma implementação própria do TimeStampGenerator em nossa applet. Ao invés de solicitar um
carimbo de tempo diretamente ao servidor, nossa implememtação se conecta a um serviço responsável por essa
solicitação, enviando para ele o conteúdo, e recebendo de volta o carimbo de tempo. Assim, da mesma forma que
a Servlet, vamos também adicionar aqui a dependência ao commons-io:
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
...
<dependencies>
Em
seu
projeto
AppletCustomizada,
crie
no
pacote
br.gov.frameworkdemoiselle.certificate.sample a classe MyTimeStampGeneratorImpl,
que deve implementar a interface TimeStampGenerator. Para descobrir as implementações de
TimeStampGenerator presentes no projeto, é utilizado o SPI, por isso precisamos fazer dois ajustes
para que a classe criada seja utilizada no lugar da implementação padrão. A primeira é anotar
a classe MyTimeStampGeneratorImpl com a anotação @Priority(Priority.MAX_PRIORITY),
para
definir
a
nossa
implementação
como
prioritária.
A
segunda
é
criar
um
arquivo
chamado
br.gov.frameworkdemoiselle.certificate.timestamp.TimeStampGenerator no
diretório
src/main/resources/META-INF/services,
e
dentro
desse
arquivo
colocar
o
caminho
completo
da
classe
MyTimestampGeneratorImpl,
que
é
br.gov.frameworkdemoiselle.certificate.sample.MyTimeStampGeneratorImpl, no caso do
nosso exemplo.
Importante
Para
garantir
que
importante
adicionar
e
adicionar
o
a
sua
a
anotação
caminho
para
implementação
será
utilizada,
é
@Priority(Priority.MAX_PRIORITY),
sua
implementação
no
arquivo
br.gov.frameworkdemoiselle.certificate.timestamp.TimeStampGenerator,
dentro da pasta src/main/resources/META-INF/services.
Ao implementar a interface TimeStampGenerator, os seguintes métodos devem ser implementados:
17
Capítulo 3. Usando um carimbo...
public void initialize(byte[] content, PrivateKey privateKey, Certificate[] certificates) throws CertificateCoreE
}
public void validateTimeStamp(byte[] content, byte[] response)
throws CertificateCoreException {
}
public byte[] generateTimeStamp() throws CertificateCoreException {
return null;
}
No método initialize vamos apenas atribuir o conteúdo this.content = content;
O método validateTimeStamp vamos manter o mesmo código da implementação padrão:
TimeStampOperator timeStampOperator = new TimeStampOperator();
timeStampOperator.validate(content, response);
O método generateTimeStamp() é o método central da nossa implementação. Nele vamos conectar à servlet
e enviar o conteúdo sobre o qual é requisitado o carimbo de tempo. Como em nossa aplicação usamos servlet,
vamos começar estabelecendo a conexão com a URL e, a seguir, usar o método POST.
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type","application/octet-stream");
A urlString em nosso caso será: http://localhost:8080/AppletCustomizadaWeb/carimbo.
Escrevemos o seguinte conteúdo no request:
OutputStream os = connection.getOutputStream();
os.write(content);
os.flush();
os.close();
Após estabelecidos os paramêtros da conexão, testamos os status HTTP que nos indica se a solicitação foi
bem sucedida. O primeiro status que testamos é o 200. Esse status indica o sucesso na requisição, e assim
poderemos ler no response o carimbo de tempo restornado. Esse mesmo carimbo será ainda validado pelo
método validateTimeStamp com o conteúdo na instância local. Essa validação sempre ocorre, e, se houve
qualquer alteração no arquivo ou carimbo, este é invalidado.
int status = connection.getResponseCode();
if (status == 200) {
if (connection.getContentType().equals("application/octet-stream")) {
InputStream is = connection.getInputStream();
timestamp = IOUtils.toByteArray(is);
18
Criando sua própria implementação de TimeStampGenerator
is.close();
}
}
Para os casos de erro, trataremos os status HTTP 500 - Internal Server Error, 401 - Unauthorized e 403 - Access
to the requested resource has been denied. Abaixo, veja como ficará o código da nossa classe:
@Priority(Priority.MAX_PRIORITY)
public class MyTimestampGeneratorImpl implements TimeStampGenerator {
private static final Logger logger = LoggerFactory.getLogger(MyTimestampGeneratorImpl.class);
private byte[] content;
public void initialize(byte[] content, PrivateKey privateKey,
Certificate[] certificates) throws CertificateCoreException {
this.content = content;
}
public byte[] generateTimeStamp() throws CertificateCoreException {
byte[] timestamp = null;
HttpURLConnection connection = null;
try {
// Cria a conexao com o servico que requisita o carimbo de Tempo
URL url = new URL("http://localhost:8080/AppletCustomizadaWeb/carimbo");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type","application/octet-stream");
// Envia o conteudo
OutputStream os = connection.getOutputStream();
os.write(content);
os.flush();
os.close();
// Trata o status da conexao
int status = connection.getResponseCode();
if (status == 200) {
if (connection.getContentType().equals("application/octet-stream")) {
InputStream is = connection.getInputStream();
timestamp = IOUtils.toByteArray(is);
is.close();
}
}
if (status == 500) {
if (connection.getContentType().equals("text/plain")) {
String message = IOUtils.toString(connection.getErrorStream());
throw new CertificateCoreException(message);
19
Capítulo 3. Usando um carimbo...
}
}
if (status == 403){
throw new CertificateCoreException("HTTP Status 403 - JBWEB000015: Access
to the requested resource has been denied");
}
if (status == 401){
throw new CertificateCoreException("HTTP Status 401");
}
if (timestamp == null){
throw new CertificateCoreException("Carimbo de Tempo n#o foi gerado");
}
} catch ( ConnectException e) {
throw new CertificateCoreException("Erro ao conectar ao servico que solicita
carimbo de tempo");
} catch ( IOException e) {
throw new RuntimeException(e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return timestamp;
}
public void validateTimeStamp(byte[] content, byte[] response)
throws CertificateCoreException {
TimeStampOperator timeStampOperator = new TimeStampOperator();
timeStampOperator.validate(content, response);
}
}
Além disso, na classe App, devemos modificar a política que ajustamos para utilizar a política de Assinatura Digital
de Referência Básica versão 2.1, para utilizar a política de Assinatura Digital de Referência Temporal versão 2.1:
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RT_CADES_2_1);
A organização da AppletCustomizada ficara assim:
AppletCustomizada
### pom.xml
### src
### main
20
#
#
### java
#
### br
#
#
### gov
Adicionando segurança
#
#
### frameworkdemoiselle
#
#
### certificate
#
#
#
#
#
#
### App.java
#
#
### MyTimestampGeneratorImpl.java
### sample
### applet
#
### resources
### test
### java
### resources
3.4. Adicionando segurança
Por fim vamos adicionar segurança à nossa aplicação. É importante que o serviço disponível (seja usando Servlet,
REST ou WebService) esteja sob o contexto de segurança da aplicação. Com isso, o serviço só poderá ser acessado
pelo usuário autorizado pela aplicação.
Em nosso exemplo usaremos a autenticação JAAS do tipo FORM em nosso Servidor Jboss EAP6.
3.4.1. Configurando o Jboss EAP 6
Acesse JBOSS_HOME/bin e execute o add-user crie um usuário de aplicação pertencente ao grupo admin,
e adicione ao realm "ApplicationRealm". Para mais detalhes consulte a documentação add-user utility [https://
docs.jboss.org/author/display/AS71/add-user+utility].
3.4.2. Configurando a aplicação Web
Crie o arquivo web.xml na pasta webapp/WEB-INF do seu projeto AppletCustomizadaWeb.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/webapp_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/
web-app_3_0.xsd"
version="3.0">
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin</web-resource-name>
<url-pattern>/applet.html</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>Only allow users from following roles</description>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/index.html</form-error-page>
</form-login-config>
</login-config>
21
Capítulo 3. Usando um carimbo...
<security-role>
<role-name>admin</role-name>
</security-role>
</web-app>
3.4.3. Criando as páginas HTML
Crie em seu projeto Web AppletCustomizadaWeb as duas páginas abaixo.
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Exemplo de Assinatura de Documento</title>
<link href="css/default.css" rel="stylesheet">
</head>
<body>
<form id="form-login" method="post" action="j_security_check">
<div>
<h2>Applet Exemplo de Assinatura Digital</h2>
</div>
<label for="username">Login</label>
<input id="username" type="text" name="j_username">
<label for="password">Senha</label>
<input id="password" type="password" name="j_password">
<button id="login" type="submit" value="Login">Entrar</button>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Exemplo de Assinatura de Documento</title>
<link href="css/default.css" rel="stylesheet">
</head>
<body>
<div>
<h2>Applet Exemplo de Assinatura Digital</h2>
</div>
<div>
<a href="applet.html">Assinar Documento</a>
</div>
22
Configurando o Servlet
</body>
</html>
3.4.4. Configurando o Servlet
Adicione a anotação de sergurança à classe TimestampGeneratorServlet para que as definições de
segurança sejam aplicadas.
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "admin"))
Ao final o seu projeto ficará com a seguinte estrutura de diretórios:
AppletCustomizadoWeb
### pom.xml
### src
###
#
#
#
#
#
#
#
#
#
#
#
#
#
#
main
### java
#
### br
#
### gov
#
### frameworkdemoiselle
#
### certificate
#
### sample
#
### TimestampGeneratorServlet.java
### resources
### webapp
### css
#
### default.css
### WEB-INF
#
### web.xml
### applet.html
#
#
#
#
#
#
###
###
###
###
###
###
AppletCustomizada-1.0.0-assinado.jar
bcmail-jdk15on-1.51-assinado.jar
bcpkix-jdk15on-1.51-assinado.jar
bcprov-jdk15on-1.51-assinado.jar
commons-io-1.3.2-assinado.jar
demoiselle-certificate-applet-2.0.0-SNAPSHOT-assinado.jar
#
#
### demoiselle-certificate-ca-icpbrasil-2.0.0-SNAPSHOT-assinado.jar
### demoiselle-certificate-core-2.0.0-SNAPSHOT-assinado.jar
#
#
### demoiselle-certificate-criptography-2.0.0-SNAPSHOT-assinado.jar
### demoiselle-certificate-policy-engine-2.0.0-SNAPSHOT-assinado.jar
#
#
### demoiselle-certificate-signer-2.0.0-SNAPSHOT-assinado.jar
### demoiselle-certificate-timestamp-2.0.0-SNAPSHOT-assinado.jar
#
### index.html
#
#
### log4j-1.2.17-assinado.jar
### login.html
#
#
### plugin-assinado.jar
### slf4j-api-1.6.1-assinado.jar
#
### slf4j-log4j12-1.6.1-assinado.jar
### test
### java
23
Capítulo 3. Usando um carimbo...
### resources
24
Apêndice A. Repositório dos JARs
Na tabela 2.1 listamos alguns arquivos .jar, que devem ser assinados para que a applet customizada funcione. Na
tabela abaixo informamos onde podemos encontrar os arquivos requisitados.
Tabela A.1. Repositório dos jars
Arquivo
Download
demoiselle-certificate-*.jar
Todos
Demoiselle
no
seu
os
arquivos
do
Certificate
podem
repositório
componente
ser
no
obtidos
Source
Forge
[http://demoiselle.sourceforge.net/repository/
release/br/gov/frameworkdemoiselle/component/]
bcprov-jdk15on-1.51.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=org/bouncycastle/bcprovjdk15on/1.51/bcprov-jdk15on-1.51.jar]
bcpkix-jdk15on-1.51.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=org/bouncycastle/bcpkixjdk15on/1.51/bcpkix-jdk15on-1.51.jar]
bcmail-jdk15on-1.51.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=org/bouncycastle/bcmailjdk15on/1.51/bcmail-jdk15on-1.51.jar]
commons-io-1.3.2.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=commons-io/commonsio/1.3.2/commons-io-1.3.2.jar]
log4j-1.2.17.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=log4j/log4j/1.2.17/
log4j-1.2.17.jar]
slf4j-api-1.6.1.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=org/slf4j/slf4j-api/1.6.1/slf4japi-1.6.1.jar]
slf4j-log4j12-1.6.1.jar
http://search.maven.org
[http://search.maven.org/
remotecontent?filepath=org/slf4j/slf4j-log4j12/1.6.1/
slf4j-log4j12-1.6.1.jar]
plugin.jar
Esse arquivo é copiado de JAVA_HOME/jre/lib
25
26
Apêndice B. Assinando JARs com
certificado auto-assinado
No capítulo 2 comentamos que, por motivos de segurança, é preciso assinar todos os arquivos .jar necessários
para a execução da applet. Para assinar esse tipo de arquivo é necessário um certificado para assinatura de código.
Porém, é provável que os desenvolvedores não tenham esse tipo de certificado disponível para fazer testes locais
antes de implementar determinado recurso em uma aplicação em produção, o que é uma prática comum.
Por isso, nesse apêndice mostraremos como gerar um certificado que pode assinar arquivos .jar, e também como
assiná-los. Enfatizamos que esse certificado só deve ser utilizado no ambiente de desenvolvimento, para testes e
afins, e nunca em ambientes de produção.
B.1. Criando um certificado
Primeiramente criaremos o keystore que armazenará o certificado digital. A ferramenta keytool será utilizada para
criação simultãnea do keystore e do certificado digital que identificaremos pelo alias applet_alias.
keytool -genkey -alias applet_alias -keyalg RSA -keypass changeit -storepass changeit -keystore
applet_keystore.jks
Na sequência serão solicitadas algumas informações do certificado:
What is your first and last name?
[Unknown]: Framework Demoiselle
What is the name of your organizational unit?
[Unknown]: Demoiselle
What is the name of your organization?
[Unknown]: Demoiselle
What is the name of your City or Locality?
[Unknown]: Salvador
What is the name of your State or Province?
[Unknown]: BA
What is the two-letter country code for this unit?
[Unknown]: BR
Is CN=Framework Demoiselle, OU=Demoiselle, O=Demoiselle, L=Salvador, ST=BA, C=BR correct?
[no]: yes
Será criado o keystore JKS de nome applet_keystore.jks que contém um certificado auto assinado. Seu
par de chaves será identificado pelo alias applet_alias.
B.2. Assinando um jar com certificado auto-assinado
Neste momento a ferramenta jarsigner será utilizada para assinar todos os jars da aplicação. Portanto será
necessário informar a localização do keystore, o nome do jar assinado, o nome do jar original e o alias do certificado:
jarsigner -keystore applet_keystore.jks -signedjar meujar-assinado.jar meujar.jar applet_alias
27
Apêndice B. Assinando JARs co...
Importante
Note que o jar assinado (meujar-assinado.jar) define o nome do arquivo jar que será criado,
diferente do nome original do jar (meujar.jar).
Dentro do jar, na pasta META-INF, foram inseridos os aquivos APPLET_A.RSA, APPLET_A.SF e MANIFEST.MF,
que possuem informações como o algoritmo de criptografia utilizado e a chave pública do certificado.
Para verificar a assinatura do jar utilize o comando jarsigner conforme abaixo:
jarsigner -verify -keystore applet_keystore.jks meujar-assinado.jar
28