Universidade Federal de Santa Catarina Departamento de Automaç
Transcrição
Universidade Federal de Santa Catarina Departamento de Automaç
Universidade Federal de Santa Catarina Departamento de Automação e Sistemas Grupo de Estudos de Agentes Móveis e Segurança RELATÓRIO TÉCNICO Implementação das Classes PRRepository e PathRegister do pacote AgentSec Rafael José Deitos Florianopólis, abril de 2004. 1 Figura 1: Notação do esquema Repositório Seguro de Resultados Parciais 1 Introdução No esquema de proteção de agentes móveis e das plataformas com as quais estes interagem, visando assegurar a integridade daqueles, o esquema proposto por Wangham em [1] suporta uma terceira técnica para resultados parciais recolhidos durante as viagens do agente: Repositório Seguro de Resultados Parciais. Além do repositório de resultados parciais, o esquema conta com uma outra técnica que consiste em um registrador de caminhos. O objetivo deste é o de prover proteção para a plataforma e proteção para os agentes móveis pois detecta as possı́veis violações da integridade do código e do estado do agente e ajuda ainda no processo de identificação de plataformas maliciosas. Através da implementação do Registrador de Caminhos, num âmbito mais geral, será implementado um Controle Social da Reputação das Plataformas. 2 2.1 Classe PRRepository Descrição Para agentes one-hop, a técnica de assinatura de código e de dados somente leitura garante a integridade dos agentes, porém, para agentes multi-hop esta técnica não é suficiente. Assim, os dados sensı́veis, gerados em uma plataforma, devem ser armazenados em um repositório seguro de dados parciais, chamado de PRRepository, que será carregado pelo agente, para que possı́veis modificações de plataformas maliciosas possam ser detectadas. Uma das caracterı́sticas do repositório de resultados parciais é a possibilidade de inserção de resultados. Os protocolos criptográficos usados pelas plataformas para inserir e proteger um resultado no PRRepository são semelhantes ao protocos P1 e P2, definidos por Karjoth et al.(1998), da Famı́lia KAG definidos em [1]. 2 Para a descrição do procedimentos necessários para a inicialização do repositorio, para inserção de um resultado parcial e para verificação da integridade do repositorioRP, é utilizada a seguinte notação (ver tabela 1). 2.2 Pseudo-código class PRRepository implements Serializable { private String _protocol; ArrayList _PRList; private int _n = -1; private SDSIRSAPublicKey _pub0; private SDSIRSAPublicKey _nextPub; public PRRepository( String, Credentials, SDSIRSAPublicKey, SDSIRSAPrivateKey, SDSIRSAPublicKey) { } public boolean addParcialResult( Object, byte[], SDSIRSAPublicKey, SDSIRSAPrivateKey, SDSIRSAPublicKey) throws IOException { } public String getProtocol() { } public VerificationResult verify( SDSIRSAPublicKey, RORepository) { } 3 Figura 2: Inicialização do Repositório de Resultados Parciais public VerificationResult verify( SDSIRSAPublicKey, SDSIRSAPrivateKey, RORepository) throws IOException { } Cada objeto PRRepository conterá os seguintes atributos: protocol : String que define o protocolo criptográfico que será usado pelo repositório; PRList : ArrayList onde ficarão armazenados os resultados parciais adicionados pelas plataformas; n : quantidade de elementos da lista de resultados parciais; pub0 : chave pública da plataforma inicial (P0); nextPub : chave pública da próxima plataforma a ser visitada pelo agente que carrega o repositório de resultados parciais; Além destes atributos cada objeto da classe PRRepository possui alguns métodos, sendo eles: constructor() : inicializa um novo repositório de resultados parciais criando o objeto que conterá os resultados a serem adicionados. Depois, seta a chave pública que será a chave P0 e a chave pública da próxima plataforma a ser visitada. Então o proprietário do agente (plataforma de origem P0) gera um número aleatório r0 e computa o valor de ho, aplicando um função hash one-way sobre ro e a identidade da primeira plataforma a ser visitada (P1) (passo 1 da Figura 2). Este h0 é o valor âncora da relação de encadeamento dos resultados parciais recolhidos. O proprietário deve ainda cifrar o valor ro com a sua 4 Figura 3: Protocolos para a inserção de resultados parciais própria chave pública. Em seguida, o resultado deste processo de cifragem mais o identificador único do agente e a relação de encadeamento h0 são assinados pelo proprietário do agente, criando o resultado parcial protegido RP0 (passo 2, Figura 2). No esquema proposto, o identificador único do agente é o valor resultante da aplicação da função hash sobre o objeto Credencials. Finalmente, no passo 3, o resultado parcial é inserido no repositorioRP (RPo) e enviado para a plataforma P1. addParcialResult() : adiciona um resultado parcial de acordo com o protocolo definido na criação do repositório. A adição de um novo resultado parcial obedece a ordem etabelecida na figura 3 para cada protocolo. Os parâmetros exigidos para a adição de um novo repositório são: um Object, um byte[], uma chave pública SDSIRSAPublicKey - que será a pub0 e um par de chaves SPKI/SDSI - RSA; getProtocol() : retorna o protocolo utilizado pelo repositório; verify() : verifica a integridade do repositório para os protocolos A ou B com os seguintes parâmetros: uma chave pública SDSIRSAPublicKey e um objeto da classe RORepository; verify() : verifica a integridade do repositório para o protocolo C. Somente pode ser feita pela plataforma inicial. Para tal os parâmetros necessários são: uma chave pública SDSIRSAPublicKey - chave da plataforma atual - , uma chave privada SDSIRSAPrivateKey - par correspondente à chave pública utilizada na inicialização - , e um objeto da classe RORepository; 5 2.3 Código Fonte O código fonte completo da classe PRRepository, bem como de suas classe internas, está disposto na seção Apêndice deste documento. 2.4 Testando a Funcionalidade do Repositório - A Classe PRRepAgentTest Na perspectiva de testar a funcionalidade do PRRepository foi implementado um agente móvel multi-saltos que percorre plataformas pré-definidas carregando um repositório de resultados parciais. A cada salto ele introduz novos resultados e verifica a integridade dos existentes. O agente teste - PRRepAgentTest - foi implementado de modo a prover todas as funções estruturadas no repositório de resultados parciais. No entanto, como descrito por Michelle S. Wangham , para a criação e utilização do repositorio de resultados parciais se faz necessária a criação de um repositório de dados somente-leitura. Desta forma, implicitamente, o agente móvel implementado para os testes do PRRepository contém algumas caracterı́sticas do agente implementado para os testes do RORepository. 2.4.1 O processo de criação O agente móvel multi-saltos implementado para os testes de funcionalidade, realizará durante a execução do método onCreation a criação dos repositórios especificados. O processo de criação do agente pode, então, ser assim resumidamente descrito: primeiro é feito todo o processo de criação de um objeto da classe RORepository, que contém as credenciais do agente sob forma de um objeto da classe Credentials e ainda, a qualidade de proteção exigida para seu agente proprietário (QoP ); em seguida é feito o hash criptográfico do objeto Credentials contigo no repositório somente-leitura. No passo seguinte, são criados os dois vetores de dados que conterão os destinos do agente teste e os identificadores de cada plataforma a percorrer. Feito isto, o agente está pronto para a criação do repositório de resultados parciais. Por fim, o agente dispara o método que inicia seu processo de viagem por diferentes plataformas. 2.4.2 Salto(s) para outra(s) plataforma(s) Depois de inicializado o processo de serialização, o agente salta até a próxima plataforma de seu itinerário, que deve estar previamente 6 Figura 4: Esquema de saltos do agente PRAgentTest inicializada em outra máquina. O esquema de saltos do agente teste pode ser visualizado na figura 4. A cada salto, o agente deverá ser interceptado por outro agente, o SecurityInterceptor, que procederá com as verificações de cada um dos repositórios carregados pelo agente. Segue-se, então, um troca de mensagens entre os dois agentes, a qual pode ser descrita, de seguinte maneira: depois que a chegada do DDAgletTest é sinalizada pela classe AgletContextImpl, o agente SecurityInterceptor envia uma mensagem requisitando os repositórios que estão presentes no agente PRAgletTest. Este, por sua vez, envia os repositórios que possui, ou seja, os objetos correspondentes ao RORepository e ao PRRepository. Feito isto, a integridade de cada repositório é testada separadamente. 2.4.3 O resultado da verificação Para finalizar, se algum indı́cio de ação maliciosa for encontrado, o agente teste é removido pelo agente interceptador. Este, por sua vez, guarda um objeto, resultado da verificação, que contém as listas de plataformas suspeitas e confiáveis. Por outro lado, se os testes de integridade dos repostitórios não apresentarem nenhum indı́cio de ação maliciosa, o agente teste fica livre para prosseguir sua viagem para outras plataformas. Como parte integrante de um sistema maior, o agente teste utilizado, PRAgletTest, pode ser utilizado como modelo para qualquer agente móvel multi-saltos no qual deseja-se garantir a integridade das informações por ele transportadas. 7 PRRepository - Classes Internas 3 Para que se pudesse encapsular as informações sobre uma determinada classe de objetos, visando otimizar o processo de serialização e desserialização, utilizou-se da criação de classes internas ao repositório de resultados parciais. 3.1 3.1.1 PRRepository.CipheredObject Descrição Classe utilizada para encapsular informações resultantes do processo de cifragem dos dados direcionados. 3.1.2 Pseudo-Código class CipheredObject implements Serializable { byte[][] _chipered; CipheredObject(byte[][]) { } public final byte[][] getChiphered() { } } Cada objeto desta classe possui os seguinte atributos e métodos: ciphered : objeto que contém os dados direcionados cifrados; constructor(byte[ [])]: cria um novo objeto CipheredObject armazenando na variável de classe ciphered os dados direcionados cifrados; getCiphered() : retorna o objeto que contém os dados direcionados cifrados. 3.1.3 Código Fonte O código fonte completo está disposto na seção Apêndice deste documento. 8 3.2 3.2.1 PRRepository.CredsHash Descrição Classe utilizada para encapsular um byte[] que é o resultado da aplicação da função sdsi.Hash sobre um objeto da classe Credentials. 3.2.2 Pseudo-Código class CredsHash implements Serializable { byte[] _credsHash; CredsHash(byte[]) { } public final byte[] getHash() { } } Cada objeto desta classe possui os seguinte atributos e métodos: chipered : contém o sdsi.Hash do objeto Credentials do agente; constructor(byte[]) : cria um novo objeto da classe CredsHash armazenando na variável de classe ciphered o sdsi.Hash do objeto Credentials; getHash() : retorna o byte[] usado na inicialização, que contém o sdsi.Hash; 3.2.3 Código Fonte O código fonte completo da classe está disposto na seção Apêndice deste documento. 3.3 3.3.1 PRRepository.ParcialResult Descrição Classe utilizada para guardar resultados parciais que fazem parte do PRRepository. 3.3.2 Pseudo-Código class ParcialResult implements Serializable { 9 Object _pri; ParcialResult(Object) { } public Object getParcial() { } } Cada objeto desta classe possui os seguinte atributos e métodos: pri : corresponde a um resultado parcial qualquer; constructor(Object) : instancia um novo objeto da classe ParcialResult armazenando em pri o resultado parcial; getParcial() : retorna o objeto que corresponde ao resultado parcial. 3.3.3 Código Fonte O código fonte completo da classe está disposto na seção Apêndice deste documento. 3.4 3.4.1 PRRepository.VerificationResult Descrição A classe VerificationResult foi implementada com o objetivo de guardar o resultado de uma verificação de integridade do repositório de resultados parciais. 3.4.2 Pseudo-Código class VerificationResult implements Serializable { private Object[] _reliablePR; private Object[] _suspectPR; boolean _ret = false; VerificationResult(int) { } VerificationResult(boolean) { } 10 public Object[] getReliablePlats() { } public Object[] getSuspectPlats() { } public boolean getResult() { } } Cada objeto desta classe possui os seguinte atributos e métodos: reliablePR : atributo que armazena os identificadores (chaves públicas) das plataformas consideradas confiáveis; suspectPR : atributo que armazena os identificadores (chaves públicas) das plataformas consideradas suspeitas; ret : atributo que representa o resultado da verificação corrente; constructor(int) : inicializa um novo objeto VerificationResult criando os conjuntos de plataformas confiáveis e suspeitas, se houver. Armazena nos atributos de classe esses conjuntos de pataformas; int i : inteiro correspondendo a posição do resultado parcial suspeito dentro da lista de resultados parciais: PRList. getReliablePlats() : retorna o conjunto de plataformas confiáveis; getSuspectPlats() : retorna o conjunto de plataformas suspeitas; getResult() : retorna o resultado da verificação. É um dado tipo boolean: true se o último elemento de PRList estiver ı́ntegro, o que significa que todos os outros anteriores estarão. 3.4.3 Código Fonte O código fonte completo da classe está disposto na seção Apêndice deste documento. 11 4 4.1 Classe PathRegister Descrição O esquema proposto por Michelle S. Wangham conta com uma técnica que consiste em um registrador de caminhos. O objetivo deste é o de prover proteção para a plataforma e proteção para os agentes móveis pois detecta as possı́veis violações da integridade do código e do estado do agente e ajuda ainda no processo de identificação de plataformas maliciosas. A implementação desta técnica se fez possı́vel através da classe PathRegister do pacote br.ufsc.das.agentSec, que é um registrador de caminhos onde ficam registradas todas as plataformas por onde o agente percorre durante sua viagem. Esses caminhos registrados são, na verdade, resultados parciais adicionados a um repositório de resultados parciais contido no registrador de caminhos. A classe PathRegister permite, ainda, a definição de quando os testes de integridade devem ser executados, isto é, os testes podem ser feitos em cada plataforma ou somente na plataforma de origem. Através da implementação do Registrador de Caminhos, num âmbito mais geral, será implementado um Controle Social da Reputação das Plataformas. 4.2 Pseudo-Código class PathRegister implements Serializable { private PRRepository _paths; public PathRegister(Credentials creds, SDSIRSAPublicKey PnextPub, SDSIRSAPublicKey pub, SDSIRSAPrivateKey priv) { } public void addPath(byte[] credsHash, SDSIRSAPublicKey PnextPub, SDSIRSAPublicKey pub, SDSIRSAPrivateKey priv) { } public SDSIRSAPublicKey getElementAt(int i){ } public PRRepository getPath(){ } 12 public boolean verifyIntegrity(SDSIRSAPublicKey pub, RORepository rorep) { } } Cada objeto da classe PathRegister possui os seguintes atributos e métodos: paths : atributo que representa o objeto da classe PRRepository criado para armazenar os caminhos percorridos pelo agente; constructor(Credentials, PublicKey1 , PublicKey2 , PrivateKey3 ) : faz a inicialização do registrador de caminhos criando um novo objeto da classe PRRepository, que possibilita a ligação segura entre cada registro (“caminho percorrido”) e ainda, a verificação de cada um destes registros. Assim, um registrador de caminhos nada mais é do que um conjunto de resultados parciais formando um repositório; addPath(byte[], PublicKey4 , PublicKey5 , PrivateKey6 ) : a adição de uma nova entrada ao registrador é feita com a ajuda do método addParcialResult do repositório de resultados parciais; getElementAt(int) : recupera o identificador da plataforma a partir de um ı́ndice fornecido; getPath() : retorna o objeto que contém o registro de todos os caminhos percorridos pelo agente; verifyIntegrity(SDSIRSAPublicKey, RORepository) : checa a integridade de cada um dos caminhos percorridos pelo agente, verificando a ligação entre eles, através do método verify do repositório de resultados parciais; 4.3 Código Fonte O código fonte completo da classe PathRegister está descrito na seção Apêndice deste documento. 1 As chaves usadas são chaves SDSIRSA 13 Figura 5: Esquema de saltos do agente PathRegAgentTest 4.4 Testando a Funcionalidade do Registrador de Caminhos - A Classe PathRegAgentTest A implementação da classe PathRegister evolveu também a criaçao de um agente de testes. Este agente, o PathRegAgentTest, é um agente multi-saltos que percorre plataformas pré-determinadas adicionando novas entradas no registrador de caminhos e ao mesmo tempo fazendo verificações de integridade. Para tanto, o agente utilizado nos testes, possui o método onCreation, onde é criado um objeto da classe PathRegister. 4.4.1 O processo de criação Durante o método onCreation o agente teste inicializa um novo objeto da classe RORepository, que será posteriormente utilizado para a criação do registrador de caminhos. O repositório somente-leitura contém a qualidade de segurança exigida pelo agente, que neste caso é a verificação da integridade do próprio repositório e do registrador de caminhos. Depois de criado o repositório somente-leitura, o agente teste inicializa o registrador de caminhos instanciando um novo objeto da classe PathRegister. 4.4.2 Salto(s) para outra(s) plataforma(s) No passo seguinte, o agente teste inicia o processo de saltos para outras plataformas de acordo com o esquema descrito na figura 5. Em cada uma das plataformas que o agente teste passa é realizado o teste de integridade do registrador de caminhos e, se nenhum 14 indı́cio de ação maliciosa for encontrado, um novo registro será adicionado. Para que seja feita a verificação e inserção de novos registros, o agente teste troca um série de mensagens com o agente interceptador (SecurityInterceptor ) da plataforma. O procedimento de verificação, bem como o processo de inserção de novos registros, é o mesmo utilizado para o repositório de resultados parciais. 4.4.3 O resultado da verificação A continuidade da viagem do agente teste está intimamente ligada ao resultado do processo de verificação. Assim, caso seja encontrado algum indı́cio de ação maliciosa em qualquer um dos registros armazenados, o agente teste é suspenso eplo agente interceptador. Por outro lado, se nenhum indı́cio de ação maliciosa for encontrado, o agente teste pode prosseguir sua viagem saltando para a próxima plataforma do seu itinerário. Como aspecto geral de implementação, o agente que carrega os repositórios, PathRegAgletTest, pode ser utilizado como modelo para qualquer agente móvel multi-saltos no qual deseja-se realizar um registro dos caminhos percorridos garantindo-se a integridade dos dados armazenados. 5 Considerações Finais Os resultados obtidos permitem afirmar com segurança que todo o processo de criação, cifragem, assinatura e posterior verificação de resultados parciais, que compôem o repositório PRRepository, pode ser realizado utilizando a classe descrita acima, ficando garantidas todas as propriedades de segurança requeridas pelo projeto. 6 Apêndice 15 /* * Created on 19/02/2004 * Copyright (C) 2004 This program is modify it under as published by of the License, c Deitos Rafael Jos~ A free software; you can redistribute it and/or the terms of the GNU General Public License the Free Software Foundation; either version 2 or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package br.ufsc.das.agentSec; import import import import import import import import java.io.IOException; java.io.Serializable; java.security.InvalidKeyException; java.security.NoSuchAlgorithmException; java.security.SignatureException; java.util.ArrayList; java.util.Arrays; java.util.Random; import javax.swing.JFrame; import javax.swing.JOptionPane; import import import import import import prototype.SdsiResolver; sdsi.SDSIPublicKey; sdsi.SDSIRSAPrivateKey; sdsi.SDSIRSAPublicKey; sdsi.SDSISignature; sdsi.sexp.SexpParseException; /** * Class that implements the Secure Parcial Results Repository of the aglet 16 * intending to protect itself from malicious plataforms maintaining its * integrity. * c Deitos * @author Rafael Jos~ A */ public class PRRepository implements Serializable { //string que define o protocolo a ser usado private String _protocol; //vetor encadeado que contem //em cada posicao outro vetor com //a assinatura e os resultados parciais(plainText) ArrayList _PRList; //quantidade de elementos no vetor _PRList //comeca com -1 devido a plataforma 0 private int _n = -1; //chave publica da plataforma inicial private SDSIRSAPublicKey _pub0; //chave publica da proxima plataforma private SDSIRSAPublicKey _nextPub; /** * Creates a new instance of PRRepository * * @param prot * String that means the protocol to be used * @param creds * Credentials object of the agent * @param P1 * SDSIRSAPublicKey of the next plataform * @param priv * SDSIRSAPrivateKey for the signature process * @param pub * SDSIRSAPublicKey for the signature process */ public PRRepository(String prot, Credentials creds, SDSIRSAPublicKey P1, SDSIRSAPrivateKey priv, SDSIRSAPublicKey pub) { //inicializando objeto Helper Helper _helper = new Helper(); 17 //inicializamos o vetor de resultados parciais _PRList = new ArrayList(); //faz-se _pub0=pub _pub0 = pub; //seta chave da proxima plataforma _nextPub = P1; //seta o protocolo a ser usado _protocol = prot; try { //gera numero aleatorio -> r0 Random r = new Random(); double r0 = r.nextDouble(); byte[] r0Byte = _helper.double2byteArray(r0); //transfoma chave publica em um byte[] byte[] P1byte = _helper.object2byteArray((Object) P1); //concatena r0 com identificador da plataforma (P1) byte[] concat = _helper.concatenate(r0Byte, P1byte); //calcula relacao de encadeamento -> faz o sdsi.Hash deste byte[] byte[] h0 = _helper.makeHash(concat); //cifra r0 (como um byte[]) com sua chave publica e guarda em // CipheredObject CipheredObject co = new CipheredObject(_helper.cipher(_helper .double2byteArray(r0), _pub0)); //calcula indentificador unico e guarda no objeto Credshash CredsHash ch = new CredsHash(_helper.makeHash(_helper .object2byteArray((Object) creds))); //objeto ObjectWriter que concatena os elementos calculados ObjectWriter ow = new ObjectWriter(); ow.writeBytes(h0); ow.writeObject(co); ow.writeObject(ch); // assina usando chaves SDSI SDSISignature sig = new SDSISignature(ow.getBytes(), priv, pub); //ARMAZENA O PRIMEIRO ELEMENTO NO VETOR _PRList bject[] PR0 = new Object[2]; PR0[0] = sig; PR0[1] = ow.getBytes(); //aqui exatamente armazena vetor _PRList.add(PR0); //incrementa contador de elementos _n++; } catch (Exception e) { e.printStackTrace(); 18 } } /** * Method that adds new parcial results in the PRRepository * * @param pri * parcial result to be inserted on the _PRList * @param credsHash * hash of the Credentials Object * @param PnextPub * SDSIRSAPublicKey of the next plataform to be visited * @param priv * SDSIRSAPrivateKey for the signing process * @param pub * SDSIRSAPublicKey for the signing process */ public boolean addParcialResult(Object pri, byte[] credsHash, SDSIRSAPublicKey PnextPub, SDSIRSAPrivateKey priv, SDSIRSAPublicKey pub) throws IOException { //verificacao da autenticidade da plataforma como //proxima plataforma boolean isNextPlat = verifyCurrentKey(pub); if (isNextPlat) { //ADICIONA RESULTADO PARCIAL //cria um helper Helper helper = new Helper(); //RELACAO DE ENCADEAMENTO //gera numero aleatorio ri Random r = new Random(); double ri = r.nextDouble(); //byte[] riByte = _helper.double2byteArray(ri); //transfoma chave publica em um byte[] byte[] PnextPubByte = helper.object2byteArray((Object) PnextPub); byte[] concat; if (_protocol.equalsIgnoreCase("C")) { //concatena resultado parcial anterior com chave publica concat = helper.concatenate(helper .object2byteArray((Object) _PRList.get(_n)), PnextPubByte); } else { //concatena assinatura do resultado parcial anterior com chave 19 // publica concat = helper.concatenate( helper.object2byteArray((Object) ((Object[]) _PRList .get(_n))[0]), PnextPubByte); } //calcula relacao de encadeamento -> hash byte[] hi = helper.makeHash(concat); //cria objeto ParcialResult ParcialResult pr = new ParcialResult(pri); //cria objeto CredsHash CredsHash ch = new CredsHash(credsHash); //VERIFICA QUAL PROTOCOLO DEVERA SER USADO if (_protocol.equalsIgnoreCase("A")) { //cria resultado parcial protegido ObjectWriter owA1 = new ObjectWriter(); owA1.writeObject(pr); owA1.writeDouble(ri); //cifra com a chave da plataforma inicial "_pub0" //criando objeto CipheredObject para separar elementos CipheredObject coA = new CipheredObject(helper.cipher(owA1 .getBytes(), _pub0)); //cria objeto que contem todos os elementos concatenados ObjectWriter owA2 = new ObjectWriter(); owA2.writeBytes(hi); owA2.writeObject(ch); owA2.writeObject(coA); try { //assina com chaves SDSI SDSISignature sig = new SDSISignature(owA2.getBytes(), priv, pub); //INSERE O NOVO ELEMENTO EM _PRList Object[] PRi = new Object[2]; PRi[0] = sig; PRi[1] = owA2.getBytes(); //aqui exatamente armazena _PRList.add(PRi); //incrementa contador de elementos _n++; } catch (Exception e) { _n++; e.printStackTrace(); } } else if (_protocol.equalsIgnoreCase("B")) { 20 //cria resultado parcial protegido ObjectWriter owB = new ObjectWriter(); owB.writeBytes(hi); owB.writeObject(ch); owB.writeObject(pr); owB.writeDouble(ri); try { //assina com chaves SDSI SDSISignature sig = new SDSISignature(owB.getBytes(), priv, pub); //INSERE O NOVO ELEMENTO NO VETOR _PRList Object[] PRi = new Object[2]; PRi[0] = sig; PRi[1] = owB.getBytes(); //aqui exatamente armazena vetor _PRList.add(PRi); //incrementa contador de elementos _n++; } catch (Exception e) { _n++; e.printStackTrace(); } } else if (_protocol.equalsIgnoreCase("C")) { try { //concatena credsHash, pr e ri ObjectWriter owC = new ObjectWriter(); owC.writeObject(ch); owC.writeObject(pr); owC.writeDouble(ri); //assina com chaves SDSI SDSISignature sig = new SDSISignature(owC.getBytes(), priv, pub); //criamos um objeto writer para guardar a assinatura ObjectWriter writSig = new ObjectWriter(); writSig.writeObject(sig); writSig.writeObject(owC.getBytes()); //cifra a assinatura com chave da plataforma inicial // "_pub0" CipheredObject coC = new CipheredObject(helper.cipher( writSig.getBytes(), _pub0)); //monta resultado parcial com "hi" na PRIMEIRA POSICAO ObjectWriter owC1 = new ObjectWriter(); owC1.writeBytes(hi); 21 owC1.writeObject(coC); //INSERE O NOVO ELEMENTO NO VETOR _PRList _PRList.add(owC1.getBytes()); //incrementa contador de elementos _n++; } catch (Exception e) { _n++; e.printStackTrace(); } } //retorna true = elemento adicionado return true; } else { //plataforma naum autorizada a adicionar //resultado parcial return false; } } /** * Class that stores an object that had been ciphered * c Deitos * @author Rafael Jos~ A */ public class CipheredObject implements Serializable { byte[][] chipered; /** * Creates a new instance of CipheredObject * * @param b * the result of a cipher process */ CipheredObject(byte[][] b) { this.chipered = b; } /** * Method that return the original result of the cipher process * * @return the ciphered object */ public byte[][] getChiphered() { 22 try { return this.chipered; } catch (Exception e) { e.printStackTrace(); } return null; } } /** * Class that stores the Hash of a Credentials object * c Deitos * @author Rafael Jos~ A */ public class CredsHash implements Serializable { byte[] credsHash; /** * Creates a new instance of CredsHash * * @param b * the bye[] meaning the hash function of Credentials */ CredsHash(byte[] b) { this.credsHash = b; } /** * Returns credsHash * * @return the hash function of Credentials */ public byte[] getHash() { try { return this.credsHash; } catch (Exception e) { e.printStackTrace(); } return null; } } 23 /** * Method that returns the protocol of the PRRepository * * @return _protocol */ public String getProtocol() { try { return _protocol; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Class that stores the actual parcial result fo the repository * c Deitos * @author Rafael Jos~ A */ public class ParcialResult implements Serializable { Object pri; /** * Creates a new instance of ParcilResult * * @param o */ ParcialResult(Object o) { this.pri = o; } /** * Returns the parcial result * * @return pri */ public Object getParcial() { try { return this.pri; } catch (Exception e) { e.printStackTrace(); } return null; 24 } } /** * Class that create lists of parcial results: for reliable and suspect * ones. Implements the result of a verification process. * c Deitos * @author Rafael Jos~ A */ public class VerificationResult implements Serializable { //PR confiaveis -> result=true private Object[] reliablePR; //plataformas suspeitas -> result=false private Object[] suspectPR; //the return state boolean ret = false; /** * Creates a new instance of VerificationResult * * @param i * int representing the position of the suspect parcial * result */ VerificationResult(int i) { try { ArrayList PRtemp = _PRList; if (i == 0) { //PR0 - todos os PRs saum confiaveis suspectPR = null; c a propria lista de PRs //reliablePR ~ A reliablePR = _PRList.toArray(); } else { for (int j = 0; j < i; j++) { //PR confiaveis reliablePR = new Object[i + 1]; reliablePR[j] = PRtemp.get(j); //remove os j primeiros elementos do array //deixando somente os PR comprometidos temp.remove(j); } 25 //agora restam em PRtemp somente //os PRs comprometidos suspectPR = PRtemp.toArray(); } } catch (Exception e) { e.printStackTrace(); } } /** * Creates a new instance of VerificationResult * * @param b * a boolean */ VerificationResult(boolean b) { //para os casos onde sabe-se de antemao o resultado ret = b; } /** * Return the plataforms considered reliable * * @return reliablePlats */ public Object[] getReliablePlats() { try { return reliablePR; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Return the plataforms considered suspect * * @return suspectPlats */ public Object[] getSuspectPlats() { try { return suspectPR; } catch (Exception e) { 26 e.printStackTrace(); } return null; } /** * Return the boolean result of a verification process * * @return true if all the parcial results were reliable */ public boolean getResult() { try { if (suspectPR == null || suspectPR.length == 0) { return true; } else { return ret; } } catch (Exception e) { e.printStackTrace(); } return false; } } /** * Method that verifies the integrity of the PRRepository for protocols A * and B * * @param Pi * SDSIRSAPublicKey of the current plataform * @param rorep * RORepository object */ public VerificationResult verify(SDSIRSAPublicKey Pi, RORepository rorep) { Helper helper = new Helper(); SdsiResolver resolver = new SdsiResolver(); if (_protocol.equalsIgnoreCase("A") || _protocol.equalsIgnoreCase("B")) { int iteracao = _n; //REALIZA TESTE DA INTEGRIDADE DA ASSINATURA if (verify_repository(Pi)) { //repositorio intacto!!! if (iteracao == 0) { //primeira ou segunda plataforma 27 //NAUM PRECISA TESTAR!!! //plataforma confiavel VerificationResult result = new VerificationResult(iteracao); return result; } else { //recalcula H(creds) pegando creds de rorep //H(creds).length: 16 fixo byte[] chCalc = helper.makeHash(helper .object2byteArray(rorep.getCredentials())); //comeca pelos menores PRs (Lazy) for (int i = 1; i <= iteracao; i++) { //TESTA RECURSIVAMENTE OS PRs byte[] PnextPubByte = null; if (i == iteracao) { //ultimo PR //chave pub = chave da plataforma atual PnextPubByte = helper.object2byteArray((Object) Pi); } else { //recupera chave do _PRList //recupera elemento " i+1 " Object[] PRii = (Object[]) _PRList.get(i + 1); //recupera a assinatura do _PRList SDSISignature sig = (SDSISignature) PRii[0]; //recuperar a chave publica " i+1 " SDSIRSAPublicKey pii = resolver.getPubFromSig(sig); //passa a chave para um byte array PnextPubByte = helper .object2byteArray((Object) pii); } //concatena assinatura do resultado parcial anterior // com chave publica //da proxima plataforma(pii) byte[] concat = helper.concatenate(helper .object2byteArray((Object) ((Object[]) _PRList .get(i - 1))[0]), PnextPubByte); //calcula relacao de encadeamento " i " -> hash byte[] hiCalc = helper.makeHash(concat); //RECUPERA CADA PR DA LISTA SEPARANDO //O CONTEUDO Object[] PRi = (Object[]) _PRList.get(i); //recuperamos o segundo elemento que contem os dados // concatenados byte[] element = (byte[]) PRi[1]; 28 //inicializacao das variaveis ObjectReader or = null; byte[] hiGot = null; byte[] chGot = null; try { //consideramos sempre hi como posicao zero do // byte[] or = new ObjectReader(element); //separamos hi do resto do byte[] hiGot = or.readByte(hiCalc.length); //separamos credsHash (posicao 1) CredsHash ch = (CredsHash) or.readObject(); chGot = ch.getHash(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } //TESTES!!! //compara-se os arrays if (Arrays.equals(chCalc, chGot) && Arrays.equals(hiCalc, hiGot)) { if (i == iteracao) { //hash intacto - PR intacto // testou ate a ultima plataforma //acabaram os testes com todas os PR = OK VerificationResult result = new VerificationResult( 0); return result; } else { //hash intacto - PR intacto //deve continuar testando pois //naum acabaram os PRs } } else { //hash corrompido ou PR corrompido VerificationResult result = new VerificationResult( i); return result; } } } } else { 29 //repositorio corrompido!!! VerificationResult result = new VerificationResult(false); return result; } //fim do teste } else { //nenhum protocolo escolhido!!! JFrame frame = new JFrame(); JOptionPane.showMessageDialog(frame, "You must choose a valid protocol (A, B or C)", "Warning", JOptionPane.WARNING_MESSAGE); return null; } return null; } /** * Method that verifies the integrity of the PRRepository for C protocol. * * @param Pi * SDSIRSAPublicKey of the current plataform * @param priv0 * SDSIRSAPrivateKey of the inicial plataform * @param rorep * RORepository object */ public VerificationResult verify(SDSIRSAPublicKey Pi, SDSIRSAPrivateKey priv0, RORepository rorep) throws IOException { Helper helper = new Helper(); SdsiResolver resolver = new SdsiResolver(); if (_protocol.equalsIgnoreCase("C")) { //recupera chave publica que assinou PR0 SDSIRSAPublicKey pub0 = null; try { Object[] PR0 = (Object[]) _PRList.get(0); SDSISignature sig0 = (SDSISignature) PR0[0]; pub0 = resolver.getPubFromSig(sig0); } catch (Exception e) { e.printStackTrace(); //Erro na recupera~ A§~ A£o da assinatura return null; } //compara com chave da plataforma atual 30 if (pub0 != null && pub0.equals(Pi)) { //plataforma atual = primeira plataforma //TESTA RECURSIVAMENTE ATE ENCONTRAR UM //RESULTADO PARCIAL INTACTO int iteracao = _n; if (iteracao == 0) { //NAUM PRECISA TESTAR //primeira ou segunda plataforma //plataforma confiavel VerificationResult result = new VerificationResult(iteracao); return result; } else { //h de " i " byte[] hiCalc = null; byte[] hiGot = null; //H(creds) obtido de PR byte[] chGot = null; //chave da plataforma a ser testada (direfente da HOME) SDSIRSAPublicKey pi = null; //h de " i+1 " byte[] hiiGot = null; //chave da plataforma i+1 SDSIRSAPublicKey pii = null; //testa todos os repositorios sem exce ~ A§~ A£o for (int i = 1; i <= iteracao; i++) { try { //recupera elemento i do vetor _PRList ObjectReader or1 = new ObjectReader( (byte[]) _PRList.get(i)); //separamos hi do resto do byte[] //HI como posicao 0 do byte[] for~ A§amos 16 bytes -> // RSA algoritm hiGot = or1.readByte(16); //recupera objeto cifrado que contem a assinatura CipheredObject co = (CipheredObject) or1 .readObject(); //recupera byte[][] correspondete a cifra com chave // publica _pub0 byte[][] ciphered = co.getChiphered(); //decifra com a privada priv0 recuperando o objeto // cifrado byte[] sigByte = helper.deCipher(ciphered, priv0); //com um ObjectReader recuperamos a assinatura 31 ObjectReader or2 = new ObjectReader(sigByte); SDSISignature sigRecovered = (SDSISignature) or2 .readObject(); //recupera os objetos assinados byte[] signedObjects = (byte[]) or2.readObject(); Objecer or5 = new ObjectReader(signedObjects); //agora recupera o hash(creds) do byte[] assinado //corresponde a posicao 1 de signedObjects CredsHash hCred = (CredsHash) or5.readObject(); chGot = hCred.getHash(); //recupera a CHAVE PUBLICA da plataforma " i " pi = resolver.getPubFromSig(sigRecovered); //######################################### if (i != iteracao) { //recupera elemento i+1 do vetor _PRList ObjectReader or3 = new ObjectReader( (byte[]) _PRList.get(i + 1)); //separamos hi do resto do byte[] //HI como posicao 0 do byte[] for~ A§amos 16 bytes // -> RSA algoritm hiiGot = or3.readByte(16); //recupera objeto cifrado que contem a // assinatura co = (CipheredObject) or3.readObject(); //recupera byte[][] correspondete a cifra com // chave publica _pub0 ciphered = co.getChiphered(); //decifra com a privada priv0 recuperando o // objeto cifrado sigByte = helper.deCipher(ciphered, priv0); //com um ObjectReader recuperamos a assinatura ObjectReader or4 = new ObjectReader(sigByte); sigRecovered = (SDSISignature) or4.readObject(); //recupera a CHAVE PUBLICA da plataforma " i+1 // " pii = resolver.getPubFromSig(sigRecovered); //CALCULA hi //concatena resultado parcial anterior com // chave publica i+1 byte[] concat = helper.concatenate(helper .object2byteArray((Object) _PRList .get(i - 1)), helper .object2byteArray((Object) pii)); 32 //calcula relacao de encadeamento novamente -> // hash hiCalc = helper.makeHash(concat); } else { //TESTE DO ULTIMO PR c a propria plataforma HOME //a proxima ~ A pii = Pi; //CALCULA hi //concatena resultado parcial anterior com // chave publica da home byte[] concat = helper.concatenate(helper .object2byteArray((Object) _PRList .get(i - 1)), helper .object2byteArray((Object) pii)); //calcula relacao de encadeamento novamente -> // hash hiCalc = helper.makeHash(concat); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } //recalcula H(creds) pegando creds de rorep //H(creds).length: 16 fixo byte[] chCalc = helper.makeHash(helper .object2byteArray(rorep.getCredentials())); //TESTES!!! //compara-se os arrays if (Arrays.equals(chCalc, chGot) && Arrays.equals(hiCalc, hiGot)) { if (i == iteracao) { //hash intacto - PR intacto // testou ate a ultima plataforma //acabaram os testes com todas os PR = OK VerificationResult result = new VerificationResult( 0); return result; } else { //hash intacto - PR intacto //deve continuar testando pois //naum acabaram os PRs 33 } } else { //hash corrompido ou PR corrompido VerificationResult result = new VerificationResult( i); return result; } } //problemas na verificacao //retorna falso VerificationResult result = new VerificationResult(false); return result; } } else { c HOME //chaves n~ A£o coincidem-> plataforma naum ~ A //nenhum teste deve ser feito VerificationResult result = new VerificationResult(true); return result; } } else { //nenhum protocolo escolhido!!! JFrame frame = new JFrame(); JOptionPane.showMessageDialog(frame, "You must choose a valid protocol (A, B or C)", "Warning", JOptionPane.WARNING_MESSAGE); return null; } } /** * Checks if the current key is equals the next plataform’s key * * @param Pi * SDSIRSAPublicKey of the current plataform * @return truefalse */ private boolean verifyCurrentKey(SDSIRSAPublicKey Pi) { try { if (_nextPub.equals(Pi)) { return true; } return false; } catch (Exception e) { 34 e.printStackTrace(); } return false; } /** * Method that verifies the integrity of the SDSSIgnature of the last * parcial result in the array. * * @param pub * SDSIPublicKey used on the signing process * @return false when the repository had been corrupted * @throws SignatureException */ private final boolean verify_repository(SDSIPublicKey pub) { byte[] data = null; SDSISignature sig = null; if (_protocol.equalsIgnoreCase("A") || _protocol.equalsIgnoreCase("B")) { //recuperando os dados => resultado parcial data = (byte[]) ((Object[]) _PRList.get(_n))[1]; sig = (SDSISignature) ((Object[]) _PRList.get(_n))[0]; } boolean result; try { //pub = chave da plataforma que assinou o PR result = sig.verify(pub, data); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } catch (SexpParseException e) { e.printStackTrace(); } if (result = true) { return true; } else return false; } } 35 /* * Created on 25/03/2004 * Copyright (C) 2004 This program is modify it under as published by of the License, c Deitos Rafael Jos~ A free software; you can redistribute it and/or the terms of the GNU General Public License the Free Software Foundation; either version 2 or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package br.ufsc.das.agentSec; import java.io.IOException; import java.io.Serializable; import prototype.SdsiResolver; import sdsi.SDSIRSAPrivateKey; import sdsi.SDSIRSAPublicKey; import sdsi.SDSISignature; /** * Class that implements a path register intending to create a social control * the related plataforms * * @author Rafael J. Deitos */ public class PathRegister implements Serializable { //registro dos caminhos private PRRepository _paths; /** * Constructor: creates a new instantce of PathRegister 36 * * @param creds * the Credentials object * @param PnextPub * a SDSIRSAPublicKey * @param pub * a SDSIRSAPublicKey * @param priv * a SDSIRSAPrivateKey */ public PathRegister(Credentials creds, SDSIRSAPublicKey PnextPub, SDSIRSAPublicKey pub, SDSIRSAPrivateKey priv) { try { //invoca o construtor do PRRepository com protocolo B _paths = new PRRepository("B", creds, PnextPub, priv, pub); } catch (Exception e) { e.printStackTrace(); } } /** * Add a new path in the path register * * @param credsHash * a sdsi.Hash of the Credentials object * @param PnextPub * a SDSIRSAPublicKey * @param pub * a SDSIRSAPublicKey * @param priv * a SDSIRSAPrivateKey */ public void addPath(byte[] credsHash, SDSIRSAPublicKey PnextPub, SDSIRSAPublicKey pub, SDSIRSAPrivateKey priv) { //cria novo resultado parcial a ser adicionado ObjectWriter ow; try { ow = new ObjectWriter(); ow.writeObject(pub); ow.writeObject(PnextPub); byte[] pri = ow.getBytes(); //adiciona resultado em _paths _paths.addParcialResult(pri, credsHash, PnextPub, priv, pub); 37 } catch (IOException e) { e.printStackTrace(); } } /** * Returns a specified plataform ID * * @param i * the integer representing the index of the element * @return the SDSIRSAPublicKey of the plataform */ public SDSIRSAPublicKey getElementAt(int i) { SdsiResolver resolver = new SdsiResolver(); SDSIRSAPublicKey pub = null; try { c sempre do tipo B //admitimos que PR ~ A //obtem o vetor correspondente ao primeiro elemento de PRList Object[] firstPR = (Object[]) _paths._PRList.get(i); //obtem a assinatura (primeiro elemento) deste elemento SDSISignature sig = (SDSISignature) firstPR[0]; //obtem a chave publica que gerou esta assinatura pub = resolver.getPubFromSig(sig); } catch (Exception e) { e.printStackTrace(); } return pub; } /** * Returns the PRRepository contained in the path register * * @return the PRRepository object */ public PRRepository getPath() { try { return _paths; } catch (Exception e) { e.printStackTrace(); } return null; } 38 /** * Verify the integrity of the Path Register object * * @param pub * a SDSIRSAPublicKey * @param rorep * a RORepository * @return true or false */ public boolean verifyIntegrity(SDSIRSAPublicKey pub, RORepository rorep) { try { //invocamos o metodo verify do PRRep com chave publica PRRepository.VerificationResult result = _paths.verify(pub, rorep); boolean verify = result.getResult(); return very; } catch (Exception e) { e.printStackTrace(); } return false; } } 39 Referências [1] M. WANGHAM, Um Esquema de Segurança de Agentes Móveis, PhD thesis, Universidade de Santa Catarina, 2004. 40