Desenvolvimento em Java 2.0: Protegendo os dados de aplicativo Java para computação em nuvem

Use criptografia de chave privada para proteger os dados em nuvem

Segurança de dados é uma preocupação séria para as organizações que estão considerando a adoção da nuvem, mas em muitos casos não precisa ser assim. Neste artigo de Desenvolvimento em Java 2.0, aprenda a usar a criptografia de chave privada e o Padrão de Criptografia Avançado para proteger aplicativos confidenciais na nuvem. Você também receberá um breve tutorial sobre estratégia de criptografia, que é importante para maximizar a eficiência das procuras condicionais nos armazenamentos de dados distribuídos na nuvem.

Andrew Glover, CTO, App47

 Andrew GloverAndrew Glover é desenvolvedor, autor, palestrante e empreendedor com uma paixão por desenvolvimento direcionado por comportamento, integração contínua e desenvolvimento de software Agile. Ele é o fundador da easyb , estrutura Behavior-Driven Development (BDD) e é o coautor de três livros: Continuous Integration, Groovy in Action e Java Testing Patterns. Você pode acompanhá-lo em seu blog e seguindo-o no Twitter.



10/Fev/2012

Sobre esta série

O panorama de desenvolvimento em Java mudou radicalmente desde que a tecnologia Java emergiu. Graças a estruturas maduras de software livre e infraestruturas de implementação confiáveis de aluguel, agora é possível montar, testar, executar e manter aplicativos Java de forma rápida e barata. Nesta série, Andrew Glover explora o espectro de tecnologias e ferramentas que tornam este novo paradigma de desenvolvimento Java possível.

Em apenas alguns anos, as plataformas e serviços de computação em nuvem mudaram drasticamente o panorama de desenvolvimento de aplicativos em Java™ . Elas diminuíram as barreiras associadas à manutenção e configuração do sistema e, simultaneamente, reduziram o custo e aumentaram a velocidade de entrada do software no mercado. Conceitualmente, a computação em nuvem faz sentido: os gerentes de negócio adoram o retorno sobre o investimento e os desenvolvedores adoram estarem livres dos códigos de infraestrutura. Mas muitas lojas ainda estão em dúvida sobre a mudança ou não para plataformas na nuvem.

A segurança dos dados é uma das principais preocupações para uma organização que está considerando migrar seu software para uma infraestrutura na nuvem. Quanto mais confidenciais forem os dados, mais motivos haverá para se preocupar. Como desenvolvedores de software, é importante que entendamos os riscos reais de segurança da computação na nuvem e as abordagens realistas para a solução de, pelo menos, algumas dessas preocupações.

Neste artigo de Desenvolvimento em Java 2.0, eu explicarei qual a diferença entre o armazenamento de dados na nuvem e o armazenamento em uma máquina centralizada. Em seguida, mostrarei como usar as normas e utilitários integrados de criptografia de chave privada da plataforma Java para proteger razoavelmente seus dados, mesmo se eles estiverem armazenados em um armazenamento de dados distribuído na nuvem. Finalmente, demonstrarei uma abordagem estratégia para criptografia, usando as condições da consulta como uma linha de base para decidir se criptografa ou não seus dados.

Protegendo os dados na nuvem

A computação na nuvem não introduz exatamente novas preocupações com a segurança dos dados; na maioria dos casos, ela apenas as amplificam. Colocar dados na nuvem os expõe possivelmente a um público maior, o que normalmente é uma coisa boa. Porém, se os dados expostos forem privados, ou acessados condicionalmente, os resultados poderão ser catastróficos. A questão fundamental com a computação em nuvem é que ela remove dados confiados do controle imediato de um desenvolvedor ou um administrador de sistema. Em vez de serem armazenados e gerenciados localmente, os dados na nuvem são armazenados em dispositivos distribuídos que podem ser localizados em qualquer lugar e possivelmente acessados por qualquer pessoa.

Privacidade de dados na UE

A abordagem da União Europeia à privacidade dos dados nas plataformas na nuvem é muito mais rígida do que a dos Estados Unidos: dados pessoais que pertencem a um cidadão da UE (como o registro médico de um cidadão da França) precisam residir em servidores existentes dentro da UE.

Mesmo se sua empresa puder viver com o fato de um armazenamento de dados distante e descentralizado, você vai querer que seus aplicativos na nuvem tenham uma quantia módica de segurança de dados. Quando você começa a pensar sobre segurança de dados, duas questões importantes surgem:

  • Os dados estão protegidos durante o trânsito?
  • Os dados em repouso estão protegidos?

Dados em trânsito estão relacionados à forma com a qual os dados vão de um local para outro; ou seja, qual tecnologia de comunicação e infraestrutura você está usando. Dados em repouso estão relacionados à forma com a qual — e quão bem — seus dados são armazenados. Se, por exemplo, você armazena nomes de usuário e senha em um banco e dados sem criptografá-los, seus dados em repouso não estarão seguros.

Para proteger os dados em repouso na web, é comum usar HTTPS. Ou seja, HTTP com criptografia de dados que viajam de navegadores para clientes. Outra vantagem do HTTPS é sua ubiquidade: a maioria dos desenvolvedores configurou o Apache, o Tomcat ou Jetty para usar HTTPS.

Criptografia também é o mecanismo comum para proteção de dados em repouso e a computação em nuvem não muda isso. Embora a criptografia possa ser esotérica, você precisa apenas saber algumas coisas básicas sobre ela para proteger razoavelmente os dados de seu aplicativo. Assim que seus dados estiverem protegidos, realmente não importa se você os acessa localmente ou por meio de uma plataforma ou armazenamento de dados na nuvem.


Criptografia de chave privada

Criptografia é o processo de transformação de um texto simples, legível para humanos, em um texto ilegível. Isso é feito com um algoritmo criptográfico, também conhecido como uma cifra. O texto criptografado é descriptografado de volta para um texto legível por meio de uma chave, que é essencialmente uma senha. A criptografia trabalha para proteger as informações tornando-as ilegíveis por qualquer pessoa que não tenha a chave.

Dois tipos de criptografia baseada em chave são usados na computação: criptografia por chave pública e criptografia por chave privada. Criptografia por chave pública é a técnica mais comum para proteger os dados em trânsito; na verdade, é a arquitetura subjacente da segurança de transação HTTPS. Essa forma de criptografia exige duas chaves em um conjunto de chave público-privada. A chave pública pode descriptografar somente dados criptografados com a chave privada. Na criptografia por chave pública, a chave pública pode ser distribuída com segurança, enquanto a chave privada precisa permanecer sob o controle de um administrador. A criptografia por chave pública facilita o compartilhamento de informações criptografadas.

Chaves privadas e privacidade

Independentemente do algoritmo de criptografia usado, você precisa se certificar de que sua chave privada esteja protegida. Sua passphrase deve atender a normas de segurança alta e nunca deve ser armazenada em texto simples — especialmente na nuvem. Felizmente, a infraestrutura de segurança da plataforma Java cria chaves razoavelmente complexas e permite que você as proteja na keystore da plataforma Java.

Na criptografia de chave privada, os dados são criptografados e descriptografados usando uma única chave privada. Esse tipo de criptografia dificulta o compartilhamento dos dados criptografados com terceiros, pois tanto o emissor quanto o receptor precisam usar a mesma chave. Se essa chave estiver comprometida, todas as informações criptografadas estarão comprometidas. A criptografia de chave privada é altamente efetiva se os dados que estão sendo criptografados não precisarem ser compartilhados com outras partes, de modo que a chave possa ser sempre mantida sob controle rígido.

A criptografia de chave privada é um modo efetivo de proteger os dados de aplicativo que serão armazenados e transmitidos por uma infraestrutura na nuvem. Como a chave de criptografia permanece sob o controle de um administrador ou criador do aplicativo, os provedores na nuvem e outras possíveis ameaças de violação não possuem acesso descontrolado a esses dados.


Criptografando um aplicativo Java

É possível escolher entre várias opções de proteção dos aplicativos Java, incluindo as bibliotecas padrão da plataforma Java. Também há vários padrões de criptografia e pacotes para sua escolha. Para os exemplos a seguir, usarei as principais bibliotecas Java e AES, ou Padrão de Criptografia Avançado. Usarei uma chave privada para criptografar o texto simples e descriptografar o texto cifrado, que é um texto simples que foi criptografado. Eu gosto do AES, pois ele é aprovado pela Agência de Segurança Nacional e padronizado pelo governo dos Estados Unidos.

Para obter o máximo de flexibilidade e facilidade em testes, criarei algumas interfaces de criptografia e classes de implementação associadas que simplesmente envolvem as principais classes Java. Em seguida, mostrarei como usar essas classes para persistir com segurança e até mesmo consultar dados nos armazenamentos de dados na nuvem, como o SimpleDB da Amazon ou até mesmo o MongoDB da MongoHQ.

Na Listagem 1, eu defini uma interface simples de criptografia genérica que define dois métodos para criptografar e descriptografar dados. Essa interface servirá como um frente para diversos algoritmos; ou seja, minhas classes de implementação usarão uma cifra específica como AES.

Lista 1. Uma interface de criptografia
package com.b50.crypto;

public interface Cryptographical {
 String encrypt(String plaintext);
 String decrypt(String ciphertext);
}

Com minha interface criptográfica , posso criptografar um texto ou descriptografar o texto cifrado. Em seguida, na Listagem 2, usarei a API de segurança Java para criar outra interface que representa uma chave:

Lista 2. Uma interface de chave
package com.b50.crypto;

import java.security.Key;

public interface CryptoKeyable {
  Key getKey();
}

Como você pode ver, minha interface CryptoKeyable serve como um wrapper para o tipo de chave principal da plataforma Java.

Se você estiver usando a criptografia AES, os caracteres binários gerados quando você criptografa um texto simples deverão ser codificados por base-64 — ou pelo menos se você quiser usá-los em solicitações da web (por exemplo, com domínios SimpleDB). Dessa forma, codificarei todas as cadeias de caractere criptografadas e decodificarei quaisquer cadeias de caractere descriptografadas.

Minha classe de implementação criptográfica para AES, exibida na Listagem 3, não somente lida com a criptografia AES, mas também com codificação e decodificação de base-64:

Lista 3. Uma implementação AES de minha interface criptográfica
package com.b50.crypto;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class AESCryptoImpl implements Cryptographical {

 private Key key;
 private Cipher ecipher;
 private Cipher dcipher;

 private AESCryptoImpl(Key key) throws NoSuchAlgorithmException,
   NoSuchPaddingException, InvalidKeyException {
  this.key = key;
  this.ecipher = Cipher.getInstance("AES");
  this.dcipher = Cipher.getInstance("AES");
  this.ecipher.init(Cipher.ENCRYPT_MODE, key);
  this.dcipher.init(Cipher.DECRYPT_MODE, key);
 }

 public static Cryptographical initialize(CryptoKeyable key) throws CryptoException {
  try {
   return new AESCryptoImpl(key.getKey());
  } catch (NoSuchAlgorithmException e) {
   throw new CryptoException(e);
  } catch (NoSuchPaddingException e) {
   throw new CryptoException(e);
  } catch (InvalidKeyException e) {
   throw new CryptoException(e);
  }
 }

 public String encrypt(String plaintext) {
  try {
   return new BASE64Encoder().encode(ecipher.doFinal(plaintext.getBytes("UTF8")));
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }

 public String decrypt(String ciphertext) {
  try {
   return new String(dcipher.doFinal(new BASE64Decoder().decodeBuffer(ciphertext)),
     "UTF8");
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }
}

O Java KeyStore

Em seguida, vamos pensar sobre a chave de criptografia. As principais bibliotecas da plataforma Java podem ser usadas para criar chaves de criptografia fortes; no entanto, esses métodos sempre produzirão uma nova chave gerada aleatoriamente. Portanto, se você criar uma chave usando a classe Java, KeyGenerator , será necessário armazenar essa chave para uso futuro (ou seja, até que você decida descriptografar o texto criptografado com essa chave). Para isso, é possível usar o utilitário KeyStore da plataforma Java e as classes correspondentes.

KeyStore tem um conjunto de classes que permite que você salve uma chave em um arquivo binário protegido por senha, chamado de keystore. Posso testar as chaves em Java com algumas etapas de teste. Primeiro, eu crio duas instâncias de uma Chave e mostro que a Cadeia de caracteres criptografada correspondente de cada uma é diferente, como mostra a Listagem 4:

Lista 4. Criptografia simples usando duas chaves diferentes
@Test
public void testEncryptRandomKey() throws Exception {
 SecretKey key = KeyGenerator.getInstance("AES").generateKey();
 Cryptographical crypto = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String enc = crypto.encrypt("Andy");
 Assert.assertEquals("Andy", crypto.decrypt(enc));

 SecretKey anotherKey = KeyGenerator.getInstance("AES").generateKey();
 Cryptographical anotherInst = AESCryptoImpl.initialize(new AESCryptoKey(anotherKey));
 String anotherEncrypt = anotherInst.encrypt("Andy");
 Assert.assertEquals("Andy", anotherInst.decrypt(anotherEncrypt));

 Assert.assertFalse(anotherEncrypt.equals(enc));
}

Em seguida, na Listagem 5, eu demonstro que a instância de uma determinada chave sempre gera exatamente o mesmo texto criptografado para uma Cadeia de caracteres correspondente:

Lista 5. Uma chave privada corresponde a uma única cadeia de caracteres
@Test
public void testEncrypt() throws Exception {
 SecretKey key = KeyGenerator.getInstance("AES").generateKey();

 KeyStore ks = KeyStore.getInstance("JCEKS");
 ks.load(null, null);
 KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(key);
 ks.setEntry("mykey", skEntry,
   new KeyStore.PasswordProtection("mykeypassword".toCharArray()));
 FileOutputStream fos = new FileOutputStream("agb50.keystore");
 ks.store(fos, "somepassword".toCharArray());
 fos.close();

 Cryptographical crypto = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String enc = crypto.encrypt("Andy");
 Assert.assertEquals("Andy", crypto.decrypt(enc));

 //alternatively, read the keystore file itself to obtain the key

 Cryptographical anotherInst = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String anotherEncrypt = anotherInst.encrypt("Andy");
 Assert.assertEquals("Andy", anotherInst.decrypt(anotherEncrypt));

 Assert.assertTrue(anotherEncrypt.equals(enc));
}

O que eu criptografo com uma chave específica, eu preciso descriptografar com a mesma chave. O uso do Java KeyStore é uma maneira conveniente e segura de armazenar minhas chaves.

Observações sobre o keystore

O código nas Listagens 4 e 5 é padrão, mas serve para demonstrar algumas coisas:

  • O keystore tem um nome.
  • O arquivo armazenado é protegido por uma senha.
  • O keystore pode armazenar mais de uma chave.
  • Cada chave dentro do armazenamento tem uma senha associada.

Para essa etapa de teste, eu decidi criar um novo keystore sempre que executava o teste. Eu poderia ter aberto facilmente um keystore existente para cada teste novo. Se eu quisesse usar um keystore existente, eu teria que saber sua senha, além da senha para acessar uma chave específica.

A chave é tudo quando o assunto é criptografia. Não importa quão forte a cifra subjacente seja; se minha chave estiver comprometida, meus dados ficarão expostos. Isso também significa garantir que o keystore e suas passphrases associadas estejam sempre protegidas. (Por exemplo, em um aplicativo de produção eu não codificaria as senhas da maneira que fiz para o propósito da demonstração nas Listagens 4 e 5.)


Criptografia na nuvem

Quando você criptografa dados, muda sua propriedades. Essencialmente, isso significa que um número inteiro criptografado não responderá adequadamente a uma comparação com um número inteiro. Consequentemente, é importante pensar bem como, por que e sob quais circunstâncias você eventualmente consultaria os dados armazenados na nuvem. A boa notícia é que em muitos casos, os dados que você deseja manter privados terão um valor de negócio diferente dos dados que você deseja manipular: criptografar o nome de uma conta ou alguma informação pessoal sobre o titular da conta faz sentido, mas criptografar o saldo de uma conta talvez não faça (pois quem vai se importar com um saldo de conta que não pode ser vinculado a uma pessoa?).

Consultando com criptografia

Os dados criptografados são facilmente procurados ao realizar correspondências exatas , como: "Encontre todas as contas chamadas 'foo' (em que 'foo' está criptografado)". Mas isso não funciona originalmente para consultas condicionais como: "Encontre todas as contas cujo saldo em atraso seja maior do que $450, em que $450 está criptografado".

Por exemplo, vamos imaginar que eu uso uma cifra simples que inverte a ordem dos caracteres e adiciona o caractere i ao final de uma cadeia de caracteres. Nesse caso, a cadeia de caracteres foo se tornaria oofi e 450 se tornaria 054i. Se o valor do nome da tabela tiver sido criptografado usando essa cifra simples, eu poderia consultar facilmente por correspondências exatas, como "select * from table where name = 'oofi'". Mas uma comparação do valor criptografado de 450 seria completamente diferente: "select * from table where amount > 054i" não é exatamente a mesma coisa que "select * from table where amount > 450".

Para fazer uma comparação de dados nesse caso, talvez eu tenha que realizar uma descriptografia no aplicativo — ou seja, eu precisaria selecionar todos os dados de uma tabela, descriptografar o campo amount e executar a comparação. Não ser capaz de depender no armazenamento de dados subjacente para essa atividade significa que minha filtragem provavelmente não será tão rápida quando poderia ser com o armazenamento de dados. Considerando que eu desejo maximizar a eficiência, eu deveria pensar sobre quais dados eu desejo criptografar e como eu desejo criptografá-los. Criptografar com as consultas futuras em mente é uma boa forma de aprimorar a eficiência geral do programa.

É fácil criptografar um nome de conta no MongoDB e procurar por seu nome criptografado, como mostra a Listagem 6:

Lista 6. Criptografia com o MongoDB
@Test
public void encryptMongoDBRecords() throws Exception {
 KeyStore.SecretKeyEntry pkEntry = getKeyStoreEntry();
 Cryptographical crypto =
   AESCryptoImpl.initialize(new AESCryptoKey(pkEntry.getSecretKey()));

 DB db = getMongoConnection();
 DBCollection coll = db.getCollection("accounts");

 BasicDBObject encryptedDoc = new BasicDBObject();
 encryptedDoc.put("name", crypto.encrypt("Acme Life, LLC"));
 coll.insert(encryptedDoc);


 BasicDBObject encryptedQuery = new BasicDBObject();
 encryptedQuery.put("name", crypto.encrypt("Acme Life, LLC"));

 DBObject result = coll.findOne(encryptedQuery);
 String value = result.get("name").toString();
 Assert.assertEquals("Acme Life, LLC", crypto.decrypt(value));
}

Minha primeira etapa na Listagem 6 é usar o método getKeyStoreEntry para ler um keystore existente. Em seguida, eu obtenho uma conexão com uma instância do MongoDB, que, nesse caso, está na nuvem, na MongoHQ. Em seguida, eu uso um link para a coleção de contas (algo que um programador RDBMS chamaria de tabela de contas) e insiro um novo registro de conta com seu nome correspondente criptografado. Finalmente, eu pesquiso por esse mesmo registro criptografando minha cadeia de caracteres de procura (na qual name é igual a um "Acme Life, LLC" criptografado).

O registro no MongoDB pareceria um pouco com o que mostra a Listagem 7. (Observe que sua cadeia de caractere "Acme Life, LLC" criptografada seria diferente da minha, pois você teria uma chave diferente.)

Lista 7. Etapa de teste de criptografia do MongoDB
{
 _id : "4ee0c541300484530bf9c6fa",
 name : "f0wJxYyVhfH0UkkTLKGZng=="
}

Eu deixei a chave real (name) sem criptografia no documento, mas eu poderia tê-la criptografado também. Se eu tivesse feito isso, minhas consultas correspondentes precisariam simplesmente refletir a mudança. Eu também poderia ter criptografado o nome da coleção. A comparação direta da Cadeia de caracteres funcionará independentemente de estar criptografada.

Essa estratégia não está limitada às implementações do MongoDB. Por exemplo, eu poderia executar exatamente a mesma etapa de teste com o SimpleDB, como mostra a Listagem 8:

Lista 8. Etapa de teste de criptografia do SimpleDB
@Test
public void testSimpleDBEncryptInsert() throws Exception {

 KeyStore.SecretKeyEntry pkEntry = getKeyStoreEntry();
 Cryptographical crypto =
   AESCryptoImpl.initialize(new AESCryptoKey(pkEntry.getSecretKey()));

 AmazonSimpleDB sdb = getSimpleDB();
 String domain = "accounts";
 sdb.createDomain(new CreateDomainRequest(domain));

 List<ReplaceableItem> data = new ArrayList<ReplaceableItem>();

 String encryptedName = crypto.encrypt("Acme Life, LLC");

 data.add(new ReplaceableItem().withName("account_02").withAttributes(
  new ReplaceableAttribute().withName("name").withValue(encryptedName)));

 sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data));

 String qry = "select * from " + SimpleDBUtils.quoteName(domain)
   + " where name = '" + encryptedName + "'";

 SelectRequest selectRequest = new SelectRequest(qry);
 for (Item item : sdb.select(selectRequest).getItems()) {
  Assert.assertEquals("account_02", item.getName());
 }
}

Aqui, eu executei as mesmas etapas de meu exemplo do MongoDB: li de um keystore existente, obtive uma conexão com o SimpleDB da Amazon e inseri um registro de conta cujo atributo name estava criptografado. Finalmente, eu procurei a conta por nome, usando o valor criptografado como sua chave.


Conclusão

Embora a computação em nuvem prometa tornar seus dados acessíveis a um público amplo, há muitas opções para proteger os dados confidenciais. Neste artigo, eu mostrei como usar as bibliotecas da plataforma Java para proteger os dados em repouso em uma infraestrutura na nuvem, como o MongoDB ou o SimpleDB. A criptografia de chave privada mantém a segurança dos dados nas mãos do administrador desses dados. E armazenar as chaves privadas em seu Java KeyStore os mantém gerenciáveis e seguros. Há apenas uma senha para acessar uma chave privada, e uma coisa que você nunca deve fazer é armazenar essa senha em texto simples, em qualquer lugar próximo a nuvem.

Os dados armazenados que foram criptografados se comportam de forma diferente dos dados de texto simples no contexto de algumas procuras. As correspondências exatas funcionarão bem, mas as consultas condicionais que envolvem correspondências não exatas podem ser um problema. A solução aqui está em como você manipula (ou não manipula) a comparação. Sempre pense bem sobre o que você vai criptografar tendo suas consultas em mente. Criptografar todos os seus dados pode ser um exagero, portanto, considere o que está sendo consultado e como.

Recursos

Aprender

Discutir

  • Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Cloud computing
ArticleID=791997
ArticleTitle=Desenvolvimento em Java 2.0: Protegendo os dados de aplicativo Java para computação em nuvem
publish-date=02102012