Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suporte a assinatura de arquivos grandes > 1.5GB #383

Open
yurix opened this issue Aug 18, 2023 · 0 comments
Open

Suporte a assinatura de arquivos grandes > 1.5GB #383

yurix opened this issue Aug 18, 2023 · 0 comments
Assignees
Labels
Milestone

Comments

@yurix
Copy link

yurix commented Aug 18, 2023

Estava substituindo a minha própria implementação de CAdEs pelo Demoiselle Signer quando me deparei com a exceção "java.lang.OutOfMemoryError: Java heap space" na assinatura de arquivos grandes. Minha solicitação é que na próxima versão o componente altere o seu design e permita o uso de Streams na leitura do conteúdo para assinatura e no armazenamento da saída (seja detached ou attached), evitando a todo custo a cópia de todo conteúdo para memória byte[].

Para contornar e suportar grandes arquivos fiz a seguinte alteração:

  1. Alteração do tipo de gerador de pacote CMS do ByteArray para o CMSProcessableFile.
CMSTypedData cmsTypedData;
// para assinatura do hash, content nulo
if (content == null) {
	cmsTypedData = new CMSAbsentContent();
} else {
	cmsTypedData = new CMSProcessableFile(content);
}

Observação: A Bouncycastle implementa de forma pública apenas estes dois, desta forma exigindo que API receba um File. Isso faz sentido, pois a partir de um arquivo é possível "consumir" vários InputStream, como me parece que é o caso. Entretanto, em termos de API me parece inadequado pois é melhor trabalhar com a abstração InputStream do que a File. Um caminho talvez para implementar desta forma seria criando uma CMSProcessableInputStream1 e trabalhando ela com as classes TeeInputStream da própria BouncyCastle. Como o tempo ficou muito curto para atender este refactoring, acabei seguindo um caminho mais simples.

  1. No trecho de geração dos atributos assinados ou não interceptei a geração do Atributo MessageDigest (Que trabalha todo em memória) por uma implementação de geração de hash por um stream. Abaixo o código da interceptação.
SignedOrUnsignedAttribute signedOrUnsignedAttribute = attributeFactory .factory(objectIdentifier.getValue());

if (signedOrUnsignedAttribute instanceof org.demoiselle.signer.policy.impl.cades.pkcs7.attribute.impl.MessageDigest) {
	StreamMessageDigest smd = new StreamMessageDigest();
	smd.initializeStream(this.pkcs1.getPrivateKey(), certificateChain, new FileInputStream(content), signaturePolicy, this.hash);
	signedOrUnsignedAttribute = smd;
} else {
	signedOrUnsignedAttribute.initialize(this.pkcs1.getPrivateKey(), certificateChain, null, signaturePolicy, this.hash);
}
  1. Abaixo o trecho da classe StreamMessageDigest, que é nada mais de uma alteração simples na classe MessageDigest.
@Override
	public Attribute getValue() {
		try {
			if (this.hash == null) {
				String hashAlgorithm = signaturePolicy.getSignPolicyHashAlg().getAlgorithm().getValue();
				if (this.contentInputStream!=null) {
					java.security.MessageDigest md = java.security.MessageDigest.getInstance(hashAlgorithm);

					byte[] buffer = new byte[8192]; // Tamanho do buffer para leitura dos dados
		            
		            int bytesRead;
		            while ((bytesRead = this.contentInputStream.read(buffer)) != -1) {
		                md.update(buffer, 0, bytesRead);
		            }
		            
		            this.hash = md.digest();
		            this.contentInputStream.close();
				} else {
					java.security.MessageDigest md = java.security.MessageDigest.getInstance(hashAlgorithm);
					this.hash = md.digest(content);
				}
			}
			return new Attribute(identifier, new DERSet(new DEROctetString(this.hash)));
		} catch (NoSuchAlgorithmException | IOException ex) {
			logger.info(ex.getMessage());
			return null;
		}
	}

Footnotes

  1. https://github.com/bcgit/bc-java/blob/1.72/pkix/src/main/java/org/bouncycastle/cms/CMSProcessableInputStream.java

@esaito esaito self-assigned this Aug 25, 2023
@esaito esaito added this to the 4.3.0 milestone Aug 25, 2023
@esaito esaito modified the milestones: 4.3.0, Backlog Sep 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants