/*
* Copyright 2013 Future Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.ca.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONConverter;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.json.JSONWriter;
import org.krakenapps.api.PrimitiveConverter;
import org.krakenapps.ca.CertificateAuthority;
import org.krakenapps.ca.CertificateMetadata;
import org.krakenapps.ca.CertificateMetadataIterator;
import org.krakenapps.ca.RevokedCertificate;
import org.krakenapps.ca.RevokedCertificateIterator;
import org.krakenapps.codec.Base64;
public class CertificateAuthorityFormatter {
public static void convertToInternalFormat(InputStream is, OutputStream os) throws ParseException, IOException {
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(os, Charset.forName("utf-8"));
JSONWriter jw = new JSONWriter(writer);
jw.object();
JSONTokener t = new JSONTokener(new InputStreamReader(is, Charset.forName("utf-8")));
Map<String, Object> metadata = parseMetadata(t);
jw.key("metadata").value(metadata);
Integer version = (Integer) metadata.get("version");
if (version != 1)
throw new ParseException("unsupported authority data format version: " + version, -1);
if (t.nextClean() != ',')
return;
Object data = t.nextValue();
if (!data.equals("authority"))
throw new ParseException("authority should be placed after metadata: token is " + data, -1);
// "authority":{"key": "value"} or { "key" : []}
t.nextClean(); // :
t.nextClean(); // {
if (t.nextClean() == '}')
return;
t.back();
jw.key("collections");
jw.object();
metadata = new HashMap<String, Object>();
int i = 0;
while (true) {
if (i++ != 0) {
if (t.nextClean() == '}')
break;
}
String key = (String) t.nextValue();
t.nextClean(); // :
if (key.equals("root_certificate")) {
metadata.putAll(JSONConverter.parse((JSONObject) t.nextValue()));
continue;
}
if (t.nextClean() == '[') {
// collection name
jw.key(key);
// typed doc list
jw.array();
jw.value("list");
// doc list begin
jw.array();
while (t.nextClean() != ']') {
t.back();
Map<String, Object> m = JSONConverter.parse((JSONObject) t.nextValue());
jw.value(insertType(m));
if (t.nextClean() != ',')
t.back();
}
jw.endArray();
jw.endArray();
continue;
} else
t.back();
metadata.put(key, t.nextValue());
}
jw.key("metadata");
jw.array();
jw.value("list");
jw.array();
Map<String, Object> m = new HashMap<String, Object>();
m.put("type", "rootpw");
m.put("password", metadata.get("key_password"));
metadata.remove("key_password");
jw.value(insertType(m));
m = new HashMap<String, Object>();
m.put("type", "crl");
m.put("base_url", metadata.get("crl_base_url"));
metadata.remove("crl_base_url");
jw.value(insertType(m));
if (metadata.containsKey("last_serial")) {
m = new HashMap<String, Object>();
m.put("type", "serial");
m.put("serial", metadata.get("last_serial"));
metadata.remove("last_serial");
jw.value(insertType(m));
}
metadata.remove("name");
jw.value(insertType(metadata));
jw.endArray();
jw.endArray();
jw.endObject();
jw.endObject();
writer.flush();
} catch (JSONException e) {
throw new IOException(e);
} finally {
}
}
private static Object insertType(Map<String, Object> m) {
for (String key : m.keySet()) {
if (key.equals("binary"))
m.put(key, createList("blob", m.get("binary")));
else if (key.equals("not_before") || key.equals("issued_date") || key.equals("not_after") || key.equals("date"))
m.put(key, createList("date", m.get(key)));
else
m.put(key, createList("string", m.get(key).toString()));
}
return createList("map", m);
}
private static List<Object> createList(String type, Object doc) {
List<Object> l = new ArrayList<Object>(2);
l.add(type);
l.add(doc);
return l;
}
private static Map<String, Object> parseMetadata(JSONTokener x) throws JSONException, IOException {
if (x.nextClean() != '{') {
throw x.syntaxError("A JSONObject text must begin with '{'");
}
Object key = x.nextValue();
if (!key.equals("metadata"))
throw x.syntaxError("confdb metadata should be placed first");
x.nextClean();
return JSONConverter.parse((JSONObject) x.nextValue());
}
@SuppressWarnings("unchecked")
public static void exportAuthority(CertificateAuthority authority, OutputStream os) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
String binary = null;
try {
OutputStreamWriter writer = new OutputStreamWriter(os, Charset.forName("utf-8"));
JSONWriter jw = new JSONWriter(writer);
jw.object();
jw.key("metadata");
jw.object();
jw.key("version").value(1);
jw.key("date").value(sdf.format(new Date()));
jw.endObject();
jw.key("authority");
jw.object();
jw.key("certs");
CertificateMetadataIterator certIt = null;
try {
certIt = authority.getCertificateIterator();
jw.array();
while (certIt.hasNext()) {
CertificateMetadata cm = certIt.next();
Map<String, Object> cert = (Map<String, Object>) PrimitiveConverter.serialize(cm);
binary = new String(Base64.encode((byte[]) cert.get("binary")));
cert.put("binary", binary);
cert.put("not_before", sdf.format(cert.get("not_before")));
cert.put("not_after", sdf.format(cert.get("not_after")));
cert.put("issued_date", sdf.format(cert.get("issued_date")));
jw.value(cert);
}
jw.endArray();
} finally {
if (certIt != null)
certIt.close();
}
RevokedCertificateIterator revokeIt = null;
try {
revokeIt = authority.getRevokedCertificateIterator();
jw.key("revoked");
jw.array();
while (revokeIt.hasNext()) {
RevokedCertificate rc = revokeIt.next();
Map<String, Object> revoked = (Map<String, Object>) PrimitiveConverter.serialize(rc);
revoked.put("date", sdf.format(revoked.get("date")));
jw.value(revoked);
}
jw.endArray();
} finally {
if (revokeIt != null)
revokeIt.close();
}
jw.key("root_certificate");
Map<String, Object> rootCertificate = (Map<String, Object>) PrimitiveConverter.serialize(authority
.getRootCertificate());
binary = new String(Base64.encode((byte[]) rootCertificate.get("binary")));
rootCertificate.put("binary", binary);
rootCertificate.put("not_before", sdf.format(rootCertificate.get("not_before")));
rootCertificate.put("not_after", sdf.format(rootCertificate.get("not_after")));
rootCertificate.put("issued_date", sdf.format(rootCertificate.get("issued_date")));
rootCertificate.put("key_password", authority.getRootKeyPassword());
jw.value(rootCertificate);
jw.key("name").value(authority.getName());
jw.key("crl_base_url").value(authority.getCrlDistPoint());
jw.key("last_serial").value(authority.getLastSerial());
jw.endObject();
jw.endObject();
writer.flush();
} catch (JSONException e) {
throw new IOException(e);
}
}
}