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).
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.
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.
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 |
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 |
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 Iteratorit = 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(); } }