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