FrameworkDemoiselle.gov.brCommunity Documentation
Este componente provê mecanismos de assinatura digital baseado nas normas ICP-Brasil e implementa mecanismos de assinatura digital em dois formatos: PKCS1 e PKCS7. A maior diferença entre esses dois mecanismos está na forma de envelopamento da assinatura digital, onde o PKCS1 não possui um formato de envelope, sendo o resultado da operação de assinatura a própria assinatura, já o PKCS7 possui um formato de retorno que pode ser binário (especificado na RFC5126) ou XML.
A interface org.demoiselle.signer.policy.impl.cades.Signer
especifica o comportamento padrão dos mecanismos de GERAÇÃO de assinatura digital.
O componente especializa essa interface em mais duas, são elas:
org.demoiselle.signer.policy.impl.cades.pkcs1.PKCS1Signer
para implementações de mecanismos PKCS1 e
org.demoiselle.signer.policy.impl.cades.pkcs7.PKCS7Signer
para implementações de mecanismos de envelopamento PKCS7.
Para as funções de VALIDAÇÃO A interface org.demoiselle.signer.policy.impl.cades.Checker
especifica as funções de validação de assinatura digital.
O componente especializa essa interface em mais duas, são elas:
org.demoiselle.signer.policy.impl.cades.pkcs1.PKCS1Checker
para implementações de mecanismos PKCS1 e
org.demoiselle.signer.policy.impl.cades.pkcs7.PKCS7Checker
para implementações de mecanismos de envelopamento PKCS7.
Este componente, até a presente versão, permite assinar dados representados por um array de bytes. Então se for necessário a assinatura de um arquivo, por exemplo, a aplicação deverá montar um array de bytes com o conteúdo do arquivo e enviar este para o componente poder assiná-lo. Também é possível enviar apenas o Hash já calculado deste arquivo. Veja nas sessões abaixo como fazê-los.
Para assinar um dado através do componente demoiselle-signer é preciso executar alguns passos.
A seguir temos um fragmento de código que demonstra uma assinatura no formato PKCS1.
/* conteudo a ser assinado */
byte[] content = "conteudo a ser assinado".getBytes();
/* chave privada */
PrivateKey chavePrivada = getPrivateKey(); /* implementar metodo para pegar chave privada */
/* construindo um objeto PKCS1Signer atraves da fabrica */
PKCS1Signer signer = PKCS1Factory.getInstance().factory();
/* Configurando o algoritmo */
signer.setAlgorithm(SignerAlgorithmEnum.SHA1withRSA);
/* Configurando a chave privada */
signer.setPrivateKey(chavePrivada);
/* Assinando um conjunto de bytes */
byte[] signature = signer.doSign(content);
O formato PKCS#7 define o tipo de arquivo para assinatura. Já a ICP-Brasil define um conjunto mínimo de informações básicas para as assinaturas digitais para o padrão CAdES. São elas: Tipo de conteúdo, data da assinatura, algoritmo de resumo aplicado e a política de assinatura. O componente policy-impl-cades já monta o pacote final com três atributos obrigatórios: tipo de conteúdo, data da assinatura e o algoritmo de resumo. Então, para montar um PKCS7 padrão CAdEs ICP-Brasil é necessário informar ao objeto PKCS7Signer qual a política de assinatura a ser aplicada. Uma das formas de gerar a assinatura digital é criar um novo arquivo com a extensão .p7s (PKCS#7) que contém apenas a assinatura, assim independente do arquivo original. Porém para validação da assinatura, será necessário informar tanto o arquivo de assinatura quanto o conteúdo original.
A seguir temos um fragmento de código que demonstra a utilização do pacote PKCS7 padrão.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificates(certificateChain);
signer.setPrivateKey(privateKey);
byte[] signature = signer.doDetachedSign(this.content);
A seguir temos um fragmento de código que demonstra a utilização do pacote PKCS7 padrão com informação da política de assinatura. Neste caso podemos escolher uma das políticas (em vigor) que já acompanham o componente e referem-se à Assinatura Digital padrão CADES.
AD_RB_CADES_2_3
Refere-se à Assinatura Digital de Referência Básica versão 2.3;
AD_RT_CADES_2_3
Refere-se à Assinatura Digital de Referência Temporal (com carimbo de tempo) versão 2.3;
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificates(certificateChain);
signer.setPrivateKey(privateKey);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_3);
byte[] signature = signer.doDetachedSign(this.content);
Caso não seja especificada nenhuma política, o componente assumirá a política padrão AD_RB_CADES_2_3.
Identica ao formato apresentado anteriormente, outra das formas de gerar a assinatura digital é incluir todo o conteúdo assinado no novo arquivo com a extensão .p7s (PKCS#7) Desta forma, tanto a assinatura quanto o conteúdo estarão dentro deste arquivo. A sua vantagem é que para validação da assinatura, basta enviar somente este arquivo. Porém, caso o arquivo original seja descartado, para ter acesso ao mesmo, será necessário o uso de um software especializado(como o próprio Demoiselle-Signer)
A seguir temos um fragmento de código que demonstra a utilização do pacote PKCS7 com o conteúdo anexado.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificates(certificateChain);
signer.setPrivateKey(privateKey);
byte[] signature = signer.doAttachedSign(fileToSign);
Este procedimento visa facilitar a geração de assinaturas digitais em aplicações onde pode haver restrição de trafegar todo o conteúdo do arquivo pela rede, sendo necessário apenas o tráfego dos bytes do resumo do conteúdo original (HASH). Neste caso, é necessário gerar o HASH do conteúdo a ser assinado e passar para o assinador. Ao gerar o HASH, é importante dar atenção ao algoritmo a ser usado, pois na validação da assinatura será considerado o algoritmo da política escolhida. Então, para que esse procedimento funcione corretamente, é necessário escolher o algoritmo do HASH igual ao algoritmo da assinatura digital.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
/* Gerando o HASH */
java.security.MessageDigest md = java.security.MessageDigest
.getInstance(DigestAlgorithmEnum.SHA_256.getAlgorithm());
byte[] hash = md.digest(content);
/* Gerando a assinatura a partir do HASH gerado anteriormente */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificate(certificate);
signer.setPrivateKey(privateKey);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_3);
byte[] signature = signer.doHashSign(hash);
Este procedimento gera o pacote PKCS7 idêntico ao pacote gerado pelo exemplo do tópico 2.2
Por definição, para gerar uma co-assinatura, basta que vários assinantes assinem o mesmo arquivo, gerando assim vários arquivos de assinaturas que estão relacionados ao arquivo original. O componente também oferece uma funcionalidade de co-assinar um arquivo gerando um único arquivo com todas assinaturas. Para isso quando é feita a chamada ao componente é necessário informar tanto o arquivo original (ou seu hash) quanto o arquivo de assinatura que contém a(s) assinatura(s) anterior(es). Conforme exemplificamos abaixo.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
byte[] signatureFile = readContent("fileSignature.p7s"); /* implementar metodo de leitura de arquivo */
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificate(certificate);
signer.setPrivateKey(privateKey);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_3);
byte[] signature = signer.doDetachedSign(content, signatureFile);
Assim, como nas outras forma de gerar a assinatura, é possível fazer o envio do Hash do conteúdo já calculado. Veja no exemplo abaixo:
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
byte[] signatureFile = readContent("fileSignature.p7s"); /* implementar metodo de leitura de arquivo */
/* Gerando o HASH */
java.security.MessageDigest md = java.security.MessageDigest
.getInstance(DigestAlgorithmEnum.SHA_256.getAlgorithm());
byte[] hash = md.digest(content);
PKCS7Signer signer = PKCS7Factory.getInstance().factoryDefault();
signer.setCertificate(certificate);
signer.setPrivateKey(privateKey);
signer.setSignaturePolicy(PolicyFactory.Policies.AD_RB_CADES_2_3);
byte[] signature = signer.doHashCoSign(hash, signatureFile);
Como foi visto nas seções anteriores, um do modos de gerar a assinatura é criando um arquivo separado do conteúdo original, e a forma de validação está no fragmento de código abaixo.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
byte[] signature = readContent("texto.pkcs7"); /* implementar metodo de leitura de arquivo */
CAdESChecker checker = new CAdESChecker();
List<SignatureInformations> signaturesInfo = checker.checkDetachedSignature(content, signature);
O retorno é um objeto do tipo org.demoiselle.signer.policy.impl.cades.SignatureInformations que possui os seguintes atributos
public class SignatureInformations {
private LinkedList<X509Certificate> chain; // cadeia do certificado que gerou a assinatura
private Date signDate; // data do equipamento no momento da gera��o das assinatura (n�o tem validade legal)
private Timestamp timeStampSigner = null; // Carimbo de tempo da assinatura, quando a politica utilizada permitir
private SignaturePolicy signaturePolicy; // Politica ICP-BRASIL usada para gera��o da assinatura
private LinkedList<String> validatorErrors = new LinkedList<String>(); // Lista de erros que por ventura possam ter sido encontrados na valida��o da assinatura
A seguir temos um fragmento de código que demonstra a validação uma assinatura PKCS7 com o conteúdo anexado.
byte[] signature = readContent("texto.pkcs7"); /* implementar metodo de leitura de arquivo */
CAdESChecker checker = new CAdESChecker();
List<SignatureInformations> signaturesInfo = checker.checkAttachedSignature(signature);
Da mesma forma que possibilitamos a criação da assinatura enviando o resumo (hash) calculado do conteúdo, podemos também fazer a validação da mesma forma. Assim como na geração, é preciso saber qual foi o algoritimo de resumo (hash) que foi usado para gerar a assinatura, pois o mesmo deve ser informado para o método de validação. A seguir temos um fragmento de código que demonstra esta validação.
byte[] content = readContent("texto.txt"); /* implementar metodo de leitura de arquivo */
byte[] signature = readContent("texto.pkcs7"); /* implementar metodo de leitura de arquiv
CAdESChecker checker = new CAdESChecker();
// gera o hash do arquivo que foi assinado
md = java.security.MessageDigest
.getInstance(DigestAlgorithmEnum.SHA_256.getAlgorithm());
byte[] hash = md.digest(content);
List<SignatureInformations> signaturesInfo = checker.checkSignatureByHash(SignerAlgorithmEnum.SHA256withRSA.getOIDAlgorithmHash(), hash, signature);
Como é possível que um mesmo arquivo possa contar várias assinaturas (PAdES principalmente), só será gerada exceção quando a assinatura estiver comprometida Nos demais casos, o Demoiselle-Signer irá devolver o resultado numa lista de objetos SignatureInformations. Essa classe contém os seguintes atributos:
chain;
Lista X509Certificate com a cadeia completa do certificado do Assinante
signDate
A data do equipamento onde foi gerada a assinatura, e serve apenas como referência, não tem nenhuma validade legal
timeStampSigner
É o carimbo do Tempo (Timestamp) incluído na Assinatura, é a prova legal da data e hora que a Assinatura foi gerada.
signaturePolicy;
A política (SignaturePolicy) que foi usada para gerar a Assinatura
notAfter;
A data de validade do Certificado do Assinante
validatorWarnins
Lista de Avisos. A assinatura pode estar correta mas não foi possível verificar algum atributo exigido por uma política da ICP-Brasil, que serão listados aqui
validatorErrors
Lista de Erros. A assinatura pode estar correta mas não foi possível verificar alguma condição de validação exigida pela ICP-Brasil
invalidSignature
valor booleano, que indica que Assinatura não está válida
icpBrasilcertificate
BasicCertificate do Assinante
Cabe ao sistema com base nos avisos ou erros, aceitar ou não a Assinatura. Apesar de existirem as políticas, qualquer tipo de Assinatura gerada com um certificado ICP-Brasil tem validade legal. A seguir temos um fragmento de código que demonstra esta validação.
List<SignatureInformations> signaturesInfo = checker
.checkDetachedSignature(fileToVerify, signatureFile);
if (signaturesInfo != null) {
System.out.println("A assinatura foi validada. e retornou resultados");
for (SignatureInformations si : signaturesInfo) {
System.out.println(si.getSignDate());
if (si.getTimeStampSigner() != null) {
System.out.println("Serial"
+ si.getTimeStampSigner().toString());
}
System.out.println("informa��es do assinante:");
BasicCertificate certificate = si.getIcpBrasilcertificate();
if (!certificate.isCACertificate()) {
if (certificate.hasCertificatePF()) {
System.out.println("CPF: "+certificate.getICPBRCertificatePF().getCPF());
System.out.println("Titulo de Eleitor: "+certificate.getICPBRCertificatePF().getElectoralDocument());
}
if (certificate.hasCertificatePJ()) {
System.out.println("CNPJ: "+certificate.getICPBRCertificatePJ().getCNPJ());
}
}
// Carimbo do tempo
if(si.getTimeStampSigner()!= null) {
System.out.println(si.getTimeStampSigner().toString());
}
// A assinatura pode estar correta mas n�o foi poss�vel verificar algum atributo exigido pela ICP-Brasil
for (String valErr : si.getValidatorErrors()) {
System.err.println("++++++++++++++ ERROS ++++++++++++++++++");
System.err.println(valErr);
}
//A assinatura pode estar correta mas n�o foi poss�vel verificar alguma condi��o de valida��o exigida pela ICP-Brasil
for (String valWarn : si.getValidatorWarnins()) {
System.err.println("++++++++++++++ AVISOS ++++++++++++++++++");
System.err.println(valWarn);
}
}
A seguir temos um fragmento de código que demonstra a extração (recuperação) do conteúdo de um arquivo anexado à uma assinatura PKCS7. Essa funcionalidade pode ser útil quando é necessário retirar/extrair o conteúdo assinado, pois no formato anexado (attached) o conteúdo está empacotado na assinatura. Esta funcionalidade também permite que seja feita a validação da assinatura no momento da extração.
byte[] attachedSignature = readContent("texto.pkcs7"); /* implementar metodo de leitura de arquivo */
CAdESChecker checker = new CAdESChecker();
/* Para extrair o conteudo original validando a assinatura */
byte[] content = checker.getAttached(attachedSignature, true);
/* Para extrair o conteudo original sem validar a assinatura */
byte[] content = checker.getAttached(attachedSignature, false);
No repositório do componente no GitHub há um código de testes unitários para os exemplos acima, neste link