views:

279

answers:

3

i like to encode a java map of strings as a single base 64 encoded string. The encoded string will be transmitted to a remote endpoint and maybe manipulated by a not nice person. So the worst thing that should happen are invaild key,value-tuples, but should not bring any other security risks aside.

Example:

Map<String,String> map = ...
String encoded = Base64.encode(map);

// somewhere else
Map<String,String> map = Base64.decode(encoded);

Yes, must be Base64. Not like that or that or any other of these. Is there an existing lightweight solution (Single Utils-Class prefered) out there? Or do i have to create my own?

Anything better than this?

// marshalling
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
String encoded = new String(Base64.encodeBase64(baos.toByteArray()));

// unmarshalling 
byte[] decoded = Base64.decodeBase64(encoded.getBytes());
ByteArrayInputStream bais = new ByteArrayInputStream(decoded);
ObjectInputStream ois = new ObjectInputStream(bais);
map = (Map<String,String>) ois.readObject();
ois.close();

Thanks,

+1  A: 

Your solution works. The only other approach would be to serialize the map yourself (iterate over the keys and values). That would mean you'd have to make sure you handle all the cases correctly (for example, if you transmit the values as key=value, you must find a way to allow = in the key/value and you must separate the pairs somehow which means you must also allow this separation character in the name, etc).

All in all, it's hard to get right, easy to get wrong and would take a whole lot more code and headache. Plus don't forget that you'd have to write a lot of error handling code in the parser (receiver side).

Aaron Digulla
A: 

Another possible way would be using JSON which is a very ligthweight lib. The the encoding then would look like this:

JSONObject jso = new JSONObject( map );
String encoded = new String(Base64.encodeBase64( jso.toString( 4 ).toByteArray()));
stacker
+6  A: 

my primary requirements are: encoded string should be as short as possible and contain only latin characters or characters from the base64 alphabet (not my call). there are no other reqs.

Use Google Gson to convert Map to JSON. Use GZIPOutputStream to compress the JSON string. Use Apache Commons Codec Base64 or Base64OutputStream to encode the compressed bytes to a Base64 string.

Kickoff example:

public static void main(String[] args) throws IOException {
    Map<String, String> map = new HashMap<String, String>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    map.put("key3", "value3");

    String serialized = serialize(map);
    Map<String, String> deserialized = deserialize(serialized, new TypeToken<Map<String, String>>() {}.getType());

    System.out.println(deserialized);
}

public static String serialize(Object object) throws IOException {
    ByteArrayOutputStream byteaOut = new ByteArrayOutputStream();
    GZIPOutputStream gzipOut = null;
    try {
        gzipOut = new GZIPOutputStream(new Base64OutputStream(byteaOut));
        gzipOut.write(new Gson().toJson(object).getBytes("UTF-8"));
    } finally {
        if (gzipOut != null) try { gzipOut.close(); } catch (IOException logOrIgnore) {}
    }
    return new String(byteaOut.toByteArray());
}

public static <T> T deserialize(String string, Type type) throws IOException {
    ByteArrayOutputStream byteaOut = new ByteArrayOutputStream();
    GZIPInputStream gzipIn = null;
    try {
        gzipIn = new GZIPInputStream(new Base64InputStream(new ByteArrayInputStream(string.getBytes("UTF-8"))));
        for (int data; (data = gzipIn.read()) > -1;) {
            byteaOut.write(data);
        }
    } finally {
        if (gzipIn != null) try { gzipIn.close(); } catch (IOException logOrIgnore) {}
    }
    return new Gson().fromJson(new String(byteaOut.toByteArray()), type);
}
BalusC
+1 for zipping the JSON string, i've overseen this req.
stacker
i like your solution because its short, working, efficient, secure and last but not least the encoded string is also understood by non-java apps.
Chris
JSON, GZIP and Base64 are global standards and supported in practically all languages (Java, C#, PHP, etc). Java serialization as you've shown (ObjectInput/OutputStream) is only supported in Java.
BalusC
yeah, i know, thats why i said "also understood by non-java apps" ;-) good job!
Chris
Oh I see, I misinterpreted your comment. Sorry about that :) Glad it helped you.
BalusC