Java development 2.0: Protección de datos de aplicación Java para computación en la nube

Utilice cifrado con clave privada para proteger datos en la nube

La seguridad de los datos causa una profunda preocupación en las organizaciones que están considerando adoptar el uso de la nube, pero en muchos casos, no debería suceder. En este capítulo de Java development 2.0, aprenda cómo utilizar cifrado de clave privada y Advanced Encryption Standard para proteger datos de aplicación confidenciales en la nube. También tendrá a su disposición un tutorial rápido sobre estrategias de cifrado, lo cual es importante para maximizar la eficacia de las búsquedas condicionales en almacenes de datos distribuidos en la nube.

Andrew Glover, CTO, App47

Andrew GloverAndrew Glover es un desarrollador, autor, conferencista y empresario apasionado por el desarrollo basado en comportamiento, la Integración Continua y el Desarrollo ágil de software. Es el fundador de la infraestructura easyb BDD (Behavior-Driven Development) y es coautor de tres libros: Continuous Integration, Groovy in Action, y Java Testing Patterns. Puede mantenerse informado de su trabajo leyendo su blog y siguiéndolo en Twitter.



31-07-2012

Acerca de esta serie

El horizonte del desarrollo Java ha cambiado radicalmente desde que la tecnología Java emergió por primera vez. Gracias a las infraestructuras maduras de fuente abierta y a las infraestructuras confiables de despliegue en alquiler, ahora es posible ensamblar, probar, ejecutar y mantener aplicaciones rápidamente y a bajo costo. En esta serie, Andrew Glover explora el espectro de las tecnologías y las herramientas que hacen que este nuevo paradigma de desarrollo Java sea posible.

En pocos años las plataformas y los servicios de computación en nube cambiaron drásticamente el horizonte del ™ desarrollo de la aplicación de Java. Han disminuido las dificultades asociadas con el mantenimiento del sistema y la configuración, y al mismo tiempo han bajado el costo y aumentado la velocidad para ingresar software en el mercado. Conceptualmente, la computación en nube es una cuestión lógica: a los encargados de negocios les encanta el retorno de la inversión, y a los desarrolladores les encanta la independencia del código de infraestructura. Sin embargo, muchos comercios encuentran difícil tomar la decisión de mudarse a plataformas en la nube.

La seguridad de los datos es una de las principales preocupaciones que enfrenta una organización que está considerando migrar su software a una infraestructura en la nube. Cuanto más confidenciales son los datos, más razones tienen para preocuparse. En nuestro rol de desarrolladores de software es importante que entendamos tanto los riesgos de seguridad reales de la computación en nube como los enfoques realistas para resolver por lo menos algunas de estas preocupaciones.

En este capítulo de Java development 2.0, les explicaré qué hace que el almacenamiento de datos en las nubes sea distinto al almacenamiento de datos en una máquina centralizada. Luego les mostraré cómo utilizar los estándares y las herramientas de cifrado de clave privada incorporados de la plataforma Java para proteger sus datos aunque estén almacenados en un almacén de datos distribuidos. Por último, haré una demostración de un enfoque estratégico al cifrado utilizando las condiciones de la consulta como línea de base para decidir si tiene que cifrar sus datos o no.

Protegiendo datos en la nube

La computación en nube no introduce, literalmente, nuevas preocupaciones acerca de la seguridad de los datos; en la mayoría de casos, simplemente los amplía. Poner datos en la nube los expone potencialmente a una mayor audiencia, lo cual suele ser algo bueno. Sin embargo, si los datos expuestos tenían que ser mantenidos en privado o con acceso restringido, los resultados pueden ser catastróficos. El problema fundamental en la computación en nube es que saca los datos del control inmediato del desarrollador o del administrador del sistema. En lugar de ser almacenados y administrados localmente, los datos en la nube son almacenados en dispositivos distribuidos que pueden estar situados en cualquier lugar y posiblemente cualquiera puede acceder a ellos.

Privacidad de la información en la UE

El enfoque de la Unión Europea acerca de la privacidad de la información en las plataformas en nube es mucho más estricto que el de los Estados Unidos: la información personal de los datos que le pertenecen a un ciudadano de la Unión Europea (por ejemplo, la historia clínica de un ciudadano francés) tiene que residir en servidores existentes dentro de la UE.

Aun si su empresa puede mantenerse al margen de la posibilidad de una catástrofe lejana descentralizada, usted querrá que sus aplicaciones en la nube procedan con un mínimo de seguridad de la información. Cuando usted comienza a pensar acerca de la seguridad de los datos, le surgen dos preguntas importantes:

  • ¿están seguros los datos en tránsito?
  • ¿están seguros los datos en reposo?

Datos en tránsito se refiere a cómo pasan los datos de una ubicación a otra; es decir, qué infraestructura y tecnología de comunicación está utilizando. Datos en reposo se refiere a cómo — y qué tan bien — almacenados están sus datos. Si, por ejemplo, usted almacena sus nombres de usuario y sus contraseñas en una base de datos sin cifrarlos, sus datos en reposo no están seguros.

Para proteger datos en tránsito en la web es común utilizar HTTPS. Esto es, HTTP con cifrado de datos viajando desde los navegadores hasta los clientes. Otra ventaja de HTTPS es su ubicuidad: La mayoría de los desarrolladores han configurado Apache, Tomcat, o Jetty para usar HTTPS.

El cifrado es también el mecanismo común para proteger datos en reposo, y la computación en nube no modifica eso. Aunque el cifrado pueda sonar esotérico, usted sólo necesita conocer algunos puntos básicos del mismo para proteger de manera razonable los datos de su aplicación. Una vez que sus datos estén seguros, no importa realmente si los coloca en un servidor locamente o vía plataforma en nube o almacén de datos.


Cifrado de clave privada

El cifrado es el proceso de transformación de texto llano legible por humanos a texto ilegible. Esto se hace con un algoritmo criptográfico, también conocido como cipher. El texto cifrado se descifra para volver a obtener texto legible mediante una clave que es esencialmente una contraseña. El cifrado sirve para proteger información haciéndola ilegible para cualquiera que no tenga la clave.

Hay dos tipos de cifrado basado en clave que se usan en informática: cifrado de clave pública y cifrado de clave privada. Cifrado de clave pública es la técnica más común para proteger datos en tránsito; de hecho, es la arquitectura subyacente de la seguridad de transacciones en HTTPS. Esta forma de cifrado requiere dos claves en un conjunto de clave pública y privada. La clave pública cifra los datos y la clave privada se utiliza para descifrar esos datos.  En el cifrado de clave pública, la clave pública se puede distribuir de manera segura mientras que la clave privada debe permanecer bajo el control de un administrador. El cifrado de clave pública hace que compartir información cifrada sea fácil.

Claves privadas y privacidad

Independientemente de qué algoritmo de cifrado utilice, usted debe asegurarse de que su clave privada esté segura. Su frase de contraseña debe cumplir con altos niveles de seguridad y nunca debe ser guardada en texto simple, — y específicamente, no debe estar en la nube. Afortunadamente, la infraestructura de la plataforma Java crea claves razonablemente complejas y le permite protegerlas en el depósito de claves de la plataforma Java.

En el cifrado de clave privada, los datos son cifrados y descifrados mediante una clave privada única. Este tipo de cifrado hace que sea difícil compartir los datos cifrados con un tercero porque tanto el emisor como el receptor deben usar la misma clave. Si esa clave se pone en peligro, toda la información cifrada estará en peligro. El cifrado de clave privada es altamente efectivo cuando los datos que se cifran no necesitan ser compartidos con otros, de modo que la clave puede quedar bajo estricto control todo el tiempo.

El cifrado de clave privada es un medio efectivo para proteger los datos de aplicación que se almacenarán y transmitirán vía una infraestructura de nube. Como la clave de cifrado permanece bajo el control de un administrador o creador de aplicación, los proveedores de la nube y otras fuentes potenciales de escuchas informáticas no tienen acceso irrestricto a esos datos.


Cifrando una aplicación Java

Usted tiene para elegir una variedad de opciones para proteger aplicaciones Java, incluyendo las bibliotecas de plataforma Java estándar. También tiene a disposición una serie de estándares y paquetes de cifrado a elección. En los ejemplos siguientes utilizaré las bibliotecas principales Java y AES o Advanced Encryption Standard. Utilizaré una clave privada tanto para cifrar texto llano como para descifrar texto cifrado, que es un texto llano que ha sido cifrado. A mí me gusta AES porque está aprobado por la National Security Agency y está normalizado por el gobierno de los Estados Unidos de América.

Para mantener máxima flexibilidad y tranquilidad en las pruebas crearé algunas interfaces de criptografía y clases de implementación asociada que simplemente agrupan las clases centrales Java. Después mostraré cómo utilizar estas clases para hacer que los datos persistan y hasta consultar los datos en almacenes en la nube como SimpleDB de Amazon o incluso MongoDB de MongoHQ.

En el Listado 1, defino una interfaz genérica simple que determina dos métodos para cifrar y descifrar datos. Esta interfaz servirá como primer plano para varios algoritmos; es decir, mis clases de implementación utilizarán un cifrado particular como AES.

Un paquete de interfaz de criptografía
package com.b50.crypto;

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

Con mi interfaz Cryptographical puedo cifrar texto o descifrar texto de cifrado. A continuación, en el Listado 2, utilizaré la API Java Security para crear otra interfaz que representa una clave:

Un paquete de interfaz clave
com.b50.crypto;

import java.security.Key;

public interface CryptoKeyable { Key getKey(); }

Como usted puede ver, mi interfaz CryptoKeyable sirve simplemente como empaquetador para el tipo principal Clave de la plataforma Java

Si usted está usando cifrado AES, los caracteres binarios generados cuando cifra texto llano tendrán que estar codificados en base 64 — o por lo menos cuando los quiera utilizar en solicitudes web (por ejemplo, con dominios SimpleDB). Por ende, codificaré todas las cadenas de caracteres cifradas y decodificaré todas las cadenas de caracteres descifradas.

Mi clase de implementación Cryptographical para AES, mostrada en el Listado 3, no sólo maneja el cifrado AES sino también la codificación y la decodificación en base 64:

Una implementación AES de mi paquete de interfaz Cryptographical
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);
  }
 }
}

El depósito de claves Java KeyStore

Ahora, pensemos en la clave de cifrado. Las bibliotecas principales de la plataforma Java se pueden usar para crear sólidas claves de cifrado; sin embargo, estos métodos siempre producirán una nueva clave generada al azar. Por lo tanto, si crea una clave utilizando la clase Java KeyGenerator , usted necesitará almacenar esa clave para usarla en el futuro (es decir, hasta que decida descifrar el texto cifrado con esa clave). Para esto, usted puede usar la aplicación de plataforma Java KeyStore y las clases correspondientes.

KeyStore contiene un conjunto de clases que le permiten guardar una clave en un archivo binario protegido con contraseña llamado depósito de claves. Puedo probar claves en Java con unos pocos casos de pruebas. Primero, creo dos instancias de una Clave y muestro que la Cadena de caracteres cifrada que le corresponde a cada uno es diferente, como se muestra en el Listado 4:

Cifrado simple utilizando dos claves 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));
}

Después, en el Listado 5, demuestro que una instancia de clave dada produce el mismo texto cifrado exacto para una Cadena de caracteres correspondiente:

Una clave privada le corresponde a una sola cadena 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));
}

Lo que yo cifro con una clave en particular tengo que descifrarlo con la misma clave. Usar Java KeyStore es un medio conveniente y seguro de almacenar mis claves.

Notas acerca del depósito de claves

El código de los Listados 4 y 5 es texto modelo, pero sirve para demostrar algunas cuestiones:

  • El depósito de claves tiene un nombre.
  • El archivo almacenado está protegido con una contraseña.
  • El depósito de claves puede almacenar más de una clave.
  • Cada clave dentro del depósito tiene una contraseña asociada.

Para este caso de pruebas decidí crear un nuevo depósito de claves cada vez que ejecuto la prueba. Podría simplemente haber abierto un depósito de claves existentes para cada nueva prueba. Si quisiera utilizar un depósito de claves existente tendría que saber su contraseña además de la contraseña para acceder a una clave en particular.

La clave lo es todo en lo que respecta al cifrado. No importa qué fuerza tenga el cifrado subyacente; si mi clave se ve en peligro, mis datos quedarán expuestos. Esto también implica asegurarme de que el depósito de claves y sus frases de contraseña asociadas estén siempre protegidas. (Por ejemplo, en una aplicación de producción, yo nunca insertaría a fuego en el código fuente las contraseñas de la forma en que lo hice a modo de demostración en los Listados 4 y 5.)


Criptografía en nube

Al cifrar datos usted está cambiando sus propiedades. Esencialmente, esto significa que un entero cifrado no responderá correctamente a la comparación de enteros. Por consiguiente, es importante pensar bien cómo, cuándo y en qué circunstancias, finalmente, consultará datos almacenados en la nube. Las buena notica es que, en muchos casos, los datos que usted desea mantener en privado serán de distinto valor comercial que los datos que usted desea manipular: es lógico cifrar el nombre de una cuenta o cierta información personal acerca del titular de la cuenta pero cifrar el saldo de una cuenta no lo es (ya que ¿a quién le importa el saldo de una cuenta que no se puede ligar a una persona?).

Consultando con cifrado

Los datos cifrados se pueden buscar fácilmente cuando se hace con coincidencias exactas tales como: "Find me all accounts named 'foo' (where 'foo' is encrypted)." Pero eso no funciona nativamente para búsquedas condicionales como: "Find me all accounts whose overdue balance is greater than $450 where $450 is encrypted."

Por ejemplo, imaginemos que uso un cifrado simple que revierte el orden de los caracteres y agrega el carácter i al final de una cadena de caracteres. En este caso, la cadena de caracteres foo se convertiría en oofi y 450 se convertiría en 054i. Si el valor de nombre de la tabla fue cifrado utilizando este cifrado simple, podría fácilmente consultar por coincidencias exactas tales como"select * from table where name = 'oofi'". Ahora bien, una comparación de un valor cifrado de 450 ya es otra cosa totalmente distinta: "select * from table where amount > 054i" no es totalmente igual a "select * from table where amount > 450".

Para hacer una comparación de datos en este caso tendría que descifrar algo en la aplicación, — es decir, necesitaría seleccionar todos los datos de una tabla, descifrar el campo de la cantidad , y después realizar la comparación. No poder confiar en el almacén de datos subyacente para esta actividad significa que probablemente mi filtrado no será tan rápido como lo sería con el almacén de datos. Dado que quiero maximizar la eficacia, yo analizaría qué datos quiero cifrar y cómo los quiero cifrar. Cifrar con consultas futuras en mente es un buen modo de mejorar la eficacia general del programa.

Es fácil cifrar un nombre de cuenta en MongoDB y buscar por su nombre cifrado, como se muestra en el Listado 6:

Cifrado con 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));
}

Mi primer paso en el Listado 6 es usar el método getKeyStoreEntry para leer un depósito de claves existente. Luego, obtengo una conexión con una instancia MongoDB, que en este caso coincidentemente reside en la nube en MongoHQ. Después, tomo un enlace a la colección de cuentas (lo que un programador de RDBMS llamaría la tabla de cuentas) y procedo a insertar un nuevo registro de cuenta con su nombre correspondiente cifrado. Finalmente, busco ese mismo registro cifrando mi cadena de caracteres de búsqueda (donde name equivale a un "Acme Life, LLC" cifrado).

El registro en MongoDB tendría una apariencia similar a lo que se muestra en el Listado 7. (Note que su cadena de caracteres cifrada "Acme Life, LLC" sería diferente porque usted tendría una clave distinta.)

Un caso de prueba de cifrado MongoDB
{ _id : "4ee0c541300484530bf9c6fa", name : "f0wJxYyVhfH0UkkTLKGZng==" }

He dejado la clave real (name) sin cifrar en el documento pero podría haberla cifrado también. Si lo hiciera, mis consultas correspondientes sólo tendrían que reflejar el cambio. También podría haber cifrado el nombre de la colección. Las comparaciones Direct String funcionarán independientemente de si están cifradas o no.

Esta estrategia no está limitada a las implementaciones MongoDB. Por ejemplo, yo podría ejecutar en líneas generales el mismo caso de pruebas con SimpleDB, como se muestra en el Listado 8:

Un caso de pruebas de cifrado 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());
 }
}

Aquí he seguido los mismos pasos de mi ejemplo MongoDB: Leí un depósito de claves existente, obtuve una conexión con SimpleDB de Amazon y después inserté un registro de cuenta cuyo atributo name estuviese cifrado. Finalmente, busqué la cuenta por nombre utilizando el valor cifrado como su clave.


En conclusión

Mientras que la computación en nube promete hacer que sus datos estén accesibles a un público amplio, hay muchas cosas que usted puede hacer para proteger sus datos confidenciales. En este artículo he mostrado cómo utilizar las bibliotecas de plataforma Java para proteger datos en reposo en una infraestructura de nube tal como MongoDB o SimpleDB. El cifrado de clave privada mantiene la seguridad de los datos en manos del administrador de esos datos. Almacenar las claves privadas en su Java KeyStore las mantiene manejables y seguras. Hay sólo una contraseña para acceder a una clave privada y algo que nunca querrá hacer es almacenar esa contraseña en texto simple en algún lugar cerca de la nube.

Los datos almacenados que han sido cifrados se comportan de forma distinta a los datos de texto simple en el contexto de algunas búsquedas. Las coincidencias exactas funcionan bien pero las búsquedas condicionales que involucran coincidencias que no son exactas pueden ser un dolor de cabeza. La solución reside en cómo manejar (o no manejar) la comparación. Evalúe siempre qué cifrará con sus consultas elegidas en mente. Cifrar todos sus datos puede ser una exageración, por lo tanto, tenga en consideración qué se solicita y cómo.

Recursos

Aprender

Comentar

  • Participe en la comunidad developerWorks. Conéctese con otros usuarios developerWorks mientras explora los blogs, foros, grupos y wikis dirigidos a desarrolladores.

Comentarios

developerWorks: Ingrese

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


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

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

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java, Cloud computing
ArticleID=827975
ArticleTitle=Java development 2.0: Protección de datos de aplicación Java para computación en la nube
publish-date=07312012