A idéia por trás do projeto do dispositivo consiste no fato de inúmeras LCR's terem vida suficientemente longa para serem reutilizadas dentro do seu prazo de validade, em especial as de autoridades certificadoras de nível elevado, que não emitem certificados para entidades finais, mas para outras AC's. Isso significa que, sendo necessária uma LCR, o sistema primeiro verifica nesse dispositivo persistente a existência de uma LCR em vigor. Se não for encontrada, ele a obtém da URL referida na extensão CRL Distribution Points do certificado do assinante.
O dispositivo foi projetado tendo em mente pelo menos duas alternativas para a persistência das LCR's: o próprio sistema de arquivos (FileSystemDatabase), no caso de versão desktop do aplicativo, e possivelmente um banco de dados para eventual versão web. Daí o projeto da classe CRLManager. Ela consiste simplesmente num índice para as LCR's armazenadas mantido num HashMap thread-safe (por definição um singleton, implementado como uma variável estática), onde a chave de acesso é o valor do campo issuer do emissor das LCR's. Assim, a classe prevê uma entrada para cada autoridade emissora. Como ao longo do tempo é esperada a coleta de várias LCR's, mas normalmente apenas uma em vigor (o dispositivo ainda não suporta LCR's emitidas em delta), cada entrada do emissor aponta para uma pilha CRLList de LCR's, onde a mais recente está sempre no topo. Isso simplifica o acesso. Como a implementação da pilha especializa java.util.ArrayDeque, a implementação deverá assegurar gravação thread-safe.
A efetiva persistência dos dados (tanto o índice para as LCR's quanto as próprias LCR's) é delegada para classes que implementem as interfaces CRLDbEventListener e CRLEventListener. A implementação deve registrar seu interesse nos vários eventos disparados na atualização dos índices (e das LCR's) através do método addEventListener(). Assim, cabe à implementação a decisão de quando (e como) persistir os dados eventualmente alterados. Os eventos databaseDestroy() e cRLDestroy() foram projetados tendo em vista assegurar que implementações com estratégia lazzy write tenham a oportunidade de salvar finalmente os dados quando da destruição de CRLManager e CRL pelo garbage collector: eles são disparados no seu evento finalize(). Por outro lado, o design do dispositivo prevê que somente a última LCR obtida para cada emissor (a do topo da pilha CRLList) resida em memória. Quando uma LCR é movida para uma posição secundária na pilha, isto é, quando uma nova LCR do emissor é colocada no seu topo, a implementação de CRLManager envia uma mensagem discard() à instância de CRL. Isso provoca o disparo de um evento cRLDiscarded(). Por outro lado, sempre que uma nova LCR é recebida, CRLManager envia uma mensagem setCRL() para a instância de CRL, o que dispara cRLReceived(). Sempre que uma LCR é necessária em memória (por exemplo, quando todo a versão corrente do banco de dados é carregada (numa seção estática anônima de CRLManager) e ela não está presente no objeto, um evento cRLNeeded() é disparado, de modo a permitir que a implementação a recupere da camada de persistência.
Por fim, o design prevê a delegação da responsabilidade de obter a LCR no URL apontado pela extensão CDP do certificado a uma implementação de CRLHttpGetter, que deve ser fornecida a CRLManager na sua instanciação. Pelo menos duas implementações foram imaginadas: DirectConnection, para conexões diretas à Internet, e ProxyConnection, para conexões através de servidores de proxy. Esta última implementação requer a atribuição dos parâmetros de conexão. Ambas as implementações dependem do componente HttpClient do projeto Jakarta Commons, a quem cabe efetivamente a conexão HTTP.
Uma última observação é relevante. CRLManager é responsável igualmente por obter a LCR do assinante armazenada num envelope de transporte SignedData. Neste caso, as LCR's extraídas não são persistidas, tanto porque isso não é necessário, já que elas estão armazenadas no envelope, como também porque possivelmente já não estejam em vigor.
A seguir um exemplo de uso do dispositivo:
public class TestCRLManager { public static void main(String[] args) { try { X509Certificate[] certs = TestCertificateValidation.getCertificateChain(); TestCRLManager manager = new TestCRLManager(); manager.registerComponents(); X509CRL[] crls = manager.getCRLs(certs); manager.doSomething(crls); } catch (Exception e) { e.printStackTrace(); e.getCause().printStackTrace(); } } public void registerComponents() { DatabaseLoader.register("FileSystemDatabase", new FileSystemCRLDatabase()); DatabaseLoader.setDefaultImplementation("FileSystemDatabase"); CRLHttpGetter.register("DirectConnection", new DirectConnection()); CRLHttpGetter.register("ProxyConnection", new ProxyConnection()); CRLHttpGetter.setDefaultImplementation("DirectConnection"); } public X509CRL[] getCRLs(X509Certificate[] chain) { CRLManager manager = new CRLManager(); DatabaseLoader persistence = DatabaseLoader.getInstance(); manager.addEventListener((CRLDbEventListener) persistence, (CRLEventListener) persistence); return manager.getCRLs(chain); } public void doSomething(X509CRL[] crls) { for (int i = 0; i < crls.length; i++) { System.out.println("CRL issued by: " + crls[i].getIssuerX500Principal().getName()); System.out.println("This update: " + crls[i].getThisUpdate()); } }