Arquitetura do dispositivo de políticas de assinatura

O dispositivo visa assegurar o cumprimento das diferentes políticas de assinatura. Ele está implementado como a composição de dois dispositivos: o primeiro é destinado a criar envelopes ContentInfo em estrita conformidade com a política selecionada; o segundo é destinado a verificar se um determinado envelope ContentInfo atende à uma política em particular. O dispositivo prevê não apenas que a política apropriada tenha sido selecionada independentemente como também que, no caso de um envelope já existente e que precisa ter sua conformidade verificada, a política apropriada tenha sido determinada (por exemplo, verificando o atributo assinado SignaturePolicy).

O dispositivo de composição do envelope criptográfico

Esse subsistema foi projetado tendo em mente a delegação da responsabilidade das diferentes tarefas prévias à composição de um determinado envelope, como por exemplo: obter o time-stamp token, efetuar a assinatura digital, obter as LCR's apropriadas, etc. Tais responsabilidades são delegadas a callbacks, a saber:

O diagrama ilustra as duas políticas básicas implementadas: CAdESBESPolicy e CAdESTPolicy. A implementação de outras políticas, por exemplo, as da ICP-Brasil, requer a especialização da classe PolicyProcessor. O método buildDocument, sobrecarregado, serve tanto para criar um envelope para um contenúdo assinado uma única vez quanto para adicionar uma assinatura a um envelope já assinado.

 

 

O dispositivo de verificação do envelope criptográfico

Este dispositivo é simplesmente uma especialização do Dispositivo de validação genérico definido para a aplicação e requer a especialização de RulesProcessor. No modelo é a classe PolicyProcessor, já descrita, a responsável por essa funcionalidade. Como diferentes políticas de assinatura devem requerer diferentes regras de validação, PolicyProcessor é ainda uma classe abstrata, que cada implementação de política deve especializar. Como descrito no documento Dispositivo de validação, é esperado que todas as implementações sejam registradas em RulesProcessor. Como já dito, o design do dispositivo pressupõe os diferentes conjuntos de regras, descritos mais adiante, foram registrados previa e independentemente em ValidationRules.

O desig do dispositivo prevê regras efetivamente atômicas, de modo a existir um único caminho de execução e, portanto, uma única saída possível. A existência dos dois métodos verifyDocument(), cuja implementação simplesmente redireciona a execução para o ancestral execute(), está disponível não apenas como um atalho, mas serve também ao seguinte propósito. Suponhamos uma verificação com erros críticos para a verificação criptográfica, como por exemplo o não fornecimento do certificado do assinante ou do próprio documento assinado. Tais eventos geram erros recuperáveis (o método isRecoverable() da implementação da regra Rule retorna true). Isso permite que a aplicação solicite do usuário o fornecimento dos dados necessários, reexecutando a validação, agora somente para aquele subconjunto de regras com execução mal sucedida. Isso requer que a implementação de Rule, na presença de um erro recuperável, devolva o ponteiro this como retorno ao método getCause() da saída RuleOutput. Para que isso seja realizado, é necessário que todas as implementações de Rule especializem Throwable.

Uma outra premissa do design é que as especializações de PolicyProcessor sejam responsáveis por recuperar da fábrica ValidationRules os conjuntos de regras aplicáveis à política. Como essas políticas são também registráveis, de modo a garantir flexibilidade ao aplicativo, deve-se atentar para a necessidade de o seu registro ser posterior ao registro das regras, de modo a evitar erro de tempo de execução.

 

 

Implementações conhecidas das callbacks

 

 

Validações implementadas

 

Validações genéricas registradas como "GENERIC-PKI"

Classe Regra Saída Recuperável
VerifyContentType O tipo de conteúdo do documento deve ser um SignedData Erro fatal Não
VerifyVersion O campo version da estrutura SignedData deve ser igual a 3. Erro Não
VerifyContent O conteúdo do campo eContent da estrutura encapContentInfo deveria estar presente. Alerta. Sim
VerifyCommitment O atributo assinado CommitmentTypeIndication deveria estar presente. Alerta Não
VerifyCRLPresence A LCR correspondente ao certificado do assinante deve estar disponível para consulta. Alerta Sim
VerifyContentTypePresence O atributo assinado ContentType deve estar presente. Erro Não
VerifyMessageDigestPresence O atributo assinado MessageDigest deve estar presente. Erro Não
VerifySidPresence O campo sid deve estar presente. Erro Não
VerifySigningCertificatePresence Um dos atributos assinados ESSSigningCertificate ou ESSSigningCertificateV2 deve estar presente. Erro Não
VerifyContentTypeConsistency O atributo assinado ContentType deve ser igual ao conteúdo do campo eContentType. Erro Não
VerifyMessageDigest O atributo assinado MessageDigest deve ser igual ao hash do campo eContent. Erro Não
VerifySid O campo sid deve ser compatível com o titular do certificado digital fornecido. Erro Sim
VerifySigningCertificate Um dos atributos assinados ESSSigningCertificate ou ESSSigningCertificateV2 deve ser compatível com o titular do certificado digital. Erro Sim
VerifySignature A verificação criptográfica da assinatura deve estar em conformidade com a RFC 3852. Erro fatal Não

 

 

Validações registradas como CADES-BES

Classe Regra Saída Recuperável
VerifyCertificate O campo certificates deveria estar presente e conter o certificado digital do assinante. Alerta Sim
VerifySignatureAlgorithm O campo signatureAlgorithm deve estar presente e ter um dos seguintes valores: DSA com SHA-1, RSA com SHA-1 ou RSA com MD5. Erro fatal Não
VerifySigningTime O atributo assinado SigningTime deveria estar presente. Alerta Não
VerifyCommitmentType O atributo assinado CommitmentTypeIndication deveria conter um dos seguintes valores: Prova de origem, Prova de recebimento, Prova de entrega, Prova de envio ou Prova de aprovação. Alerta Não
VerifyValidity O certificado digital do assinante deve estar em vigor quando da assinatura digital, caso o instante da assinatura seja conhecido. Isto é, o instante t de uso do certificado deve atender às condições: (Certificate.Validity.notBefore < t < TBSCertificate.Validity.notAfter) ∧ (TBSCertificate.serialNumber ∉ TBSCertList.revokedCertificates ∀ TBSCertList.thisUpdate < t < TBSCertList.nextUpdate). Erro fatal Sim/não

 

 

Validações registradas como CADES-T

Classe Regra Saída Recuperável
VerifySigningTimeConsistency O valor do atributo SigningTime, se presente, deve ser menor ou igual ao valor do TST contido no atributo SignatureTimeStamp. Erro Não
VerifySignatureTimeStamp O atributo não assinado SignatureTimeStamp deve estar presente. Erro Fatal Não

 

A seguir um exemplo de uso do mecanismo de validação da política de assinatura:

public class TestCAdESBESValidation {

	public static void main(String[] args) {
		
		// Gets the envelope
		TestCMSParser parser = new TestCMSParser();
		parser.registerComponents();
		FileInputStream in;
		ContentInfo envelope = null;
		try {
			in = new FileInputStream(FILE);
			try {
				envelope = parser.parseEnvelope(in);
			}
			finally {
				in.close();
			}
		}
		catch (RuntimeException e) {
			System.out.println("Parse error:");
			e.printStackTrace();
			e.getCause().printStackTrace();
		}
		catch (IOException e) {
			System.out.println("I/O error:");
			e.printStackTrace();
		}
		
		// Validates the envelope
		TestCAdESBESValidation validator = new TestCAdESBESValidation();
		String implementation = validator.registerComponents();
		ValidationOutputs output = validator.validate(envelope, implementation);
		
		// Now we may try to recover some errors
		Iterator it = output.iterator();
		while (it.hasNext()) {
			RuleOutput out = it.next();
			if (out.getCondition() != 0) {
				SignaturePolicyRule rule = (SignaturePolicyRule)out.getCause();
				if (rule.recoverableException() != null) {
					Throwable exception = (Throwable)rule.recoverableException();
					if (exception instanceof MissingCertificateException)
						System.out.println("try to recover the certificate");
					else if (rule.recoverableException() instanceof MissingCRLException)
						System.out.println("try to recover the CRL");
					else if (rule.recoverableException() instanceof MissingContentException)
						System.out.println("try to recover the content");
				}
			}
		}
		
		// If we could recover some errors and update the envelope, we may run validation again an 
		// then do something with the new output
		System.out.println(output.toString());

	}
	
	
	public String registerComponents() {
	
		ValidationRules genRules = new ValidationRules();
		genRules.add(new VerifyCommitment());
		genRules.add(new VerifyContent());
		genRules.add(new VerifyContentType());
		genRules.add(new VerifyContentTypeConsistency());
		genRules.add(new VerifyContentTypePresence());
		genRules.add(new VerifyCRLPresence());
		genRules.add(new VerifyMessageDigest());
		genRules.add(new VerifyMessageDigestPresence());
		genRules.add(new VerifySid());
		genRules.add(new VerifySidPresence());
		genRules.add(new VerifySignature());
		genRules.add(new VerifySigningCertificate());
		genRules.add(new VerifySigningCertificatePresence());
		genRules.add(new VerifyVersion());
		
		ValidationRules besRules = new ValidationRules();
		besRules.add(new VerifyCertificate());
		besRules.add(new VerifySignatureAlgorithm());
		besRules.add(new VerifySigningTime());
		besRules.add(new VerifyCommitmentType());
		besRules.add(new VerifyValidity());
		
		HashSet references = new HashSet();
		references.add("GENERIC-PKI");
		references.add("CADES-BES");
		
		ValidationRules.register("GENERIC-PKI", genRules);
		ValidationRules.register("CADES-BES", besRules);
		CAdESBESPolicy.register(references);
		
		RulesProcessor.register("CAdES-BES", new CAdESBESPolicy());
		
		return "CAdES-BES";
	}
	
	public ValidationOutputs validate(ContentInfo envelope, String implementationName) {

		// Gets the inputs to rules
		ValidationInputs inputs = new ValidationInputs();
		inputs.add(new CryptographicEnvelope(envelope.getContentType(), envelope.getContent()));

		// Gets the processor
		RulesProcessor implementation = RulesProcessor.getInstance(implementationName);
		implementation.setValidationInputs(inputs);
		
		// Runs the validation rules
		return implementation.execute();
	}
}