Saturday, November 10, 2007

Case for Web services with JSON RPC

I have recently been working on developing JSON RPC based web services (over https) and using Java client. The server side JSONRPC services were developed using the JSON-RPC-Java and later also using the JSON-RPC C libraries.

The only client side JSON RPC stack in Java that is available at the time of this writing is http://code.google.com/p/json-rpc-client/. It supports JSON RPC over http (using apache commons httpclient library). It was easily extensible to support JSON RPC over https. In this post, i am going to put down my experiences of using JSON RPC.

  1. JSON is a fat-free XML. (Read more at http://json.org/xml.html).
  2. JSON RPC is an alternative RPC mechanism over http (or https).
  3. JSON RPC is simpler to learn and implement than SOAP. The stacks are much less lines of code compared to SOAP stacks.
  4. JSON RPC is simple as it does not include an Interface Definition Language like WSDL for SOAP based web services. So there is no contract definition between client and server in a IDL rather contract is defined on paper and then implememted in respective languages of server-side and client-side.
  5. JSON RPC spec is very loosly written and hence leaves alot of room for vendors to come up with their own solutions. Like metaparadig folks have their proprietary way of implementing class hinting (viz the way to identify the class type to the other end so that JSON message can be mapped to a class type and an instance of the class can be created with the passed in values in the JSON stream).
  6. The interoperability between JSON RPC C/C++ service and Java client is limited in following aspects:
    1. No Java collections can be used. This is same for even SOAP web services. The root cause for this limitation is that the pre JDK 1.5 Java had no generics and hence all collection classes (like ArrayList) could have held more than one Object types so it was hard to tell the type of the element held in the collection. This is solved by proprietary class hinting ismplementations when both client and server are in Java but across languages this becomes an issue. So the solution is to use arrays instead.
    2. Enum types are not supported by the metaparadigm JSON-RPC-Java stack at present as its a newer JDK 1.5 feature. So use int instead.
  7. Security: Though several approaches may be possible but the simplest solution is to implement JSON RPC over https with basic authentication for client. You may have a self-signed certificate for the web service to keep the deployments simple. But if you really want the most security possible then go for a trusted CA signed certificate for the web service but then you will require a certificate signing infrastructure in place to be able to create a certificate for each instance of web service installed.
  8. JSON RPC spec does not have anything to say about intermediary message handlers but it is easy to think of creating JSON RPC intermediary nodes although the spec does not have provisions for extensible message control headers like SOAP spec has. So JSON RPC is pretty much limited to being used between two nodes (the client and the server) - the message source and the message destination or end point. Its not really meant for "document" style messaging for which SOAP is used in B2B applications.
So if you want to build a robust, fat-free (read faster) distributed RPC infrastructure then you can base it on JSON RPC.

JSON RPC makes most sense in web applications where the client is in Javascript language as JSON maps directly to Javascript objects and hence you dont need to parse the message and extract the data, its done automatically. But other than AJAXing your web pages, you can also use it for straight forward RPC architectures where SOAP may be an overkill. You will have a working JSON RPC solution much sooner and it is of course much easier to comprehend and implement than SOAP. So when you are using SOAP web services with RPC style then think twice as you have an more able alternative approach in JSON RPC.

Let me know your thoughts by leaving your comments.

Tuesday, November 06, 2007

Using Basic authentication and HTTPS (w/ self-signed certificates) in Java

1. Client Authentication is in practice only used for B2B type applications.
2. In some cases we may even be okay with not authenticating the server on the client end during SSL handshake, for sake of:
o simplicity (no certificate signing infrastructure is required) and
o performance (we only use SSL for encryption and not for server authentication).

This approach is of self-signed certificate which the server can sign for itself and client will by-pass server authentication.

3. We first need to configure web server for SSL. Tomcat currently operates only on JKS, PKCS11 or PKCS12 format keystores.
4. We can use the JDK keytool to generate self-signed certificate for the host running tomcat as shown below:

$ keytool -genkey -alias tomcat -keyalg RSA -keystore example.keystore
Enter keystore password: secret
Re-enter new password: secret
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]:
What is the name of your organization?
[Unknown]: <My Company Name>
What is the name of your City or Locality?
[Unknown]: <City>
What is the name of your State or Province?
[Unknown]: <State>
What is the two-letter country code for this unit?
[Unknown]: <Country Code>
Is CN=localhost, OU=Unkown, O=<My Company Name>, L=<City>, ST=<State>, C=<Country> correct?
[no]: yes

Enter key password for
(RETURN if same as keystore password): <Enter>


The example.keystore is then generated and is in JKS (Java Key Store) format.

5. Copy it to the Tomcat root directory say C:\Program Files\Apache Software Foundation\Tomcat 6.0 path.

6. The final step is to configure your secure socket in the $CATALINA_HOME/conf/server.xml file, where $CATALINA_HOME represents the directory into which you installed Tomcat 6.

<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" minSpareThreads="5" maxSpareThreads="75"
enableLookups="true" disableUploadTimeout="true"
acceptCount="100" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="./example.keystore" keystorePass="secret"
clientAuth="false" sslProtocol="TLS"/>

NOTE: You can refer to the http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html for more configuration options.

With the above settings you can verify that browsing to https://localhost:8443 returns the splash page of tomcat home.

7. We will also make sure that tomcat has a role named "manager" and some user associated with the role. We can edit the tomcat_users.xml for that:

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager"/>
<user username="admin" password="admin" roles="manager"/>
</tomcat-users>

8. Now, we can enforce that a certain URL pattern for our web application always requires https access. To do this, we need to edit the web.xml of the web application:


<security-constraint>
<display-name>some name for service</display-name>
<web-resource-collection>
<web-resource-name>My Service</web-resource-name>
<description/>
<url-pattern>/secure/XYZ/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
<user-data-constraint>
<description/>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>MY_SECURE_REALM</realm-name>
</login-config>
<security-role>
<description>manager api can use this role.</description>
<role-name>manager</role-name>
</security-role>

With the above configuration, we have Basic authentication and HTTPS enabled for all resources accessed by the URL pattern /secure/XYZ/*.

So even if you try to access the resource at /secure/XYZ/* using http then tomcat will redirect you to the page using https scheme and thus enforce secure use. Since we also use the Basic authentication so browser client will prompt you entering user credentials.

9. If you are using API based http client access from say a J2SE client (using apache commons httpclient 3.x) then you will need to set the credentials for the realm MY_SECURE_REALM (which defines the Authentication Scope on the web server) in the Http header.

HttpState state = new HttpState();
state.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT,
"MY_SECURE_REALM"), new UsernamePasswordCredentials(user, passwd));

Also you will need to use the org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory to be able to by-pass the agent authentication on client side. Apache commons httpclient comes with this contrib code which is included with the source distro but is not bundled in the jar file. So you will need to pull the source out from contrib/ssl path and use it in your project.

Basically you will need to check if the uri in use has scheme type of https then associate the EasySSLProtocolSocketFactory as the protocol handler for the scheme.

if (uri.getScheme().equals("https")) {
Protocol easyhttps = new Protocol(uri.getScheme(), new EasySSLProtocolSocketFactory(), uri.getPort());

Protocol.registerProtocol("https", easyhttps);
}

The way it works is, EasySSLProtocolSocketFactory in turn uses the EasyX509TrustManager (again from contrib/ssl) to just do a agent certificate validity from and to time validation (so that the ceritificate is not expired and is not before the validity start date). As long as the certificate in use by the agent is valid the EasyX509TrustManager will be okay to bypass doing any authentication for the self-signed certificate for the agent.

That completes the simple discourse on how to use Basic authentication with HTTPS (using self-signed certificate for the server end).

Book notes: Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems, by Martin Kleppmann

My notes from the excellent book on how software has evolved to handle data from hierarchical databases to the NoSQL -  https://www.goodrea...