Java tutorial
/* * Copyright (C) 2011 Alastair R. Beresford * * 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 com.google.nigori.common; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; import com.google.nigori.common.NigoriMessages.AuthenticateRequest; import com.google.nigori.common.NigoriMessages.DeleteRequest; import com.google.nigori.common.NigoriMessages.GetIndicesRequest; import com.google.nigori.common.NigoriMessages.GetIndicesResponse; import com.google.nigori.common.NigoriMessages.GetRequest; import com.google.nigori.common.NigoriMessages.GetResponse; import com.google.nigori.common.NigoriMessages.GetRevisionsRequest; import com.google.nigori.common.NigoriMessages.GetRevisionsResponse; import com.google.nigori.common.NigoriMessages.PutRequest; import com.google.nigori.common.NigoriMessages.RegisterRequest; import com.google.nigori.common.NigoriMessages.RevisionValue; import com.google.nigori.common.NigoriMessages.UnregisterRequest; import com.google.protobuf.ByteString; import com.google.protobuf.GeneratedMessage; public class MessageLibrary { private static final boolean DEBUG = false; //The character set used to encode all string-based communications in Nigori. public static final String CHARSET = "UTF-8"; public static byte[] toBytes(String string) { try { return string.getBytes(CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e);// never happens as UTF-8 is supported } } public static String bytesToString(byte[] bytes) { try { return new String(bytes, CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e);// never happens as UTF-8 is supported } } //The mimetypes used for all supported communication formats in Nigori public static final String MIMETYPE_JSON = "application/json"; public static final String MIMETYPE_PROTOBUF = "application/x-google-protobuf"; public static final String REQUEST_GET = "get"; public static final String REQUEST_GET_INDICES = "get-indices"; public static final String REQUEST_GET_REVISIONS = "get-revisions"; public static final String REQUEST_PUT = "put"; public static final String REQUEST_DELETE = "delete"; //public static final String REQUEST_UPDATE = "update"; public static final String REQUEST_AUTHENTICATE = "authenticate"; public static final String REQUEST_REGISTER = "register"; public static final String REQUEST_UNREGISTER = "unregister"; private static Gson gson = initializeGson(); private static Gson initializeGson() { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(GetRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(GetResponse.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(GetIndicesRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(GetIndicesResponse.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(GetRevisionsRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(GetRevisionsResponse.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(PutRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(DeleteRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(RegisterRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(UnregisterRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(AuthenticateRequest.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(RevisionValue.class, new TypeAdapterProtobuf()); gsonBuilder.registerTypeAdapter(ByteString.class, new TypeAdapterByteString()); gsonBuilder.setPrettyPrinting(); return gsonBuilder.create(); } @SuppressWarnings("serial") public static class JsonConversionException extends Exception { JsonConversionException(String msg) { super(msg); } } public static String toJson(GeneratedMessage src) { return gson.toJson(src); } private static <T extends GeneratedMessage> T fromJson(String json, Class<T> clz) throws JsonConversionException { try { return gson.fromJson(json, clz); } catch (JsonSyntaxException jse) { throw new JsonConversionException("Invalid JSON syntax" + (DEBUG ? json : "")); } catch (JsonParseException jse) { throw new JsonConversionException( "Unable to parse JSON fields into correct message format" + (DEBUG ? json : "")); } } public static GetRequest getRequestAsProtobuf(String serverName, DSASign signer, byte[] index, byte[] revision) throws NigoriCryptographyException { if (revision != null) { return GetRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_GET, index, revision)) .setKey(ByteString.copyFrom(index)).setRevision(ByteString.copyFrom(revision)).build(); } else { return GetRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_GET, index)) .setKey(ByteString.copyFrom(index)).build(); } } public static String getRequestAsJson(String serverName, DSASign signer, byte[] index, byte[] revision) throws NigoriCryptographyException { return gson.toJson(getRequestAsProtobuf(serverName, signer, index, revision)); } public static GetRequest getRequestFromJson(String json) throws JsonConversionException { return fromJson(json, GetRequest.class); } public static GetResponse getResponseAsProtobuf(Collection<RevValue> revisions) { // TODO(drt24) add index List<RevisionValue> protoRevisions = new ArrayList<RevisionValue>(revisions.size()); for (RevValue rv : revisions) { protoRevisions .add(RevisionValue.newBuilder().setRevision(ByteString.copyFrom(rv.getRevision().getBytes())) .setValue(ByteString.copyFrom(rv.getValue())).build()); } GetResponse resp = GetResponse.newBuilder().addAllRevisions(protoRevisions).build(); return resp; } public static String getResponseAsJson(Collection<RevValue> revisions) { return gson.toJson(getResponseAsProtobuf(revisions)); } public static GetResponse getResponseFromJson(String json) throws JsonConversionException { return fromJson(json, GetResponse.class); } public static GetIndicesRequest getIndicesRequestAsProtobuf(String serverName, DSASign signer) throws NigoriCryptographyException { return GetIndicesRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_GET_INDICES)).build(); } public static String getIndicesRequestAsJson(String serverName, DSASign signer) throws NigoriCryptographyException { return gson.toJson(getIndicesRequestAsProtobuf(serverName, signer)); } public static GetIndicesRequest getIndicesRequestFromJson(String json) throws JsonConversionException { return fromJson(json, GetIndicesRequest.class); } public static GetIndicesResponse getIndicesResponseAsProtobuf(Collection<byte[]> value) { List<ByteString> values = new ArrayList<ByteString>(value.size()); for (byte[] valueA : value) { values.add(ByteString.copyFrom(valueA)); } return GetIndicesResponse.newBuilder().addAllIndices(values).build(); } public static String getIndicesResponseAsJson(Collection<byte[]> value) { return gson.toJson(getIndicesResponseAsProtobuf(value)); } public static GetIndicesResponse getIndicesResponseFromJson(String json) throws JsonConversionException { return fromJson(json, GetIndicesResponse.class); } public static GetRevisionsRequest getRevisionsRequestAsProtobuf(String serverName, DSASign signer, byte[] index) throws NigoriCryptographyException { return GetRevisionsRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_GET_REVISIONS, index)) .setKey(ByteString.copyFrom(index)).build(); } public static String getRevisionsRequestAsJson(String serverName, DSASign signer, byte[] encIndex) throws NigoriCryptographyException { return gson.toJson(getRevisionsRequestAsProtobuf(serverName, signer, encIndex)); } public static GetRevisionsRequest getRevisionsRequestFromJson(String json) throws JsonConversionException { return fromJson(json, GetRevisionsRequest.class); } public static GetRevisionsResponse getRevisionsResponseAsProtobuf(Collection<byte[]> value) { List<ByteString> values = new ArrayList<ByteString>(value.size()); for (byte[] valueA : value) { values.add(ByteString.copyFrom(valueA)); } return GetRevisionsResponse.newBuilder().addAllRevisions(values).build(); } public static String getRevisionsResponseAsJson(Collection<byte[]> value) { return gson.toJson(getRevisionsResponseAsProtobuf(value)); } public static GetRevisionsResponse getRevisionsResponseFromJson(String json) throws JsonConversionException { return fromJson(json, GetRevisionsResponse.class); } public static PutRequest putRequestAsProtobuf(String serverName, DSASign signer, byte[] index, byte[] revision, byte[] value) throws NigoriCryptographyException { PutRequest.Builder reqBuilder = PutRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_PUT, index, revision, value)) .setKey(ByteString.copyFrom(index)).setRevision(ByteString.copyFrom(revision)) .setValue(ByteString.copyFrom(value)); PutRequest req = reqBuilder.build(); return req; } public static String putRequestAsJson(String serverName, DSASign signer, byte[] index, byte[] revision, byte[] value) throws NigoriCryptographyException { return gson.toJson(putRequestAsProtobuf(serverName, signer, index, revision, value)); } public static PutRequest putRequestFromJson(String json) throws JsonConversionException { return fromJson(json, PutRequest.class); } public static DeleteRequest deleteRequestAsProtobuf(String serverName, DSASign signer, byte[] index) throws NigoriCryptographyException { DeleteRequest.Builder delBuilder = DeleteRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_DELETE, index)) .setKey(ByteString.copyFrom(index)); DeleteRequest del = delBuilder.build(); return del; } public static String deleteRequestAsJson(String serverName, DSASign signer, byte[] index) throws NigoriCryptographyException { return gson.toJson(deleteRequestAsProtobuf(serverName, signer, index)); } public static DeleteRequest deleteRequestFromJson(String json) throws JsonConversionException { return fromJson(json, DeleteRequest.class); } public static AuthenticateRequest authenticateRequestAsProtobuf(String serverName, DSASign signer) throws NigoriCryptographyException { return authenticateRequestAsProtobuf(serverName, signer, REQUEST_AUTHENTICATE); } protected static AuthenticateRequest authenticateRequestAsProtobuf(String serverName, DSASign signer, String command, byte[]... payload) throws NigoriCryptographyException { try { Nonce nonce = new Nonce(); DSASignature signedNonce = signer.sign(Util.joinBytes(MessageLibrary.toBytes(serverName), nonce.nt(), nonce.nr(), toBytes(command), Util.joinBytes(payload))); byte[] sig = Util.joinBytes(signedNonce.getR(), signedNonce.getS()); AuthenticateRequest req = AuthenticateRequest.newBuilder() .setPublicKey(ByteString.copyFrom(signer.getPublicHash())).setSig(ByteString.copyFrom(sig)) .setNonce(ByteString.copyFrom(nonce.toToken())).setServerName(serverName).build(); return req; } catch (NoSuchAlgorithmException e) { throw new NigoriCryptographyException("Platform does have required crypto support:" + e.getMessage()); } } public static String authenticateRequestAsJson(String serverName, DSASign signer) throws NigoriCryptographyException { return gson.toJson(authenticateRequestAsProtobuf(serverName, signer)); } public static AuthenticateRequest authenticateRequestFromJson(String json) throws JsonConversionException { return fromJson(json, AuthenticateRequest.class); } public static RegisterRequest registerRequestAsProtobuf(DSASign signer, byte[] token) { RegisterRequest req = RegisterRequest.newBuilder().setPublicKey(ByteString.copyFrom(signer.getPublicKey())) .setToken(ByteString.copyFrom(token)).build(); return req; } public static String registerRequestAsJson(DSASign signer, byte[] token) { return gson.toJson(registerRequestAsProtobuf(signer, token)); } public static RegisterRequest registerRequestFromJson(String json) throws JsonConversionException { return fromJson(json, RegisterRequest.class); } public static UnregisterRequest unregisterRequestAsProtobuf(String serverName, DSASign signer) throws NigoriCryptographyException { UnregisterRequest req = UnregisterRequest.newBuilder() .setAuth(authenticateRequestAsProtobuf(serverName, signer, REQUEST_UNREGISTER)).build(); return req; } public static String unregisterRequestAsJson(String serverName, DSASign signer) throws NigoriCryptographyException { return gson.toJson(unregisterRequestAsProtobuf(serverName, signer)); } public static UnregisterRequest unregisterRequestFromJson(String json) throws JsonConversionException { return fromJson(json, UnregisterRequest.class); } }